LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - expandedrecord.c (source / functions) Coverage Total Hit UBC GIC GNC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 84.3 % 483 407 76 2 405
Current Date: 2024-04-14 14:21:10 Functions: 85.0 % 20 17 3 1 16
Baseline: 16@8cea358b128 Branches: 68.7 % 262 180 82 180
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 84.3 % 483 407 76 2 405
Function coverage date bins:
(240..) days: 85.0 % 20 17 3 1 16
Branch coverage date bins:
(240..) days: 68.7 % 262 180 82 180

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * expandedrecord.c
                                  4                 :                :  *    Functions for manipulating composite expanded objects.
                                  5                 :                :  *
                                  6                 :                :  * This module supports "expanded objects" (cf. expandeddatum.h) that can
                                  7                 :                :  * store values of named composite types, domains over named composite types,
                                  8                 :                :  * and record types (registered or anonymous).
                                  9                 :                :  *
                                 10                 :                :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
                                 11                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                 12                 :                :  *
                                 13                 :                :  *
                                 14                 :                :  * IDENTIFICATION
                                 15                 :                :  *    src/backend/utils/adt/expandedrecord.c
                                 16                 :                :  *
                                 17                 :                :  *-------------------------------------------------------------------------
                                 18                 :                :  */
                                 19                 :                : #include "postgres.h"
                                 20                 :                : 
                                 21                 :                : #include "access/detoast.h"
                                 22                 :                : #include "access/heaptoast.h"
                                 23                 :                : #include "access/htup_details.h"
                                 24                 :                : #include "catalog/heap.h"
                                 25                 :                : #include "catalog/pg_type.h"
                                 26                 :                : #include "utils/builtins.h"
                                 27                 :                : #include "utils/datum.h"
                                 28                 :                : #include "utils/expandedrecord.h"
                                 29                 :                : #include "utils/memutils.h"
                                 30                 :                : #include "utils/typcache.h"
                                 31                 :                : 
                                 32                 :                : 
                                 33                 :                : /* "Methods" required for an expanded object */
                                 34                 :                : static Size ER_get_flat_size(ExpandedObjectHeader *eohptr);
                                 35                 :                : static void ER_flatten_into(ExpandedObjectHeader *eohptr,
                                 36                 :                :                             void *result, Size allocated_size);
                                 37                 :                : 
                                 38                 :                : static const ExpandedObjectMethods ER_methods =
                                 39                 :                : {
                                 40                 :                :     ER_get_flat_size,
                                 41                 :                :     ER_flatten_into
                                 42                 :                : };
                                 43                 :                : 
                                 44                 :                : /* Other local functions */
                                 45                 :                : static void ER_mc_callback(void *arg);
                                 46                 :                : static MemoryContext get_short_term_cxt(ExpandedRecordHeader *erh);
                                 47                 :                : static void build_dummy_expanded_header(ExpandedRecordHeader *main_erh);
                                 48                 :                : static pg_noinline void check_domain_for_new_field(ExpandedRecordHeader *erh,
                                 49                 :                :                                                    int fnumber,
                                 50                 :                :                                                    Datum newValue, bool isnull);
                                 51                 :                : static pg_noinline void check_domain_for_new_tuple(ExpandedRecordHeader *erh,
                                 52                 :                :                                                    HeapTuple tuple);
                                 53                 :                : 
                                 54                 :                : 
                                 55                 :                : /*
                                 56                 :                :  * Build an expanded record of the specified composite type
                                 57                 :                :  *
                                 58                 :                :  * type_id can be RECORDOID, but only if a positive typmod is given.
                                 59                 :                :  *
                                 60                 :                :  * The expanded record is initially "empty", having a state logically
                                 61                 :                :  * equivalent to a NULL composite value (not ROW(NULL, NULL, ...)).
                                 62                 :                :  * Note that this might not be a valid state for a domain type;
                                 63                 :                :  * if the caller needs to check that, call
                                 64                 :                :  * expanded_record_set_tuple(erh, NULL, false, false).
                                 65                 :                :  *
                                 66                 :                :  * The expanded object will be a child of parentcontext.
                                 67                 :                :  */
                                 68                 :                : ExpandedRecordHeader *
 2252 tgl@sss.pgh.pa.us          69                 :CBC         327 : make_expanded_record_from_typeid(Oid type_id, int32 typmod,
                                 70                 :                :                                  MemoryContext parentcontext)
                                 71                 :                : {
                                 72                 :                :     ExpandedRecordHeader *erh;
                                 73                 :            327 :     int         flags = 0;
                                 74                 :                :     TupleDesc   tupdesc;
                                 75                 :                :     uint64      tupdesc_id;
                                 76                 :                :     MemoryContext objcxt;
                                 77                 :                :     char       *chunk;
                                 78                 :                : 
                                 79         [ +  + ]:            327 :     if (type_id != RECORDOID)
                                 80                 :                :     {
                                 81                 :                :         /*
                                 82                 :                :          * Consult the typcache to see if it's a domain over composite, and in
                                 83                 :                :          * any case to get the tupdesc and tupdesc identifier.
                                 84                 :                :          */
                                 85                 :                :         TypeCacheEntry *typentry;
                                 86                 :                : 
                                 87                 :            297 :         typentry = lookup_type_cache(type_id,
                                 88                 :                :                                      TYPECACHE_TUPDESC |
                                 89                 :                :                                      TYPECACHE_DOMAIN_BASE_INFO);
                                 90         [ +  + ]:            297 :         if (typentry->typtype == TYPTYPE_DOMAIN)
                                 91                 :                :         {
                                 92                 :             35 :             flags |= ER_FLAG_IS_DOMAIN;
                                 93                 :             35 :             typentry = lookup_type_cache(typentry->domainBaseType,
                                 94                 :                :                                          TYPECACHE_TUPDESC);
                                 95                 :                :         }
                                 96         [ -  + ]:            297 :         if (typentry->tupDesc == NULL)
 2252 tgl@sss.pgh.pa.us          97         [ #  # ]:UBC           0 :             ereport(ERROR,
                                 98                 :                :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 99                 :                :                      errmsg("type %s is not composite",
                                100                 :                :                             format_type_be(type_id))));
 2252 tgl@sss.pgh.pa.us         101                 :CBC         297 :         tupdesc = typentry->tupDesc;
                                102                 :            297 :         tupdesc_id = typentry->tupDesc_identifier;
                                103                 :                :     }
                                104                 :                :     else
                                105                 :                :     {
                                106                 :                :         /*
                                107                 :                :          * For RECORD types, get the tupdesc and identifier from typcache.
                                108                 :                :          */
                                109                 :             30 :         tupdesc = lookup_rowtype_tupdesc(type_id, typmod);
                                110                 :             30 :         tupdesc_id = assign_record_type_identifier(type_id, typmod);
                                111                 :                :     }
                                112                 :                : 
                                113                 :                :     /*
                                114                 :                :      * Allocate private context for expanded object.  We use a regular-size
                                115                 :                :      * context, not a small one, to improve the odds that we can fit a tupdesc
                                116                 :                :      * into it without needing an extra malloc block.  (This code path doesn't
                                117                 :                :      * ever need to copy a tupdesc into the expanded record, but let's be
                                118                 :                :      * consistent with the other ways of making an expanded record.)
                                119                 :                :      */
                                120                 :            327 :     objcxt = AllocSetContextCreate(parentcontext,
                                121                 :                :                                    "expanded record",
                                122                 :                :                                    ALLOCSET_DEFAULT_SIZES);
                                123                 :                : 
                                124                 :                :     /*
                                125                 :                :      * Since we already know the number of fields in the tupdesc, we can
                                126                 :                :      * allocate the dvalues/dnulls arrays along with the record header.  This
                                127                 :                :      * is useless if we never need those arrays, but it costs almost nothing,
                                128                 :                :      * and it will save a palloc cycle if we do need them.
                                129                 :                :      */
                                130                 :                :     erh = (ExpandedRecordHeader *)
                                131                 :            327 :         MemoryContextAlloc(objcxt, MAXALIGN(sizeof(ExpandedRecordHeader))
                                132                 :            327 :                            + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
                                133                 :                : 
                                134                 :                :     /* Ensure all header fields are initialized to 0/null */
                                135                 :            327 :     memset(erh, 0, sizeof(ExpandedRecordHeader));
                                136                 :                : 
                                137                 :            327 :     EOH_init_header(&erh->hdr, &ER_methods, objcxt);
                                138                 :            327 :     erh->er_magic = ER_MAGIC;
                                139                 :                : 
                                140                 :                :     /* Set up dvalues/dnulls, with no valid contents as yet */
                                141                 :            327 :     chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
                                142                 :            327 :     erh->dvalues = (Datum *) chunk;
                                143                 :            327 :     erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
                                144                 :            327 :     erh->nfields = tupdesc->natts;
                                145                 :                : 
                                146                 :                :     /* Fill in composite-type identification info */
                                147                 :            327 :     erh->er_decltypeid = type_id;
                                148                 :            327 :     erh->er_typeid = tupdesc->tdtypeid;
                                149                 :            327 :     erh->er_typmod = tupdesc->tdtypmod;
                                150                 :            327 :     erh->er_tupdesc_id = tupdesc_id;
                                151                 :                : 
                                152                 :            327 :     erh->flags = flags;
                                153                 :                : 
                                154                 :                :     /*
                                155                 :                :      * If what we got from the typcache is a refcounted tupdesc, we need to
                                156                 :                :      * acquire our own refcount on it.  We manage the refcount with a memory
                                157                 :                :      * context callback rather than assuming that the CurrentResourceOwner is
                                158                 :                :      * longer-lived than this expanded object.
                                159                 :                :      */
                                160         [ +  - ]:            327 :     if (tupdesc->tdrefcount >= 0)
                                161                 :                :     {
                                162                 :                :         /* Register callback to release the refcount */
                                163                 :            327 :         erh->er_mcb.func = ER_mc_callback;
                                164                 :            327 :         erh->er_mcb.arg = (void *) erh;
                                165                 :            327 :         MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
                                166                 :                :                                            &erh->er_mcb);
                                167                 :                : 
                                168                 :                :         /* And save the pointer */
                                169                 :            327 :         erh->er_tupdesc = tupdesc;
                                170                 :            327 :         tupdesc->tdrefcount++;
                                171                 :                : 
                                172                 :                :         /* If we called lookup_rowtype_tupdesc, release the pin it took */
                                173         [ +  + ]:            327 :         if (type_id == RECORDOID)
  851                           174         [ +  - ]:             30 :             ReleaseTupleDesc(tupdesc);
                                175                 :                :     }
                                176                 :                :     else
                                177                 :                :     {
                                178                 :                :         /*
                                179                 :                :          * If it's not refcounted, just assume it will outlive the expanded
                                180                 :                :          * object.  (This can happen for shared record types, for instance.)
                                181                 :                :          */
 2252 tgl@sss.pgh.pa.us         182                 :UBC           0 :         erh->er_tupdesc = tupdesc;
                                183                 :                :     }
                                184                 :                : 
                                185                 :                :     /*
                                186                 :                :      * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
                                187                 :                :      * record remains logically empty.
                                188                 :                :      */
                                189                 :                : 
 2252 tgl@sss.pgh.pa.us         190                 :CBC         327 :     return erh;
                                191                 :                : }
                                192                 :                : 
                                193                 :                : /*
                                194                 :                :  * Build an expanded record of the rowtype defined by the tupdesc
                                195                 :                :  *
                                196                 :                :  * The tupdesc is copied if necessary (i.e., if we can't just bump its
                                197                 :                :  * reference count instead).
                                198                 :                :  *
                                199                 :                :  * The expanded record is initially "empty", having a state logically
                                200                 :                :  * equivalent to a NULL composite value (not ROW(NULL, NULL, ...)).
                                201                 :                :  *
                                202                 :                :  * The expanded object will be a child of parentcontext.
                                203                 :                :  */
                                204                 :                : ExpandedRecordHeader *
                                205                 :          10247 : make_expanded_record_from_tupdesc(TupleDesc tupdesc,
                                206                 :                :                                   MemoryContext parentcontext)
                                207                 :                : {
                                208                 :                :     ExpandedRecordHeader *erh;
                                209                 :                :     uint64      tupdesc_id;
                                210                 :                :     MemoryContext objcxt;
                                211                 :                :     MemoryContext oldcxt;
                                212                 :                :     char       *chunk;
                                213                 :                : 
                                214         [ +  + ]:          10247 :     if (tupdesc->tdtypeid != RECORDOID)
                                215                 :                :     {
                                216                 :                :         /*
                                217                 :                :          * If it's a named composite type (not RECORD), we prefer to reference
                                218                 :                :          * the typcache's copy of the tupdesc, which is guaranteed to be
                                219                 :                :          * refcounted (the given tupdesc might not be).  In any case, we need
                                220                 :                :          * to consult the typcache to get the correct tupdesc identifier.
                                221                 :                :          *
                                222                 :                :          * Note that tdtypeid couldn't be a domain type, so we need not
                                223                 :                :          * consider that case here.
                                224                 :                :          */
                                225                 :                :         TypeCacheEntry *typentry;
                                226                 :                : 
                                227                 :           7537 :         typentry = lookup_type_cache(tupdesc->tdtypeid, TYPECACHE_TUPDESC);
                                228         [ -  + ]:           7537 :         if (typentry->tupDesc == NULL)
 2252 tgl@sss.pgh.pa.us         229         [ #  # ]:UBC           0 :             ereport(ERROR,
                                230                 :                :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                231                 :                :                      errmsg("type %s is not composite",
                                232                 :                :                             format_type_be(tupdesc->tdtypeid))));
 2252 tgl@sss.pgh.pa.us         233                 :CBC        7537 :         tupdesc = typentry->tupDesc;
                                234                 :           7537 :         tupdesc_id = typentry->tupDesc_identifier;
                                235                 :                :     }
                                236                 :                :     else
                                237                 :                :     {
                                238                 :                :         /*
                                239                 :                :          * For RECORD types, get the appropriate unique identifier (possibly
                                240                 :                :          * freshly assigned).
                                241                 :                :          */
                                242                 :           2710 :         tupdesc_id = assign_record_type_identifier(tupdesc->tdtypeid,
                                243                 :                :                                                    tupdesc->tdtypmod);
                                244                 :                :     }
                                245                 :                : 
                                246                 :                :     /*
                                247                 :                :      * Allocate private context for expanded object.  We use a regular-size
                                248                 :                :      * context, not a small one, to improve the odds that we can fit a tupdesc
                                249                 :                :      * into it without needing an extra malloc block.
                                250                 :                :      */
                                251                 :          10247 :     objcxt = AllocSetContextCreate(parentcontext,
                                252                 :                :                                    "expanded record",
                                253                 :                :                                    ALLOCSET_DEFAULT_SIZES);
                                254                 :                : 
                                255                 :                :     /*
                                256                 :                :      * Since we already know the number of fields in the tupdesc, we can
                                257                 :                :      * allocate the dvalues/dnulls arrays along with the record header.  This
                                258                 :                :      * is useless if we never need those arrays, but it costs almost nothing,
                                259                 :                :      * and it will save a palloc cycle if we do need them.
                                260                 :                :      */
                                261                 :                :     erh = (ExpandedRecordHeader *)
                                262                 :          10247 :         MemoryContextAlloc(objcxt, MAXALIGN(sizeof(ExpandedRecordHeader))
                                263                 :          10247 :                            + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
                                264                 :                : 
                                265                 :                :     /* Ensure all header fields are initialized to 0/null */
                                266                 :          10247 :     memset(erh, 0, sizeof(ExpandedRecordHeader));
                                267                 :                : 
                                268                 :          10247 :     EOH_init_header(&erh->hdr, &ER_methods, objcxt);
                                269                 :          10247 :     erh->er_magic = ER_MAGIC;
                                270                 :                : 
                                271                 :                :     /* Set up dvalues/dnulls, with no valid contents as yet */
                                272                 :          10247 :     chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
                                273                 :          10247 :     erh->dvalues = (Datum *) chunk;
                                274                 :          10247 :     erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
                                275                 :          10247 :     erh->nfields = tupdesc->natts;
                                276                 :                : 
                                277                 :                :     /* Fill in composite-type identification info */
                                278                 :          10247 :     erh->er_decltypeid = erh->er_typeid = tupdesc->tdtypeid;
                                279                 :          10247 :     erh->er_typmod = tupdesc->tdtypmod;
                                280                 :          10247 :     erh->er_tupdesc_id = tupdesc_id;
                                281                 :                : 
                                282                 :                :     /*
                                283                 :                :      * Copy tupdesc if needed, but we prefer to bump its refcount if possible.
                                284                 :                :      * We manage the refcount with a memory context callback rather than
                                285                 :                :      * assuming that the CurrentResourceOwner is longer-lived than this
                                286                 :                :      * expanded object.
                                287                 :                :      */
                                288         [ +  + ]:          10247 :     if (tupdesc->tdrefcount >= 0)
                                289                 :                :     {
                                290                 :                :         /* Register callback to release the refcount */
                                291                 :           7537 :         erh->er_mcb.func = ER_mc_callback;
                                292                 :           7537 :         erh->er_mcb.arg = (void *) erh;
                                293                 :           7537 :         MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
                                294                 :                :                                            &erh->er_mcb);
                                295                 :                : 
                                296                 :                :         /* And save the pointer */
                                297                 :           7537 :         erh->er_tupdesc = tupdesc;
                                298                 :           7537 :         tupdesc->tdrefcount++;
                                299                 :                :     }
                                300                 :                :     else
                                301                 :                :     {
                                302                 :                :         /* Just copy it */
                                303                 :           2710 :         oldcxt = MemoryContextSwitchTo(objcxt);
                                304                 :           2710 :         erh->er_tupdesc = CreateTupleDescCopy(tupdesc);
                                305                 :           2710 :         erh->flags |= ER_FLAG_TUPDESC_ALLOCED;
                                306                 :           2710 :         MemoryContextSwitchTo(oldcxt);
                                307                 :                :     }
                                308                 :                : 
                                309                 :                :     /*
                                310                 :                :      * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
                                311                 :                :      * record remains logically empty.
                                312                 :                :      */
                                313                 :                : 
                                314                 :          10247 :     return erh;
                                315                 :                : }
                                316                 :                : 
                                317                 :                : /*
                                318                 :                :  * Build an expanded record of the same rowtype as the given expanded record
                                319                 :                :  *
                                320                 :                :  * This is faster than either of the above routines because we can bypass
                                321                 :                :  * typcache lookup(s).
                                322                 :                :  *
                                323                 :                :  * The expanded record is initially "empty" --- we do not copy whatever
                                324                 :                :  * tuple might be in the source expanded record.
                                325                 :                :  *
                                326                 :                :  * The expanded object will be a child of parentcontext.
                                327                 :                :  */
                                328                 :                : ExpandedRecordHeader *
                                329                 :           7699 : make_expanded_record_from_exprecord(ExpandedRecordHeader *olderh,
                                330                 :                :                                     MemoryContext parentcontext)
                                331                 :                : {
                                332                 :                :     ExpandedRecordHeader *erh;
                                333                 :           7699 :     TupleDesc   tupdesc = expanded_record_get_tupdesc(olderh);
                                334                 :                :     MemoryContext objcxt;
                                335                 :                :     MemoryContext oldcxt;
                                336                 :                :     char       *chunk;
                                337                 :                : 
                                338                 :                :     /*
                                339                 :                :      * Allocate private context for expanded object.  We use a regular-size
                                340                 :                :      * context, not a small one, to improve the odds that we can fit a tupdesc
                                341                 :                :      * into it without needing an extra malloc block.
                                342                 :                :      */
                                343                 :           7699 :     objcxt = AllocSetContextCreate(parentcontext,
                                344                 :                :                                    "expanded record",
                                345                 :                :                                    ALLOCSET_DEFAULT_SIZES);
                                346                 :                : 
                                347                 :                :     /*
                                348                 :                :      * Since we already know the number of fields in the tupdesc, we can
                                349                 :                :      * allocate the dvalues/dnulls arrays along with the record header.  This
                                350                 :                :      * is useless if we never need those arrays, but it costs almost nothing,
                                351                 :                :      * and it will save a palloc cycle if we do need them.
                                352                 :                :      */
                                353                 :                :     erh = (ExpandedRecordHeader *)
                                354                 :           7699 :         MemoryContextAlloc(objcxt, MAXALIGN(sizeof(ExpandedRecordHeader))
                                355                 :           7699 :                            + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
                                356                 :                : 
                                357                 :                :     /* Ensure all header fields are initialized to 0/null */
                                358                 :           7699 :     memset(erh, 0, sizeof(ExpandedRecordHeader));
                                359                 :                : 
                                360                 :           7699 :     EOH_init_header(&erh->hdr, &ER_methods, objcxt);
                                361                 :           7699 :     erh->er_magic = ER_MAGIC;
                                362                 :                : 
                                363                 :                :     /* Set up dvalues/dnulls, with no valid contents as yet */
                                364                 :           7699 :     chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
                                365                 :           7699 :     erh->dvalues = (Datum *) chunk;
                                366                 :           7699 :     erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
                                367                 :           7699 :     erh->nfields = tupdesc->natts;
                                368                 :                : 
                                369                 :                :     /* Fill in composite-type identification info */
                                370                 :           7699 :     erh->er_decltypeid = olderh->er_decltypeid;
                                371                 :           7699 :     erh->er_typeid = olderh->er_typeid;
                                372                 :           7699 :     erh->er_typmod = olderh->er_typmod;
                                373                 :           7699 :     erh->er_tupdesc_id = olderh->er_tupdesc_id;
                                374                 :                : 
                                375                 :                :     /* The only flag bit that transfers over is IS_DOMAIN */
                                376                 :           7699 :     erh->flags = olderh->flags & ER_FLAG_IS_DOMAIN;
                                377                 :                : 
                                378                 :                :     /*
                                379                 :                :      * Copy tupdesc if needed, but we prefer to bump its refcount if possible.
                                380                 :                :      * We manage the refcount with a memory context callback rather than
                                381                 :                :      * assuming that the CurrentResourceOwner is longer-lived than this
                                382                 :                :      * expanded object.
                                383                 :                :      */
                                384         [ +  - ]:           7699 :     if (tupdesc->tdrefcount >= 0)
                                385                 :                :     {
                                386                 :                :         /* Register callback to release the refcount */
                                387                 :           7699 :         erh->er_mcb.func = ER_mc_callback;
                                388                 :           7699 :         erh->er_mcb.arg = (void *) erh;
                                389                 :           7699 :         MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
                                390                 :                :                                            &erh->er_mcb);
                                391                 :                : 
                                392                 :                :         /* And save the pointer */
                                393                 :           7699 :         erh->er_tupdesc = tupdesc;
                                394                 :           7699 :         tupdesc->tdrefcount++;
                                395                 :                :     }
 2252 tgl@sss.pgh.pa.us         396         [ #  # ]:UBC           0 :     else if (olderh->flags & ER_FLAG_TUPDESC_ALLOCED)
                                397                 :                :     {
                                398                 :                :         /* We need to make our own copy of the tupdesc */
                                399                 :              0 :         oldcxt = MemoryContextSwitchTo(objcxt);
                                400                 :              0 :         erh->er_tupdesc = CreateTupleDescCopy(tupdesc);
                                401                 :              0 :         erh->flags |= ER_FLAG_TUPDESC_ALLOCED;
                                402                 :              0 :         MemoryContextSwitchTo(oldcxt);
                                403                 :                :     }
                                404                 :                :     else
                                405                 :                :     {
                                406                 :                :         /*
                                407                 :                :          * Assume the tupdesc will outlive this expanded object, just like
                                408                 :                :          * we're assuming it will outlive the source object.
                                409                 :                :          */
                                410                 :              0 :         erh->er_tupdesc = tupdesc;
                                411                 :                :     }
                                412                 :                : 
                                413                 :                :     /*
                                414                 :                :      * We don't set ER_FLAG_DVALUES_VALID or ER_FLAG_FVALUE_VALID, so the
                                415                 :                :      * record remains logically empty.
                                416                 :                :      */
                                417                 :                : 
 2252 tgl@sss.pgh.pa.us         418                 :CBC        7699 :     return erh;
                                419                 :                : }
                                420                 :                : 
                                421                 :                : /*
                                422                 :                :  * Insert given tuple as the value of the expanded record
                                423                 :                :  *
                                424                 :                :  * It is caller's responsibility that the tuple matches the record's
                                425                 :                :  * previously-assigned rowtype.  (However domain constraints, if any,
                                426                 :                :  * will be checked here.)
                                427                 :                :  *
                                428                 :                :  * The tuple is physically copied into the expanded record's local storage
                                429                 :                :  * if "copy" is true, otherwise it's caller's responsibility that the tuple
                                430                 :                :  * will live as long as the expanded record does.
                                431                 :                :  *
                                432                 :                :  * Out-of-line field values in the tuple are automatically inlined if
                                433                 :                :  * "expand_external" is true, otherwise not.  (The combination copy = false,
                                434                 :                :  * expand_external = true is not sensible and not supported.)
                                435                 :                :  *
                                436                 :                :  * Alternatively, tuple can be NULL, in which case we just set the expanded
                                437                 :                :  * record to be empty.
                                438                 :                :  */
                                439                 :                : void
                                440                 :          15224 : expanded_record_set_tuple(ExpandedRecordHeader *erh,
                                441                 :                :                           HeapTuple tuple,
                                442                 :                :                           bool copy,
                                443                 :                :                           bool expand_external)
                                444                 :                : {
                                445                 :                :     int         oldflags;
                                446                 :                :     HeapTuple   oldtuple;
                                447                 :                :     char       *oldfstartptr;
                                448                 :                :     char       *oldfendptr;
                                449                 :                :     int         newflags;
                                450                 :                :     HeapTuple   newtuple;
                                451                 :                :     MemoryContext oldcxt;
                                452                 :                : 
                                453                 :                :     /* Shouldn't ever be trying to assign new data to a dummy header */
                                454         [ -  + ]:          15224 :     Assert(!(erh->flags & ER_FLAG_IS_DUMMY));
                                455                 :                : 
                                456                 :                :     /*
                                457                 :                :      * Before performing the assignment, see if result will satisfy domain.
                                458                 :                :      */
                                459         [ +  + ]:          15224 :     if (erh->flags & ER_FLAG_IS_DOMAIN)
                                460                 :             32 :         check_domain_for_new_tuple(erh, tuple);
                                461                 :                : 
                                462                 :                :     /*
                                463                 :                :      * If we need to get rid of out-of-line field values, do so, using the
                                464                 :                :      * short-term context to avoid leaking whatever cruft the toast fetch
                                465                 :                :      * might generate.
                                466                 :                :      */
 2160                           467   [ +  +  +  - ]:          15216 :     if (expand_external && tuple)
                                468                 :                :     {
                                469                 :                :         /* Assert caller didn't ask for unsupported case */
                                470         [ -  + ]:           2358 :         Assert(copy);
                                471         [ +  + ]:           2358 :         if (HeapTupleHasExternal(tuple))
                                472                 :                :         {
                                473                 :              8 :             oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
                                474                 :              8 :             tuple = toast_flatten_tuple(tuple, erh->er_tupdesc);
                                475                 :              8 :             MemoryContextSwitchTo(oldcxt);
                                476                 :                :         }
                                477                 :                :         else
                                478                 :           2350 :             expand_external = false;    /* need not clean up below */
                                479                 :                :     }
                                480                 :                : 
                                481                 :                :     /*
                                482                 :                :      * Initialize new flags, keeping only non-data status bits.
                                483                 :                :      */
 2252                           484                 :          15216 :     oldflags = erh->flags;
                                485                 :          15216 :     newflags = oldflags & ER_FLAGS_NON_DATA;
                                486                 :                : 
                                487                 :                :     /*
                                488                 :                :      * Copy tuple into local storage if needed.  We must be sure this succeeds
                                489                 :                :      * before we start to modify the expanded record's state.
                                490                 :                :      */
                                491   [ +  +  +  - ]:          15216 :     if (copy && tuple)
                                492                 :                :     {
                                493                 :           5250 :         oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
                                494                 :           5250 :         newtuple = heap_copytuple(tuple);
                                495                 :           5250 :         newflags |= ER_FLAG_FVALUE_ALLOCED;
                                496                 :           5250 :         MemoryContextSwitchTo(oldcxt);
                                497                 :                : 
                                498                 :                :         /* We can now flush anything that detoasting might have leaked. */
 2160                           499         [ +  + ]:           5250 :         if (expand_external)
                                500                 :              8 :             MemoryContextReset(erh->er_short_term_cxt);
                                501                 :                :     }
                                502                 :                :     else
 2252                           503                 :           9966 :         newtuple = tuple;
                                504                 :                : 
                                505                 :                :     /* Make copies of fields we're about to overwrite */
                                506                 :          15216 :     oldtuple = erh->fvalue;
                                507                 :          15216 :     oldfstartptr = erh->fstartptr;
                                508                 :          15216 :     oldfendptr = erh->fendptr;
                                509                 :                : 
                                510                 :                :     /*
                                511                 :                :      * It's now safe to update the expanded record's state.
                                512                 :                :      */
                                513         [ +  + ]:          15216 :     if (newtuple)
                                514                 :                :     {
                                515                 :                :         /* Save flat representation */
                                516                 :          15204 :         erh->fvalue = newtuple;
                                517                 :          15204 :         erh->fstartptr = (char *) newtuple->t_data;
                                518                 :          15204 :         erh->fendptr = ((char *) newtuple->t_data) + newtuple->t_len;
                                519                 :          15204 :         newflags |= ER_FLAG_FVALUE_VALID;
                                520                 :                : 
                                521                 :                :         /* Remember if we have any out-of-line field values */
                                522         [ +  + ]:          15204 :         if (HeapTupleHasExternal(newtuple))
                                523                 :            103 :             newflags |= ER_FLAG_HAVE_EXTERNAL;
                                524                 :                :     }
                                525                 :                :     else
                                526                 :                :     {
                                527                 :             12 :         erh->fvalue = NULL;
                                528                 :             12 :         erh->fstartptr = erh->fendptr = NULL;
                                529                 :                :     }
                                530                 :                : 
                                531                 :          15216 :     erh->flags = newflags;
                                532                 :                : 
                                533                 :                :     /* Reset flat-size info; we don't bother to make it valid now */
                                534                 :          15216 :     erh->flat_size = 0;
                                535                 :                : 
                                536                 :                :     /*
                                537                 :                :      * Now, release any storage belonging to old field values.  It's safe to
                                538                 :                :      * do this because ER_FLAG_DVALUES_VALID is no longer set in erh->flags;
                                539                 :                :      * even if we fail partway through, the record is valid, and at worst
                                540                 :                :      * we've failed to reclaim some space.
                                541                 :                :      */
                                542         [ +  + ]:          15216 :     if (oldflags & ER_FLAG_DVALUES_ALLOCED)
                                543                 :                :     {
                                544                 :             24 :         TupleDesc   tupdesc = erh->er_tupdesc;
                                545                 :                :         int         i;
                                546                 :                : 
                                547         [ +  + ]:            120 :         for (i = 0; i < erh->nfields; i++)
                                548                 :                :         {
                                549         [ +  + ]:             96 :             if (!erh->dnulls[i] &&
                                550         [ +  + ]:             84 :                 !(TupleDescAttr(tupdesc, i)->attbyval))
                                551                 :                :             {
                                552                 :             60 :                 char       *oldValue = (char *) DatumGetPointer(erh->dvalues[i]);
                                553                 :                : 
                                554   [ +  +  +  + ]:             60 :                 if (oldValue < oldfstartptr || oldValue >= oldfendptr)
                                555                 :             30 :                     pfree(oldValue);
                                556                 :                :             }
                                557                 :                :         }
                                558                 :                :     }
                                559                 :                : 
                                560                 :                :     /* Likewise free the old tuple, if it was locally allocated */
                                561         [ +  + ]:          15216 :     if (oldflags & ER_FLAG_FVALUE_ALLOCED)
                                562                 :           2872 :         heap_freetuple(oldtuple);
                                563                 :                : 
                                564                 :                :     /* We won't make a new deconstructed representation until/unless needed */
                                565                 :          15216 : }
                                566                 :                : 
                                567                 :                : /*
                                568                 :                :  * make_expanded_record_from_datum: build expanded record from composite Datum
                                569                 :                :  *
                                570                 :                :  * This combines the functions of make_expanded_record_from_typeid and
                                571                 :                :  * expanded_record_set_tuple.  However, we do not force a lookup of the
                                572                 :                :  * tupdesc immediately, reasoning that it might never be needed.
                                573                 :                :  *
                                574                 :                :  * The expanded object will be a child of parentcontext.
                                575                 :                :  *
                                576                 :                :  * Note: a composite datum cannot self-identify as being of a domain type,
                                577                 :                :  * so we need not consider domain cases here.
                                578                 :                :  */
                                579                 :                : Datum
 2252 tgl@sss.pgh.pa.us         580                 :UBC           0 : make_expanded_record_from_datum(Datum recorddatum, MemoryContext parentcontext)
                                581                 :                : {
                                582                 :                :     ExpandedRecordHeader *erh;
                                583                 :                :     HeapTupleHeader tuphdr;
                                584                 :                :     HeapTupleData tmptup;
                                585                 :                :     HeapTuple   newtuple;
                                586                 :                :     MemoryContext objcxt;
                                587                 :                :     MemoryContext oldcxt;
                                588                 :                : 
                                589                 :                :     /*
                                590                 :                :      * Allocate private context for expanded object.  We use a regular-size
                                591                 :                :      * context, not a small one, to improve the odds that we can fit a tupdesc
                                592                 :                :      * into it without needing an extra malloc block.
                                593                 :                :      */
                                594                 :              0 :     objcxt = AllocSetContextCreate(parentcontext,
                                595                 :                :                                    "expanded record",
                                596                 :                :                                    ALLOCSET_DEFAULT_SIZES);
                                597                 :                : 
                                598                 :                :     /* Set up expanded record header, initializing fields to 0/null */
                                599                 :                :     erh = (ExpandedRecordHeader *)
                                600                 :              0 :         MemoryContextAllocZero(objcxt, sizeof(ExpandedRecordHeader));
                                601                 :                : 
                                602                 :              0 :     EOH_init_header(&erh->hdr, &ER_methods, objcxt);
                                603                 :              0 :     erh->er_magic = ER_MAGIC;
                                604                 :                : 
                                605                 :                :     /*
                                606                 :                :      * Detoast and copy source record into private context, as a HeapTuple.
                                607                 :                :      * (If we actually have to detoast the source, we'll leak some memory in
                                608                 :                :      * the caller's context, but it doesn't seem worth worrying about.)
                                609                 :                :      */
                                610                 :              0 :     tuphdr = DatumGetHeapTupleHeader(recorddatum);
                                611                 :                : 
                                612                 :              0 :     tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
                                613                 :              0 :     ItemPointerSetInvalid(&(tmptup.t_self));
                                614                 :              0 :     tmptup.t_tableOid = InvalidOid;
                                615                 :              0 :     tmptup.t_data = tuphdr;
                                616                 :                : 
                                617                 :              0 :     oldcxt = MemoryContextSwitchTo(objcxt);
                                618                 :              0 :     newtuple = heap_copytuple(&tmptup);
                                619                 :              0 :     erh->flags |= ER_FLAG_FVALUE_ALLOCED;
                                620                 :              0 :     MemoryContextSwitchTo(oldcxt);
                                621                 :                : 
                                622                 :                :     /* Fill in composite-type identification info */
                                623                 :              0 :     erh->er_decltypeid = erh->er_typeid = HeapTupleHeaderGetTypeId(tuphdr);
                                624                 :              0 :     erh->er_typmod = HeapTupleHeaderGetTypMod(tuphdr);
                                625                 :                : 
                                626                 :                :     /* remember we have a flat representation */
                                627                 :              0 :     erh->fvalue = newtuple;
                                628                 :              0 :     erh->fstartptr = (char *) newtuple->t_data;
                                629                 :              0 :     erh->fendptr = ((char *) newtuple->t_data) + newtuple->t_len;
                                630                 :              0 :     erh->flags |= ER_FLAG_FVALUE_VALID;
                                631                 :                : 
                                632                 :                :     /* Shouldn't need to set ER_FLAG_HAVE_EXTERNAL */
                                633         [ #  # ]:              0 :     Assert(!HeapTupleHeaderHasExternal(tuphdr));
                                634                 :                : 
                                635                 :                :     /*
                                636                 :                :      * We won't look up the tupdesc till we have to, nor make a deconstructed
                                637                 :                :      * representation.  We don't have enough info to fill flat_size and
                                638                 :                :      * friends, either.
                                639                 :                :      */
                                640                 :                : 
                                641                 :                :     /* return a R/W pointer to the expanded record */
                                642                 :              0 :     return EOHPGetRWDatum(&erh->hdr);
                                643                 :                : }
                                644                 :                : 
                                645                 :                : /*
                                646                 :                :  * get_flat_size method for expanded records
                                647                 :                :  *
                                648                 :                :  * Note: call this in a reasonably short-lived memory context, in case of
                                649                 :                :  * memory leaks from activities such as detoasting.
                                650                 :                :  */
                                651                 :                : static Size
 2252 tgl@sss.pgh.pa.us         652                 :CBC        1381 : ER_get_flat_size(ExpandedObjectHeader *eohptr)
                                653                 :                : {
                                654                 :           1381 :     ExpandedRecordHeader *erh = (ExpandedRecordHeader *) eohptr;
                                655                 :                :     TupleDesc   tupdesc;
                                656                 :                :     Size        len;
                                657                 :                :     Size        data_len;
                                658                 :                :     int         hoff;
                                659                 :                :     bool        hasnull;
                                660                 :                :     int         i;
                                661                 :                : 
                                662         [ -  + ]:           1381 :     Assert(erh->er_magic == ER_MAGIC);
                                663                 :                : 
                                664                 :                :     /*
                                665                 :                :      * The flat representation has to be a valid composite datum.  Make sure
                                666                 :                :      * that we have a registered, not anonymous, RECORD type.
                                667                 :                :      */
                                668         [ +  + ]:           1381 :     if (erh->er_typeid == RECORDOID &&
                                669         [ +  + ]:             34 :         erh->er_typmod < 0)
                                670                 :                :     {
                                671                 :              9 :         tupdesc = expanded_record_get_tupdesc(erh);
                                672                 :              9 :         assign_record_type_typmod(tupdesc);
                                673                 :              9 :         erh->er_typmod = tupdesc->tdtypmod;
                                674                 :                :     }
                                675                 :                : 
                                676                 :                :     /*
                                677                 :                :      * If we have a valid flattened value without out-of-line fields, we can
                                678                 :                :      * just use it as-is.
                                679                 :                :      */
                                680         [ +  + ]:           1381 :     if (erh->flags & ER_FLAG_FVALUE_VALID &&
                                681         [ +  + ]:           1233 :         !(erh->flags & ER_FLAG_HAVE_EXTERNAL))
                                682                 :           1209 :         return erh->fvalue->t_len;
                                683                 :                : 
                                684                 :                :     /* If we have a cached size value, believe that */
                                685         [ +  + ]:            172 :     if (erh->flat_size)
                                686                 :             14 :         return erh->flat_size;
                                687                 :                : 
                                688                 :                :     /* If we haven't yet deconstructed the tuple, do that */
                                689         [ +  + ]:            158 :     if (!(erh->flags & ER_FLAG_DVALUES_VALID))
                                690                 :             24 :         deconstruct_expanded_record(erh);
                                691                 :                : 
                                692                 :                :     /* Tuple descriptor must be valid by now */
                                693                 :            158 :     tupdesc = erh->er_tupdesc;
                                694                 :                : 
                                695                 :                :     /*
                                696                 :                :      * Composite datums mustn't contain any out-of-line values.
                                697                 :                :      */
                                698         [ +  + ]:            158 :     if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
                                699                 :                :     {
                                700         [ +  + ]:            120 :         for (i = 0; i < erh->nfields; i++)
                                701                 :                :         {
                                702                 :             96 :             Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
                                703                 :                : 
                                704         [ +  + ]:             96 :             if (!erh->dnulls[i] &&
                                705   [ +  +  +  - ]:             84 :                 !attr->attbyval && attr->attlen == -1 &&
                                706         [ +  + ]:             60 :                 VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
                                707                 :                :             {
                                708                 :                :                 /*
                                709                 :                :                  * expanded_record_set_field_internal can do the actual work
                                710                 :                :                  * of detoasting.  It needn't recheck domain constraints.
                                711                 :                :                  */
                                712                 :             30 :                 expanded_record_set_field_internal(erh, i + 1,
 2160                           713                 :             30 :                                                    erh->dvalues[i], false,
                                714                 :                :                                                    true,
                                715                 :                :                                                    false);
                                716                 :                :             }
                                717                 :                :         }
                                718                 :                : 
                                719                 :                :         /*
                                720                 :                :          * We have now removed all external field values, so we can clear the
                                721                 :                :          * flag about them.  This won't cause ER_flatten_into() to mistakenly
                                722                 :                :          * take the fast path, since expanded_record_set_field() will have
                                723                 :                :          * cleared ER_FLAG_FVALUE_VALID.
                                724                 :                :          */
 2252                           725                 :             24 :         erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
                                726                 :                :     }
                                727                 :                : 
                                728                 :                :     /* Test if we currently have any null values */
                                729                 :            158 :     hasnull = false;
                                730         [ +  + ]:            484 :     for (i = 0; i < erh->nfields; i++)
                                731                 :                :     {
                                732         [ +  + ]:            350 :         if (erh->dnulls[i])
                                733                 :                :         {
                                734                 :             24 :             hasnull = true;
                                735                 :             24 :             break;
                                736                 :                :         }
                                737                 :                :     }
                                738                 :                : 
                                739                 :                :     /* Determine total space needed */
                                740                 :            158 :     len = offsetof(HeapTupleHeaderData, t_bits);
                                741                 :                : 
                                742         [ +  + ]:            158 :     if (hasnull)
                                743                 :             24 :         len += BITMAPLEN(tupdesc->natts);
                                744                 :                : 
                                745                 :            158 :     hoff = len = MAXALIGN(len); /* align user data safely */
                                746                 :                : 
                                747                 :            158 :     data_len = heap_compute_data_size(tupdesc, erh->dvalues, erh->dnulls);
                                748                 :                : 
                                749                 :            158 :     len += data_len;
                                750                 :                : 
                                751                 :                :     /* Cache for next time */
                                752                 :            158 :     erh->flat_size = len;
                                753                 :            158 :     erh->data_len = data_len;
                                754                 :            158 :     erh->hoff = hoff;
                                755                 :            158 :     erh->hasnull = hasnull;
                                756                 :                : 
                                757                 :            158 :     return len;
                                758                 :                : }
                                759                 :                : 
                                760                 :                : /*
                                761                 :                :  * flatten_into method for expanded records
                                762                 :                :  */
                                763                 :                : static void
                                764                 :           1379 : ER_flatten_into(ExpandedObjectHeader *eohptr,
                                765                 :                :                 void *result, Size allocated_size)
                                766                 :                : {
                                767                 :           1379 :     ExpandedRecordHeader *erh = (ExpandedRecordHeader *) eohptr;
                                768                 :           1379 :     HeapTupleHeader tuphdr = (HeapTupleHeader) result;
                                769                 :                :     TupleDesc   tupdesc;
                                770                 :                : 
                                771         [ -  + ]:           1379 :     Assert(erh->er_magic == ER_MAGIC);
                                772                 :                : 
                                773                 :                :     /* Easy if we have a valid flattened value without out-of-line fields */
                                774         [ +  + ]:           1379 :     if (erh->flags & ER_FLAG_FVALUE_VALID &&
                                775         [ +  - ]:           1209 :         !(erh->flags & ER_FLAG_HAVE_EXTERNAL))
                                776                 :                :     {
                                777         [ -  + ]:           1209 :         Assert(allocated_size == erh->fvalue->t_len);
                                778                 :           1209 :         memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
                                779                 :                :         /* The original flattened value might not have datum header fields */
                                780                 :           1209 :         HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
                                781                 :           1209 :         HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
                                782                 :           1209 :         HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
                                783                 :           1209 :         return;
                                784                 :                :     }
                                785                 :                : 
                                786                 :                :     /* Else allocation should match previous get_flat_size result */
                                787         [ -  + ]:            170 :     Assert(allocated_size == erh->flat_size);
                                788                 :                : 
                                789                 :                :     /* We'll need the tuple descriptor */
                                790                 :            170 :     tupdesc = expanded_record_get_tupdesc(erh);
                                791                 :                : 
                                792                 :                :     /* We must ensure that any pad space is zero-filled */
                                793                 :            170 :     memset(tuphdr, 0, allocated_size);
                                794                 :                : 
                                795                 :                :     /* Set up header fields of composite Datum */
                                796                 :            170 :     HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
                                797                 :            170 :     HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
                                798                 :            170 :     HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
                                799                 :                :     /* We also make sure that t_ctid is invalid unless explicitly set */
                                800                 :            170 :     ItemPointerSetInvalid(&(tuphdr->t_ctid));
                                801                 :                : 
                                802                 :            170 :     HeapTupleHeaderSetNatts(tuphdr, tupdesc->natts);
                                803                 :            170 :     tuphdr->t_hoff = erh->hoff;
                                804                 :                : 
                                805                 :                :     /* And fill the data area from dvalues/dnulls */
                                806                 :            170 :     heap_fill_tuple(tupdesc,
 2252 tgl@sss.pgh.pa.us         807                 :GIC         170 :                     erh->dvalues,
                                808                 :            170 :                     erh->dnulls,
 2252 tgl@sss.pgh.pa.us         809                 :CBC         170 :                     (char *) tuphdr + erh->hoff,
                                810                 :                :                     erh->data_len,
                                811                 :                :                     &tuphdr->t_infomask,
                                812         [ +  + ]:            170 :                     (erh->hasnull ? tuphdr->t_bits : NULL));
                                813                 :                : }
                                814                 :                : 
                                815                 :                : /*
                                816                 :                :  * Look up the tupdesc for the expanded record's actual type
                                817                 :                :  *
                                818                 :                :  * Note: code internal to this module is allowed to just fetch
                                819                 :                :  * erh->er_tupdesc if ER_FLAG_DVALUES_VALID is set; otherwise it should call
                                820                 :                :  * expanded_record_get_tupdesc.  This function is the out-of-line portion
                                821                 :                :  * of expanded_record_get_tupdesc.
                                822                 :                :  */
                                823                 :                : TupleDesc
 2252 tgl@sss.pgh.pa.us         824                 :UBC           0 : expanded_record_fetch_tupdesc(ExpandedRecordHeader *erh)
                                825                 :                : {
                                826                 :                :     TupleDesc   tupdesc;
                                827                 :                : 
                                828                 :                :     /* Easy if we already have it (but caller should have checked already) */
                                829         [ #  # ]:              0 :     if (erh->er_tupdesc)
                                830                 :              0 :         return erh->er_tupdesc;
                                831                 :                : 
                                832                 :                :     /* Lookup the composite type's tupdesc using the typcache */
                                833                 :              0 :     tupdesc = lookup_rowtype_tupdesc(erh->er_typeid, erh->er_typmod);
                                834                 :                : 
                                835                 :                :     /*
                                836                 :                :      * If it's a refcounted tupdesc rather than a statically allocated one, we
                                837                 :                :      * want to manage the refcount with a memory context callback rather than
                                838                 :                :      * assuming that the CurrentResourceOwner is longer-lived than this
                                839                 :                :      * expanded object.
                                840                 :                :      */
                                841         [ #  # ]:              0 :     if (tupdesc->tdrefcount >= 0)
                                842                 :                :     {
                                843                 :                :         /* Register callback if we didn't already */
                                844         [ #  # ]:              0 :         if (erh->er_mcb.arg == NULL)
                                845                 :                :         {
                                846                 :              0 :             erh->er_mcb.func = ER_mc_callback;
                                847                 :              0 :             erh->er_mcb.arg = (void *) erh;
                                848                 :              0 :             MemoryContextRegisterResetCallback(erh->hdr.eoh_context,
                                849                 :                :                                                &erh->er_mcb);
                                850                 :                :         }
                                851                 :                : 
                                852                 :                :         /* Remember our own pointer */
                                853                 :              0 :         erh->er_tupdesc = tupdesc;
                                854                 :              0 :         tupdesc->tdrefcount++;
                                855                 :                : 
                                856                 :                :         /* Release the pin lookup_rowtype_tupdesc acquired */
  851                           857         [ #  # ]:              0 :         ReleaseTupleDesc(tupdesc);
                                858                 :                :     }
                                859                 :                :     else
                                860                 :                :     {
                                861                 :                :         /* Just remember the pointer */
 2252                           862                 :              0 :         erh->er_tupdesc = tupdesc;
                                863                 :                :     }
                                864                 :                : 
                                865                 :                :     /* In either case, fetch the process-global ID for this tupdesc */
                                866                 :              0 :     erh->er_tupdesc_id = assign_record_type_identifier(tupdesc->tdtypeid,
                                867                 :                :                                                        tupdesc->tdtypmod);
                                868                 :                : 
                                869                 :              0 :     return tupdesc;
                                870                 :                : }
                                871                 :                : 
                                872                 :                : /*
                                873                 :                :  * Get a HeapTuple representing the current value of the expanded record
                                874                 :                :  *
                                875                 :                :  * If valid, the originally stored tuple is returned, so caller must not
                                876                 :                :  * scribble on it.  Otherwise, we return a HeapTuple created in the current
                                877                 :                :  * memory context.  In either case, no attempt has been made to inline
                                878                 :                :  * out-of-line toasted values, so the tuple isn't usable as a composite
                                879                 :                :  * datum.
                                880                 :                :  *
                                881                 :                :  * Returns NULL if expanded record is empty.
                                882                 :                :  */
                                883                 :                : HeapTuple
 2252 tgl@sss.pgh.pa.us         884                 :CBC        6179 : expanded_record_get_tuple(ExpandedRecordHeader *erh)
                                885                 :                : {
                                886                 :                :     /* Easy case if we still have original tuple */
                                887         [ +  + ]:           6179 :     if (erh->flags & ER_FLAG_FVALUE_VALID)
                                888                 :           5277 :         return erh->fvalue;
                                889                 :                : 
                                890                 :                :     /* Else just build a tuple from datums */
                                891         [ +  - ]:            902 :     if (erh->flags & ER_FLAG_DVALUES_VALID)
                                892                 :            902 :         return heap_form_tuple(erh->er_tupdesc, erh->dvalues, erh->dnulls);
                                893                 :                : 
                                894                 :                :     /* Expanded record is empty */
 2252 tgl@sss.pgh.pa.us         895                 :UBC           0 :     return NULL;
                                896                 :                : }
                                897                 :                : 
                                898                 :                : /*
                                899                 :                :  * Memory context reset callback for cleaning up external resources
                                900                 :                :  */
                                901                 :                : static void
 2252 tgl@sss.pgh.pa.us         902                 :CBC       15563 : ER_mc_callback(void *arg)
                                903                 :                : {
                                904                 :          15563 :     ExpandedRecordHeader *erh = (ExpandedRecordHeader *) arg;
                                905                 :          15563 :     TupleDesc   tupdesc = erh->er_tupdesc;
                                906                 :                : 
                                907                 :                :     /* Release our privately-managed tupdesc refcount, if any */
                                908         [ +  - ]:          15563 :     if (tupdesc)
                                909                 :                :     {
                                910                 :          15563 :         erh->er_tupdesc = NULL; /* just for luck */
                                911         [ +  - ]:          15563 :         if (tupdesc->tdrefcount > 0)
                                912                 :                :         {
                                913         [ -  + ]:          15563 :             if (--tupdesc->tdrefcount == 0)
 2252 tgl@sss.pgh.pa.us         914                 :UBC           0 :                 FreeTupleDesc(tupdesc);
                                915                 :                :         }
                                916                 :                :     }
 2252 tgl@sss.pgh.pa.us         917                 :CBC       15563 : }
                                918                 :                : 
                                919                 :                : /*
                                920                 :                :  * DatumGetExpandedRecord: get a writable expanded record from an input argument
                                921                 :                :  *
                                922                 :                :  * Caution: if the input is a read/write pointer, this returns the input
                                923                 :                :  * argument; so callers must be sure that their changes are "safe", that is
                                924                 :                :  * they cannot leave the record in a corrupt state.
                                925                 :                :  */
                                926                 :                : ExpandedRecordHeader *
 2252 tgl@sss.pgh.pa.us         927                 :UBC           0 : DatumGetExpandedRecord(Datum d)
                                928                 :                : {
                                929                 :                :     /* If it's a writable expanded record already, just return it */
                                930   [ #  #  #  # ]:              0 :     if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
                                931                 :                :     {
                                932                 :              0 :         ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(d);
                                933                 :                : 
                                934         [ #  # ]:              0 :         Assert(erh->er_magic == ER_MAGIC);
                                935                 :              0 :         return erh;
                                936                 :                :     }
                                937                 :                : 
                                938                 :                :     /* Else expand the hard way */
                                939                 :              0 :     d = make_expanded_record_from_datum(d, CurrentMemoryContext);
                                940                 :              0 :     return (ExpandedRecordHeader *) DatumGetEOHP(d);
                                941                 :                : }
                                942                 :                : 
                                943                 :                : /*
                                944                 :                :  * Create the Datum/isnull representation of an expanded record object
                                945                 :                :  * if we didn't do so already.  After calling this, it's OK to read the
                                946                 :                :  * dvalues/dnulls arrays directly, rather than going through get_field.
                                947                 :                :  *
                                948                 :                :  * Note that if the object is currently empty ("null"), this will change
                                949                 :                :  * it to represent a row of nulls.
                                950                 :                :  */
                                951                 :                : void
 2252 tgl@sss.pgh.pa.us         952                 :CBC       11877 : deconstruct_expanded_record(ExpandedRecordHeader *erh)
                                953                 :                : {
                                954                 :                :     TupleDesc   tupdesc;
                                955                 :                :     Datum      *dvalues;
                                956                 :                :     bool       *dnulls;
                                957                 :                :     int         nfields;
                                958                 :                : 
                                959         [ +  + ]:          11877 :     if (erh->flags & ER_FLAG_DVALUES_VALID)
                                960                 :             15 :         return;                 /* already valid, nothing to do */
                                961                 :                : 
                                962                 :                :     /* We'll need the tuple descriptor */
                                963                 :          11862 :     tupdesc = expanded_record_get_tupdesc(erh);
                                964                 :                : 
                                965                 :                :     /*
                                966                 :                :      * Allocate arrays in private context, if we don't have them already.  We
                                967                 :                :      * don't expect to see a change in nfields here, so while we cope if it
                                968                 :                :      * happens, we don't bother avoiding a leak of the old arrays (which might
                                969                 :                :      * not be separately palloc'd, anyway).
                                970                 :                :      */
                                971                 :          11862 :     nfields = tupdesc->natts;
                                972   [ +  -  -  + ]:          11862 :     if (erh->dvalues == NULL || erh->nfields != nfields)
 2252 tgl@sss.pgh.pa.us         973                 :UBC           0 :     {
                                974                 :                :         char       *chunk;
                                975                 :                : 
                                976                 :                :         /*
                                977                 :                :          * To save a palloc cycle, we allocate both the Datum and isnull
                                978                 :                :          * arrays in one palloc chunk.
                                979                 :                :          */
                                980                 :              0 :         chunk = MemoryContextAlloc(erh->hdr.eoh_context,
                                981                 :                :                                    nfields * (sizeof(Datum) + sizeof(bool)));
                                982                 :              0 :         dvalues = (Datum *) chunk;
                                983                 :              0 :         dnulls = (bool *) (chunk + nfields * sizeof(Datum));
                                984                 :              0 :         erh->dvalues = dvalues;
                                985                 :              0 :         erh->dnulls = dnulls;
                                986                 :              0 :         erh->nfields = nfields;
                                987                 :                :     }
                                988                 :                :     else
                                989                 :                :     {
 2252 tgl@sss.pgh.pa.us         990                 :CBC       11862 :         dvalues = erh->dvalues;
                                991                 :          11862 :         dnulls = erh->dnulls;
                                992                 :                :     }
                                993                 :                : 
                                994         [ +  + ]:          11862 :     if (erh->flags & ER_FLAG_FVALUE_VALID)
                                995                 :                :     {
                                996                 :                :         /* Deconstruct tuple */
                                997                 :          11010 :         heap_deform_tuple(erh->fvalue, tupdesc, dvalues, dnulls);
                                998                 :                :     }
                                999                 :                :     else
                               1000                 :                :     {
                               1001                 :                :         /* If record was empty, instantiate it as a row of nulls */
                               1002                 :            852 :         memset(dvalues, 0, nfields * sizeof(Datum));
                               1003                 :            852 :         memset(dnulls, true, nfields * sizeof(bool));
                               1004                 :                :     }
                               1005                 :                : 
                               1006                 :                :     /* Mark the dvalues as valid */
                               1007                 :          11862 :     erh->flags |= ER_FLAG_DVALUES_VALID;
                               1008                 :                : }
                               1009                 :                : 
                               1010                 :                : /*
                               1011                 :                :  * Look up a record field by name
                               1012                 :                :  *
                               1013                 :                :  * If there is a field named "fieldname", fill in the contents of finfo
                               1014                 :                :  * and return "true".  Else return "false" without changing *finfo.
                               1015                 :                :  */
                               1016                 :                : bool
                               1017                 :           3093 : expanded_record_lookup_field(ExpandedRecordHeader *erh, const char *fieldname,
                               1018                 :                :                              ExpandedRecordFieldInfo *finfo)
                               1019                 :                : {
                               1020                 :                :     TupleDesc   tupdesc;
                               1021                 :                :     int         fno;
                               1022                 :                :     Form_pg_attribute attr;
                               1023                 :                :     const FormData_pg_attribute *sysattr;
                               1024                 :                : 
                               1025                 :           3093 :     tupdesc = expanded_record_get_tupdesc(erh);
                               1026                 :                : 
                               1027                 :                :     /* First, check user-defined attributes */
                               1028         [ +  + ]:          11092 :     for (fno = 0; fno < tupdesc->natts; fno++)
                               1029                 :                :     {
                               1030                 :          11078 :         attr = TupleDescAttr(tupdesc, fno);
                               1031         [ +  + ]:          11078 :         if (namestrcmp(&attr->attname, fieldname) == 0 &&
                               1032         [ +  - ]:           3079 :             !attr->attisdropped)
                               1033                 :                :         {
                               1034                 :           3079 :             finfo->fnumber = attr->attnum;
                               1035                 :           3079 :             finfo->ftypeid = attr->atttypid;
                               1036                 :           3079 :             finfo->ftypmod = attr->atttypmod;
                               1037                 :           3079 :             finfo->fcollation = attr->attcollation;
                               1038                 :           3079 :             return true;
                               1039                 :                :         }
                               1040                 :                :     }
                               1041                 :                : 
                               1042                 :                :     /* How about system attributes? */
 1972 andres@anarazel.de       1043                 :             14 :     sysattr = SystemAttributeByName(fieldname);
 2007                          1044         [ +  + ]:             14 :     if (sysattr != NULL)
                               1045                 :                :     {
                               1046                 :              2 :         finfo->fnumber = sysattr->attnum;
                               1047                 :              2 :         finfo->ftypeid = sysattr->atttypid;
                               1048                 :              2 :         finfo->ftypmod = sysattr->atttypmod;
                               1049                 :              2 :         finfo->fcollation = sysattr->attcollation;
 2252 tgl@sss.pgh.pa.us        1050                 :              2 :         return true;
                               1051                 :                :     }
                               1052                 :                : 
                               1053                 :             12 :     return false;
                               1054                 :                : }
                               1055                 :                : 
                               1056                 :                : /*
                               1057                 :                :  * Fetch value of record field
                               1058                 :                :  *
                               1059                 :                :  * expanded_record_get_field is the frontend for this; it handles the
                               1060                 :                :  * easy inline-able cases.
                               1061                 :                :  */
                               1062                 :                : Datum
                               1063                 :          10880 : expanded_record_fetch_field(ExpandedRecordHeader *erh, int fnumber,
                               1064                 :                :                             bool *isnull)
                               1065                 :                : {
                               1066         [ +  + ]:          10880 :     if (fnumber > 0)
                               1067                 :                :     {
                               1068                 :                :         /* Empty record has null fields */
                               1069         [ +  + ]:          10876 :         if (ExpandedRecordIsEmpty(erh))
                               1070                 :                :         {
                               1071                 :             27 :             *isnull = true;
                               1072                 :             27 :             return (Datum) 0;
                               1073                 :                :         }
                               1074                 :                :         /* Make sure we have deconstructed form */
                               1075                 :          10849 :         deconstruct_expanded_record(erh);
                               1076                 :                :         /* Out-of-range field number reads as null */
                               1077         [ -  + ]:          10849 :         if (unlikely(fnumber > erh->nfields))
                               1078                 :                :         {
 2252 tgl@sss.pgh.pa.us        1079                 :UBC           0 :             *isnull = true;
                               1080                 :              0 :             return (Datum) 0;
                               1081                 :                :         }
 2252 tgl@sss.pgh.pa.us        1082                 :CBC       10849 :         *isnull = erh->dnulls[fnumber - 1];
                               1083                 :          10849 :         return erh->dvalues[fnumber - 1];
                               1084                 :                :     }
                               1085                 :                :     else
                               1086                 :                :     {
                               1087                 :                :         /* System columns read as null if we haven't got flat tuple */
                               1088         [ -  + ]:              4 :         if (erh->fvalue == NULL)
                               1089                 :                :         {
 2252 tgl@sss.pgh.pa.us        1090                 :UBC           0 :             *isnull = true;
                               1091                 :              0 :             return (Datum) 0;
                               1092                 :                :         }
                               1093                 :                :         /* heap_getsysattr doesn't actually use tupdesc, so just pass null */
 2252 tgl@sss.pgh.pa.us        1094                 :CBC           4 :         return heap_getsysattr(erh->fvalue, fnumber, NULL, isnull);
                               1095                 :                :     }
                               1096                 :                : }
                               1097                 :                : 
                               1098                 :                : /*
                               1099                 :                :  * Set value of record field
                               1100                 :                :  *
                               1101                 :                :  * If the expanded record is of domain type, the assignment will be rejected
                               1102                 :                :  * (without changing the record's state) if the domain's constraints would
                               1103                 :                :  * be violated.
                               1104                 :                :  *
                               1105                 :                :  * If expand_external is true and newValue is an out-of-line value, we'll
                               1106                 :                :  * forcibly detoast it so that the record does not depend on external storage.
                               1107                 :                :  *
                               1108                 :                :  * Internal callers can pass check_constraints = false to skip application
                               1109                 :                :  * of domain constraints.  External callers should never do that.
                               1110                 :                :  */
                               1111                 :                : void
                               1112                 :           1162 : expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
                               1113                 :                :                                    Datum newValue, bool isnull,
                               1114                 :                :                                    bool expand_external,
                               1115                 :                :                                    bool check_constraints)
                               1116                 :                : {
                               1117                 :                :     TupleDesc   tupdesc;
                               1118                 :                :     Form_pg_attribute attr;
                               1119                 :                :     Datum      *dvalues;
                               1120                 :                :     bool       *dnulls;
                               1121                 :                :     char       *oldValue;
                               1122                 :                : 
                               1123                 :                :     /*
                               1124                 :                :      * Shouldn't ever be trying to assign new data to a dummy header, except
                               1125                 :                :      * in the case of an internal call for field inlining.
                               1126                 :                :      */
                               1127   [ -  +  -  - ]:           1162 :     Assert(!(erh->flags & ER_FLAG_IS_DUMMY) || !check_constraints);
                               1128                 :                : 
                               1129                 :                :     /* Before performing the assignment, see if result will satisfy domain */
                               1130   [ +  +  +  - ]:           1162 :     if ((erh->flags & ER_FLAG_IS_DOMAIN) && check_constraints)
                               1131                 :             12 :         check_domain_for_new_field(erh, fnumber, newValue, isnull);
                               1132                 :                : 
                               1133                 :                :     /* If we haven't yet deconstructed the tuple, do that */
                               1134         [ +  + ]:           1159 :     if (!(erh->flags & ER_FLAG_DVALUES_VALID))
                               1135                 :            190 :         deconstruct_expanded_record(erh);
                               1136                 :                : 
                               1137                 :                :     /* Tuple descriptor must be valid by now */
                               1138                 :           1159 :     tupdesc = erh->er_tupdesc;
                               1139         [ -  + ]:           1159 :     Assert(erh->nfields == tupdesc->natts);
                               1140                 :                : 
                               1141                 :                :     /* Caller error if fnumber is system column or nonexistent column */
                               1142   [ +  -  -  +  :           1159 :     if (unlikely(fnumber <= 0 || fnumber > erh->nfields))
                                              -  + ]
 2252 tgl@sss.pgh.pa.us        1143         [ #  # ]:UBC           0 :         elog(ERROR, "cannot assign to field %d of expanded record", fnumber);
                               1144                 :                : 
                               1145                 :                :     /*
                               1146                 :                :      * Copy new field value into record's context, and deal with detoasting,
                               1147                 :                :      * if needed.
                               1148                 :                :      */
 2252 tgl@sss.pgh.pa.us        1149                 :CBC        1159 :     attr = TupleDescAttr(tupdesc, fnumber - 1);
                               1150   [ +  +  +  + ]:           1159 :     if (!isnull && !attr->attbyval)
                               1151                 :                :     {
                               1152                 :                :         MemoryContext oldcxt;
                               1153                 :                : 
                               1154                 :                :         /* If requested, detoast any external value */
 2160                          1155         [ +  + ]:            745 :         if (expand_external)
                               1156                 :                :         {
                               1157         [ +  - ]:             51 :             if (attr->attlen == -1 &&
                               1158         [ +  + ]:             51 :                 VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
                               1159                 :                :             {
                               1160                 :                :                 /* Detoasting should be done in short-lived context. */
                               1161                 :             31 :                 oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
 1654 rhaas@postgresql.org     1162                 :             31 :                 newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
 2160 tgl@sss.pgh.pa.us        1163                 :             31 :                 MemoryContextSwitchTo(oldcxt);
                               1164                 :                :             }
                               1165                 :                :             else
                               1166                 :             20 :                 expand_external = false;    /* need not clean up below */
                               1167                 :                :         }
                               1168                 :                : 
                               1169                 :                :         /* Copy value into record's context */
 2252                          1170                 :            745 :         oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
                               1171                 :            745 :         newValue = datumCopy(newValue, false, attr->attlen);
                               1172                 :            745 :         MemoryContextSwitchTo(oldcxt);
                               1173                 :                : 
                               1174                 :                :         /* We can now flush anything that detoasting might have leaked */
 2160                          1175         [ +  + ]:            745 :         if (expand_external)
                               1176                 :             31 :             MemoryContextReset(erh->er_short_term_cxt);
                               1177                 :                : 
                               1178                 :                :         /* Remember that we have field(s) that may need to be pfree'd */
 2252                          1179                 :            745 :         erh->flags |= ER_FLAG_DVALUES_ALLOCED;
                               1180                 :                : 
                               1181                 :                :         /*
                               1182                 :                :          * While we're here, note whether it's an external toasted value,
                               1183                 :                :          * because that could mean we need to inline it later.  (Think not to
                               1184                 :                :          * merge this into the previous expand_external logic: datumCopy could
                               1185                 :                :          * by itself have made the value non-external.)
                               1186                 :                :          */
                               1187         [ +  - ]:            745 :         if (attr->attlen == -1 &&
                               1188         [ -  + ]:            745 :             VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
 2252 tgl@sss.pgh.pa.us        1189                 :UBC           0 :             erh->flags |= ER_FLAG_HAVE_EXTERNAL;
                               1190                 :                :     }
                               1191                 :                : 
                               1192                 :                :     /*
                               1193                 :                :      * We're ready to make irreversible changes.
                               1194                 :                :      */
 2252 tgl@sss.pgh.pa.us        1195                 :CBC        1159 :     dvalues = erh->dvalues;
                               1196                 :           1159 :     dnulls = erh->dnulls;
                               1197                 :                : 
                               1198                 :                :     /* Flattened value will no longer represent record accurately */
                               1199                 :           1159 :     erh->flags &= ~ER_FLAG_FVALUE_VALID;
                               1200                 :                :     /* And we don't know the flattened size either */
                               1201                 :           1159 :     erh->flat_size = 0;
                               1202                 :                : 
                               1203                 :                :     /* Grab old field value for pfree'ing, if needed. */
                               1204   [ +  +  +  + ]:           1159 :     if (!attr->attbyval && !dnulls[fnumber - 1])
                               1205                 :            697 :         oldValue = (char *) DatumGetPointer(dvalues[fnumber - 1]);
                               1206                 :                :     else
                               1207                 :            462 :         oldValue = NULL;
                               1208                 :                : 
                               1209                 :                :     /* And finally we can insert the new field. */
                               1210                 :           1159 :     dvalues[fnumber - 1] = newValue;
                               1211                 :           1159 :     dnulls[fnumber - 1] = isnull;
                               1212                 :                : 
                               1213                 :                :     /*
                               1214                 :                :      * Free old field if needed; this keeps repeated field replacements from
                               1215                 :                :      * bloating the record's storage.  If the pfree somehow fails, it won't
                               1216                 :                :      * corrupt the record.
                               1217                 :                :      *
                               1218                 :                :      * If we're updating a dummy header, we can't risk pfree'ing the old
                               1219                 :                :      * value, because most likely the expanded record's main header still has
                               1220                 :                :      * a pointer to it.  This won't result in any sustained memory leak, since
                               1221                 :                :      * whatever we just allocated here is in the short-lived domain check
                               1222                 :                :      * context.
                               1223                 :                :      */
                               1224   [ +  +  +  - ]:           1159 :     if (oldValue && !(erh->flags & ER_FLAG_IS_DUMMY))
                               1225                 :                :     {
                               1226                 :                :         /* Don't try to pfree a part of the original flat record */
                               1227   [ +  -  +  + ]:            697 :         if (oldValue < erh->fstartptr || oldValue >= erh->fendptr)
                               1228                 :             14 :             pfree(oldValue);
                               1229                 :                :     }
                               1230                 :           1159 : }
                               1231                 :                : 
                               1232                 :                : /*
                               1233                 :                :  * Set all record field(s)
                               1234                 :                :  *
                               1235                 :                :  * Caller must ensure that the provided datums are of the right types
                               1236                 :                :  * to match the record's previously assigned rowtype.
                               1237                 :                :  *
                               1238                 :                :  * If expand_external is true, we'll forcibly detoast out-of-line field values
                               1239                 :                :  * so that the record does not depend on external storage.
                               1240                 :                :  *
                               1241                 :                :  * Unlike repeated application of expanded_record_set_field(), this does not
                               1242                 :                :  * guarantee to leave the expanded record in a non-corrupt state in event
                               1243                 :                :  * of an error.  Typically it would only be used for initializing a new
                               1244                 :                :  * expanded record.  Also, because we expect this to be applied at most once
                               1245                 :                :  * in the lifespan of an expanded record, we do not worry about any cruft
                               1246                 :                :  * that detoasting might leak.
                               1247                 :                :  */
                               1248                 :                : void
                               1249                 :             51 : expanded_record_set_fields(ExpandedRecordHeader *erh,
                               1250                 :                :                            const Datum *newValues, const bool *isnulls,
                               1251                 :                :                            bool expand_external)
                               1252                 :                : {
                               1253                 :                :     TupleDesc   tupdesc;
                               1254                 :                :     Datum      *dvalues;
                               1255                 :                :     bool       *dnulls;
                               1256                 :                :     int         fnumber;
                               1257                 :                :     MemoryContext oldcxt;
                               1258                 :                : 
                               1259                 :                :     /* Shouldn't ever be trying to assign new data to a dummy header */
                               1260         [ -  + ]:             51 :     Assert(!(erh->flags & ER_FLAG_IS_DUMMY));
                               1261                 :                : 
                               1262                 :                :     /* If we haven't yet deconstructed the tuple, do that */
                               1263         [ +  - ]:             51 :     if (!(erh->flags & ER_FLAG_DVALUES_VALID))
                               1264                 :             51 :         deconstruct_expanded_record(erh);
                               1265                 :                : 
                               1266                 :                :     /* Tuple descriptor must be valid by now */
                               1267                 :             51 :     tupdesc = erh->er_tupdesc;
                               1268         [ -  + ]:             51 :     Assert(erh->nfields == tupdesc->natts);
                               1269                 :                : 
                               1270                 :                :     /* Flattened value will no longer represent record accurately */
                               1271                 :             51 :     erh->flags &= ~ER_FLAG_FVALUE_VALID;
                               1272                 :                :     /* And we don't know the flattened size either */
                               1273                 :             51 :     erh->flat_size = 0;
                               1274                 :                : 
                               1275                 :             51 :     oldcxt = MemoryContextSwitchTo(erh->hdr.eoh_context);
                               1276                 :                : 
                               1277                 :             51 :     dvalues = erh->dvalues;
                               1278                 :             51 :     dnulls = erh->dnulls;
                               1279                 :                : 
                               1280         [ +  + ]:            157 :     for (fnumber = 0; fnumber < erh->nfields; fnumber++)
                               1281                 :                :     {
                               1282                 :            106 :         Form_pg_attribute attr = TupleDescAttr(tupdesc, fnumber);
                               1283                 :                :         Datum       newValue;
                               1284                 :                :         bool        isnull;
                               1285                 :                : 
                               1286                 :                :         /* Ignore dropped columns */
                               1287         [ +  + ]:            106 :         if (attr->attisdropped)
                               1288                 :              4 :             continue;
                               1289                 :                : 
                               1290                 :            102 :         newValue = newValues[fnumber];
                               1291                 :            102 :         isnull = isnulls[fnumber];
                               1292                 :                : 
                               1293         [ +  + ]:            102 :         if (!attr->attbyval)
                               1294                 :                :         {
                               1295                 :                :             /*
                               1296                 :                :              * Copy new field value into record's context, and deal with
                               1297                 :                :              * detoasting, if needed.
                               1298                 :                :              */
                               1299         [ +  - ]:             13 :             if (!isnull)
                               1300                 :                :             {
                               1301                 :                :                 /* Is it an external toasted value? */
 2160                          1302         [ +  - ]:             13 :                 if (attr->attlen == -1 &&
                               1303         [ +  + ]:             13 :                     VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
                               1304                 :                :                 {
                               1305         [ +  - ]:              1 :                     if (expand_external)
                               1306                 :                :                     {
                               1307                 :                :                         /* Detoast as requested while copying the value */
 1654 rhaas@postgresql.org     1308                 :              1 :                         newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
                               1309                 :                :                     }
                               1310                 :                :                     else
                               1311                 :                :                     {
                               1312                 :                :                         /* Just copy the value */
 2160 tgl@sss.pgh.pa.us        1313                 :UBC           0 :                         newValue = datumCopy(newValue, false, -1);
                               1314                 :                :                         /* If it's still external, remember that */
                               1315         [ #  # ]:              0 :                         if (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
                               1316                 :              0 :                             erh->flags |= ER_FLAG_HAVE_EXTERNAL;
                               1317                 :                :                     }
                               1318                 :                :                 }
                               1319                 :                :                 else
                               1320                 :                :                 {
                               1321                 :                :                     /* Not an external value, just copy it */
 2160 tgl@sss.pgh.pa.us        1322                 :CBC          12 :                     newValue = datumCopy(newValue, false, attr->attlen);
                               1323                 :                :                 }
                               1324                 :                : 
                               1325                 :                :                 /* Remember that we have field(s) that need to be pfree'd */
 2252                          1326                 :             13 :                 erh->flags |= ER_FLAG_DVALUES_ALLOCED;
                               1327                 :                :             }
                               1328                 :                : 
                               1329                 :                :             /*
                               1330                 :                :              * Free old field value, if any (not likely, since really we ought
                               1331                 :                :              * to be inserting into an empty record).
                               1332                 :                :              */
                               1333         [ -  + ]:             13 :             if (unlikely(!dnulls[fnumber]))
                               1334                 :                :             {
                               1335                 :                :                 char       *oldValue;
                               1336                 :                : 
 2252 tgl@sss.pgh.pa.us        1337                 :UBC           0 :                 oldValue = (char *) DatumGetPointer(dvalues[fnumber]);
                               1338                 :                :                 /* Don't try to pfree a part of the original flat record */
                               1339   [ #  #  #  # ]:              0 :                 if (oldValue < erh->fstartptr || oldValue >= erh->fendptr)
                               1340                 :              0 :                     pfree(oldValue);
                               1341                 :                :             }
                               1342                 :                :         }
                               1343                 :                : 
                               1344                 :                :         /* And finally we can insert the new field. */
 2252 tgl@sss.pgh.pa.us        1345                 :CBC         102 :         dvalues[fnumber] = newValue;
                               1346                 :            102 :         dnulls[fnumber] = isnull;
                               1347                 :                :     }
                               1348                 :                : 
                               1349                 :                :     /*
                               1350                 :                :      * Because we don't guarantee atomicity of set_fields(), we can just leave
                               1351                 :                :      * checking of domain constraints to occur as the final step; if it throws
                               1352                 :                :      * an error, too bad.
                               1353                 :                :      */
                               1354         [ +  + ]:             51 :     if (erh->flags & ER_FLAG_IS_DOMAIN)
                               1355                 :                :     {
                               1356                 :                :         /* We run domain_check in a short-lived context to limit cruft */
 2160                          1357                 :              9 :         MemoryContextSwitchTo(get_short_term_cxt(erh));
                               1358                 :                : 
 2252                          1359                 :              9 :         domain_check(ExpandedRecordGetRODatum(erh), false,
                               1360                 :                :                      erh->er_decltypeid,
                               1361                 :                :                      &erh->er_domaininfo,
                               1362                 :                :                      erh->hdr.eoh_context);
                               1363                 :                :     }
                               1364                 :                : 
                               1365                 :             47 :     MemoryContextSwitchTo(oldcxt);
                               1366                 :             47 : }
                               1367                 :                : 
                               1368                 :                : /*
                               1369                 :                :  * Construct (or reset) working memory context for short-term operations.
                               1370                 :                :  *
                               1371                 :                :  * This context is used for domain check evaluation and for detoasting.
                               1372                 :                :  *
                               1373                 :                :  * If we don't have a short-lived memory context, make one; if we have one,
                               1374                 :                :  * reset it to get rid of any leftover cruft.  (It is a tad annoying to need a
                               1375                 :                :  * whole context for this, since it will often go unused --- but it's hard to
                               1376                 :                :  * avoid memory leaks otherwise.  We can make the context small, at least.)
                               1377                 :                :  */
                               1378                 :                : static MemoryContext
 2160                          1379                 :             92 : get_short_term_cxt(ExpandedRecordHeader *erh)
                               1380                 :                : {
                               1381         [ +  + ]:             92 :     if (erh->er_short_term_cxt == NULL)
                               1382                 :             68 :         erh->er_short_term_cxt =
 2252                          1383                 :             68 :             AllocSetContextCreate(erh->hdr.eoh_context,
                               1384                 :                :                                   "expanded record short-term context",
                               1385                 :                :                                   ALLOCSET_SMALL_SIZES);
                               1386                 :                :     else
 2160                          1387                 :             24 :         MemoryContextReset(erh->er_short_term_cxt);
                               1388                 :             92 :     return erh->er_short_term_cxt;
                               1389                 :                : }
                               1390                 :                : 
                               1391                 :                : /*
                               1392                 :                :  * Construct "dummy header" for checking domain constraints.
                               1393                 :                :  *
                               1394                 :                :  * Since we don't want to modify the state of the expanded record until
                               1395                 :                :  * we've validated the constraints, our approach is to set up a dummy
                               1396                 :                :  * record header containing the new field value(s) and then pass that to
                               1397                 :                :  * domain_check.  We retain the dummy header as part of the expanded
                               1398                 :                :  * record's state to save palloc cycles, but reinitialize (most of)
                               1399                 :                :  * its contents on each use.
                               1400                 :                :  */
                               1401                 :                : static void
 2252                          1402                 :             28 : build_dummy_expanded_header(ExpandedRecordHeader *main_erh)
                               1403                 :                : {
                               1404                 :                :     ExpandedRecordHeader *erh;
                               1405                 :             28 :     TupleDesc   tupdesc = expanded_record_get_tupdesc(main_erh);
                               1406                 :                : 
                               1407                 :                :     /* Ensure we have a short-lived context */
 2160                          1408                 :             28 :     (void) get_short_term_cxt(main_erh);
                               1409                 :                : 
                               1410                 :                :     /*
                               1411                 :                :      * Allocate dummy header on first time through, or in the unlikely event
                               1412                 :                :      * that the number of fields changes (in which case we just leak the old
                               1413                 :                :      * one).  Include space for its field values in the request.
                               1414                 :                :      */
 2252                          1415                 :             28 :     erh = main_erh->er_dummy_header;
                               1416   [ +  +  -  + ]:             28 :     if (erh == NULL || erh->nfields != tupdesc->natts)
                               1417                 :                :     {
                               1418                 :                :         char       *chunk;
                               1419                 :                : 
                               1420                 :                :         erh = (ExpandedRecordHeader *)
                               1421                 :             18 :             MemoryContextAlloc(main_erh->hdr.eoh_context,
                               1422                 :                :                                MAXALIGN(sizeof(ExpandedRecordHeader))
                               1423                 :             18 :                                + tupdesc->natts * (sizeof(Datum) + sizeof(bool)));
                               1424                 :                : 
                               1425                 :                :         /* Ensure all header fields are initialized to 0/null */
                               1426                 :             18 :         memset(erh, 0, sizeof(ExpandedRecordHeader));
                               1427                 :                : 
                               1428                 :                :         /*
                               1429                 :                :          * We set up the dummy header with an indication that its memory
                               1430                 :                :          * context is the short-lived context.  This is so that, if any
                               1431                 :                :          * detoasting of out-of-line values happens due to an attempt to
                               1432                 :                :          * extract a composite datum from the dummy header, the detoasted
                               1433                 :                :          * stuff will end up in the short-lived context and not cause a leak.
                               1434                 :                :          * This is cheating a bit on the expanded-object protocol; but since
                               1435                 :                :          * we never pass a R/W pointer to the dummy object to any other code,
                               1436                 :                :          * nothing else is authorized to delete or transfer ownership of the
                               1437                 :                :          * object's context, so it should be safe enough.
                               1438                 :                :          */
 2160                          1439                 :             18 :         EOH_init_header(&erh->hdr, &ER_methods, main_erh->er_short_term_cxt);
 2252                          1440                 :             18 :         erh->er_magic = ER_MAGIC;
                               1441                 :                : 
                               1442                 :                :         /* Set up dvalues/dnulls, with no valid contents as yet */
                               1443                 :             18 :         chunk = (char *) erh + MAXALIGN(sizeof(ExpandedRecordHeader));
                               1444                 :             18 :         erh->dvalues = (Datum *) chunk;
                               1445                 :             18 :         erh->dnulls = (bool *) (chunk + tupdesc->natts * sizeof(Datum));
                               1446                 :             18 :         erh->nfields = tupdesc->natts;
                               1447                 :                : 
                               1448                 :                :         /*
                               1449                 :                :          * The fields we just set are assumed to remain constant through
                               1450                 :                :          * multiple uses of the dummy header to check domain constraints.  All
                               1451                 :                :          * other dummy header fields should be explicitly reset below, to
                               1452                 :                :          * ensure there's not accidental effects of one check on the next one.
                               1453                 :                :          */
                               1454                 :                : 
                               1455                 :             18 :         main_erh->er_dummy_header = erh;
                               1456                 :                :     }
                               1457                 :                : 
                               1458                 :                :     /*
                               1459                 :                :      * If anything inquires about the dummy header's declared type, it should
                               1460                 :                :      * report the composite base type, not the domain type (since the VALUE in
                               1461                 :                :      * a domain check constraint is of the base type not the domain).  Hence
                               1462                 :                :      * we do not transfer over the IS_DOMAIN flag, nor indeed any of the main
                               1463                 :                :      * header's flags, since the dummy header is empty of data at this point.
                               1464                 :                :      * But don't forget to mark header as dummy.
                               1465                 :                :      */
                               1466                 :             28 :     erh->flags = ER_FLAG_IS_DUMMY;
                               1467                 :                : 
                               1468                 :                :     /* Copy composite-type identification info */
                               1469                 :             28 :     erh->er_decltypeid = erh->er_typeid = main_erh->er_typeid;
                               1470                 :             28 :     erh->er_typmod = main_erh->er_typmod;
                               1471                 :                : 
                               1472                 :                :     /* Dummy header does not need its own tupdesc refcount */
                               1473                 :             28 :     erh->er_tupdesc = tupdesc;
                               1474                 :             28 :     erh->er_tupdesc_id = main_erh->er_tupdesc_id;
                               1475                 :                : 
                               1476                 :                :     /*
                               1477                 :                :      * It's tempting to copy over whatever we know about the flat size, but
                               1478                 :                :      * there's no point since we're surely about to modify the dummy record's
                               1479                 :                :      * field(s).  Instead just clear anything left over from a previous usage
                               1480                 :                :      * cycle.
                               1481                 :                :      */
                               1482                 :             28 :     erh->flat_size = 0;
                               1483                 :                : 
                               1484                 :                :     /* Copy over fvalue if we have it, so that system columns are available */
                               1485                 :             28 :     erh->fvalue = main_erh->fvalue;
                               1486                 :             28 :     erh->fstartptr = main_erh->fstartptr;
                               1487                 :             28 :     erh->fendptr = main_erh->fendptr;
                               1488                 :             28 : }
                               1489                 :                : 
                               1490                 :                : /*
                               1491                 :                :  * Precheck domain constraints for a set_field operation
                               1492                 :                :  */
                               1493                 :                : static pg_noinline void
                               1494                 :             12 : check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber,
                               1495                 :                :                            Datum newValue, bool isnull)
                               1496                 :                : {
                               1497                 :                :     ExpandedRecordHeader *dummy_erh;
                               1498                 :                :     MemoryContext oldcxt;
                               1499                 :                : 
                               1500                 :                :     /* Construct dummy header to contain proposed new field set */
                               1501                 :             12 :     build_dummy_expanded_header(erh);
                               1502                 :             12 :     dummy_erh = erh->er_dummy_header;
                               1503                 :                : 
                               1504                 :                :     /*
                               1505                 :                :      * If record isn't empty, just deconstruct it (if needed) and copy over
                               1506                 :                :      * the existing field values.  If it is empty, just fill fields with nulls
                               1507                 :                :      * manually --- don't call deconstruct_expanded_record prematurely.
                               1508                 :                :      */
                               1509         [ +  + ]:             12 :     if (!ExpandedRecordIsEmpty(erh))
                               1510                 :                :     {
                               1511                 :              8 :         deconstruct_expanded_record(erh);
                               1512                 :              8 :         memcpy(dummy_erh->dvalues, erh->dvalues,
                               1513                 :              8 :                dummy_erh->nfields * sizeof(Datum));
                               1514                 :              8 :         memcpy(dummy_erh->dnulls, erh->dnulls,
                               1515                 :              8 :                dummy_erh->nfields * sizeof(bool));
                               1516                 :                :         /* There might be some external values in there... */
                               1517                 :              8 :         dummy_erh->flags |= erh->flags & ER_FLAG_HAVE_EXTERNAL;
                               1518                 :                :     }
                               1519                 :                :     else
                               1520                 :                :     {
                               1521                 :              4 :         memset(dummy_erh->dvalues, 0, dummy_erh->nfields * sizeof(Datum));
                               1522                 :              4 :         memset(dummy_erh->dnulls, true, dummy_erh->nfields * sizeof(bool));
                               1523                 :                :     }
                               1524                 :                : 
                               1525                 :                :     /* Either way, we now have valid dvalues */
                               1526                 :             12 :     dummy_erh->flags |= ER_FLAG_DVALUES_VALID;
                               1527                 :                : 
                               1528                 :                :     /* Caller error if fnumber is system column or nonexistent column */
                               1529   [ +  -  -  +  :             12 :     if (unlikely(fnumber <= 0 || fnumber > dummy_erh->nfields))
                                              -  + ]
 2252 tgl@sss.pgh.pa.us        1530         [ #  # ]:UBC           0 :         elog(ERROR, "cannot assign to field %d of expanded record", fnumber);
                               1531                 :                : 
                               1532                 :                :     /* Insert proposed new value into dummy field array */
 2252 tgl@sss.pgh.pa.us        1533                 :CBC          12 :     dummy_erh->dvalues[fnumber - 1] = newValue;
                               1534                 :             12 :     dummy_erh->dnulls[fnumber - 1] = isnull;
                               1535                 :                : 
                               1536                 :                :     /*
                               1537                 :                :      * The proposed new value might be external, in which case we'd better set
                               1538                 :                :      * the flag for that in dummy_erh.  (This matters in case something in the
                               1539                 :                :      * domain check expressions tries to extract a flat value from the dummy
                               1540                 :                :      * header.)
                               1541                 :                :      */
                               1542         [ +  + ]:             12 :     if (!isnull)
                               1543                 :                :     {
                               1544                 :             11 :         Form_pg_attribute attr = TupleDescAttr(erh->er_tupdesc, fnumber - 1);
                               1545                 :                : 
                               1546   [ +  +  +  - ]:             11 :         if (!attr->attbyval && attr->attlen == -1 &&
                               1547         [ -  + ]:              6 :             VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
 2252 tgl@sss.pgh.pa.us        1548                 :UBC           0 :             dummy_erh->flags |= ER_FLAG_HAVE_EXTERNAL;
                               1549                 :                :     }
                               1550                 :                : 
                               1551                 :                :     /*
                               1552                 :                :      * We call domain_check in the short-lived context, so that any cruft
                               1553                 :                :      * leaked by expression evaluation can be reclaimed.
                               1554                 :                :      */
 2160 tgl@sss.pgh.pa.us        1555                 :CBC          12 :     oldcxt = MemoryContextSwitchTo(erh->er_short_term_cxt);
                               1556                 :                : 
                               1557                 :                :     /*
                               1558                 :                :      * And now we can apply the check.  Note we use main header's domain cache
                               1559                 :                :      * space, so that caching carries across repeated uses.
                               1560                 :                :      */
 2252                          1561                 :             12 :     domain_check(ExpandedRecordGetRODatum(dummy_erh), false,
                               1562                 :                :                  erh->er_decltypeid,
                               1563                 :                :                  &erh->er_domaininfo,
                               1564                 :                :                  erh->hdr.eoh_context);
                               1565                 :                : 
                               1566                 :              9 :     MemoryContextSwitchTo(oldcxt);
                               1567                 :                : 
                               1568                 :                :     /* We might as well clean up cruft immediately. */
 2160                          1569                 :              9 :     MemoryContextReset(erh->er_short_term_cxt);
 2252                          1570                 :              9 : }
                               1571                 :                : 
                               1572                 :                : /*
                               1573                 :                :  * Precheck domain constraints for a set_tuple operation
                               1574                 :                :  */
                               1575                 :                : static pg_noinline void
                               1576                 :             32 : check_domain_for_new_tuple(ExpandedRecordHeader *erh, HeapTuple tuple)
                               1577                 :                : {
                               1578                 :                :     ExpandedRecordHeader *dummy_erh;
                               1579                 :                :     MemoryContext oldcxt;
                               1580                 :                : 
                               1581                 :                :     /* If we're being told to set record to empty, just see if NULL is OK */
                               1582         [ +  + ]:             32 :     if (tuple == NULL)
                               1583                 :                :     {
                               1584                 :                :         /* We run domain_check in a short-lived context to limit cruft */
 2160                          1585                 :             16 :         oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
                               1586                 :                : 
 2252                          1587                 :             16 :         domain_check((Datum) 0, true,
                               1588                 :                :                      erh->er_decltypeid,
                               1589                 :                :                      &erh->er_domaininfo,
                               1590                 :                :                      erh->hdr.eoh_context);
                               1591                 :                : 
                               1592                 :             12 :         MemoryContextSwitchTo(oldcxt);
                               1593                 :                : 
                               1594                 :                :         /* We might as well clean up cruft immediately. */
 2160                          1595                 :             12 :         MemoryContextReset(erh->er_short_term_cxt);
                               1596                 :                : 
 2252                          1597                 :             12 :         return;
                               1598                 :                :     }
                               1599                 :                : 
                               1600                 :                :     /* Construct dummy header to contain replacement tuple */
                               1601                 :             16 :     build_dummy_expanded_header(erh);
                               1602                 :             16 :     dummy_erh = erh->er_dummy_header;
                               1603                 :                : 
                               1604                 :                :     /* Insert tuple, but don't bother to deconstruct its fields for now */
                               1605                 :             16 :     dummy_erh->fvalue = tuple;
                               1606                 :             16 :     dummy_erh->fstartptr = (char *) tuple->t_data;
                               1607                 :             16 :     dummy_erh->fendptr = ((char *) tuple->t_data) + tuple->t_len;
                               1608                 :             16 :     dummy_erh->flags |= ER_FLAG_FVALUE_VALID;
                               1609                 :                : 
                               1610                 :                :     /* Remember if we have any out-of-line field values */
                               1611         [ +  + ]:             16 :     if (HeapTupleHasExternal(tuple))
                               1612                 :              2 :         dummy_erh->flags |= ER_FLAG_HAVE_EXTERNAL;
                               1613                 :                : 
                               1614                 :                :     /*
                               1615                 :                :      * We call domain_check in the short-lived context, so that any cruft
                               1616                 :                :      * leaked by expression evaluation can be reclaimed.
                               1617                 :                :      */
 2160                          1618                 :             16 :     oldcxt = MemoryContextSwitchTo(erh->er_short_term_cxt);
                               1619                 :                : 
                               1620                 :                :     /*
                               1621                 :                :      * And now we can apply the check.  Note we use main header's domain cache
                               1622                 :                :      * space, so that caching carries across repeated uses.
                               1623                 :                :      */
 2252                          1624                 :             16 :     domain_check(ExpandedRecordGetRODatum(dummy_erh), false,
                               1625                 :                :                  erh->er_decltypeid,
                               1626                 :                :                  &erh->er_domaininfo,
                               1627                 :                :                  erh->hdr.eoh_context);
                               1628                 :                : 
                               1629                 :             12 :     MemoryContextSwitchTo(oldcxt);
                               1630                 :                : 
                               1631                 :                :     /* We might as well clean up cruft immediately. */
 2160                          1632                 :             12 :     MemoryContextReset(erh->er_short_term_cxt);
                               1633                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622