LCOV - code coverage report
Current view: top level - src/backend/access/common - tupdesc.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18beta1 Lines: 398 437 91.1 %
Date: 2025-06-27 21:17:17 Functions: 23 25 92.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tupdesc.c
       4             :  *    POSTGRES tuple descriptor support code
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/common/tupdesc.c
      12             :  *
      13             :  * NOTES
      14             :  *    some of the executor utility code such as "ExecTypeFromTL" should be
      15             :  *    moved here.
      16             :  *
      17             :  *-------------------------------------------------------------------------
      18             :  */
      19             : 
      20             : #include "postgres.h"
      21             : 
      22             : #include "access/htup_details.h"
      23             : #include "access/toast_compression.h"
      24             : #include "access/tupdesc_details.h"
      25             : #include "catalog/catalog.h"
      26             : #include "catalog/pg_collation.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "common/hashfn.h"
      29             : #include "utils/builtins.h"
      30             : #include "utils/datum.h"
      31             : #include "utils/resowner.h"
      32             : #include "utils/syscache.h"
      33             : 
      34             : /* ResourceOwner callbacks to hold tupledesc references  */
      35             : static void ResOwnerReleaseTupleDesc(Datum res);
      36             : static char *ResOwnerPrintTupleDesc(Datum res);
      37             : 
      38             : static const ResourceOwnerDesc tupdesc_resowner_desc =
      39             : {
      40             :     .name = "tupdesc reference",
      41             :     .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
      42             :     .release_priority = RELEASE_PRIO_TUPDESC_REFS,
      43             :     .ReleaseResource = ResOwnerReleaseTupleDesc,
      44             :     .DebugPrint = ResOwnerPrintTupleDesc
      45             : };
      46             : 
      47             : /* Convenience wrappers over ResourceOwnerRemember/Forget */
      48             : static inline void
      49    32580972 : ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
      50             : {
      51    32580972 :     ResourceOwnerRemember(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_desc);
      52    32580972 : }
      53             : 
      54             : static inline void
      55    32565806 : ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
      56             : {
      57    32565806 :     ResourceOwnerForget(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_desc);
      58    32565806 : }
      59             : 
      60             : /*
      61             :  * populate_compact_attribute_internal
      62             :  *      Helper function for populate_compact_attribute()
      63             :  */
      64             : static inline void
      65    70368936 : populate_compact_attribute_internal(Form_pg_attribute src,
      66             :                                     CompactAttribute *dst)
      67             : {
      68    70368936 :     memset(dst, 0, sizeof(CompactAttribute));
      69             : 
      70    70368936 :     dst->attcacheoff = -1;
      71    70368936 :     dst->attlen = src->attlen;
      72             : 
      73    70368936 :     dst->attbyval = src->attbyval;
      74    70368936 :     dst->attispackable = (src->attstorage != TYPSTORAGE_PLAIN);
      75    70368936 :     dst->atthasmissing = src->atthasmissing;
      76    70368936 :     dst->attisdropped = src->attisdropped;
      77    70368936 :     dst->attgenerated = (src->attgenerated != '\0');
      78             : 
      79             :     /*
      80             :      * Assign nullability status for this column.  Assuming that a constraint
      81             :      * exists, at this point we don't know if a not-null constraint is valid,
      82             :      * so we assign UNKNOWN unless the table is a catalog, in which case we
      83             :      * know it's valid.
      84             :      */
      85   110204850 :     dst->attnullability = !src->attnotnull ? ATTNULLABLE_UNRESTRICTED :
      86    39835914 :         IsCatalogRelationOid(src->attrelid) ? ATTNULLABLE_VALID :
      87             :         ATTNULLABLE_UNKNOWN;
      88             : 
      89    70368936 :     switch (src->attalign)
      90             :     {
      91    45130926 :         case TYPALIGN_INT:
      92    45130926 :             dst->attalignby = ALIGNOF_INT;
      93    45130926 :             break;
      94    17850460 :         case TYPALIGN_CHAR:
      95    17850460 :             dst->attalignby = sizeof(char);
      96    17850460 :             break;
      97     4150282 :         case TYPALIGN_DOUBLE:
      98     4150282 :             dst->attalignby = ALIGNOF_DOUBLE;
      99     4150282 :             break;
     100     3237268 :         case TYPALIGN_SHORT:
     101     3237268 :             dst->attalignby = ALIGNOF_SHORT;
     102     3237268 :             break;
     103           0 :         default:
     104           0 :             dst->attalignby = 0;
     105           0 :             elog(ERROR, "invalid attalign value: %c", src->attalign);
     106             :             break;
     107             :     }
     108    70368936 : }
     109             : 
     110             : /*
     111             :  * populate_compact_attribute
     112             :  *      Fill in the corresponding CompactAttribute element from the
     113             :  *      Form_pg_attribute for the given attribute number.  This must be called
     114             :  *      whenever a change is made to a Form_pg_attribute in the TupleDesc.
     115             :  */
     116             : void
     117    70368936 : populate_compact_attribute(TupleDesc tupdesc, int attnum)
     118             : {
     119    70368936 :     Form_pg_attribute src = TupleDescAttr(tupdesc, attnum);
     120             :     CompactAttribute *dst;
     121             : 
     122             :     /*
     123             :      * Don't use TupleDescCompactAttr to prevent infinite recursion in assert
     124             :      * builds.
     125             :      */
     126    70368936 :     dst = &tupdesc->compact_attrs[attnum];
     127             : 
     128    70368936 :     populate_compact_attribute_internal(src, dst);
     129    70368936 : }
     130             : 
     131             : /*
     132             :  * verify_compact_attribute
     133             :  *      In Assert enabled builds, we verify that the CompactAttribute is
     134             :  *      populated correctly.  This helps find bugs in places such as ALTER
     135             :  *      TABLE where code makes changes to the FormData_pg_attribute but
     136             :  *      forgets to call populate_compact_attribute().
     137             :  *
     138             :  * This is used in TupleDescCompactAttr(), but declared here to allow access
     139             :  * to populate_compact_attribute_internal().
     140             :  */
     141             : void
     142           0 : verify_compact_attribute(TupleDesc tupdesc, int attnum)
     143             : {
     144             : #ifdef USE_ASSERT_CHECKING
     145             :     CompactAttribute cattr;
     146             :     Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum);
     147             :     CompactAttribute tmp;
     148             : 
     149             :     /*
     150             :      * Make a temp copy of the TupleDesc's CompactAttribute.  This may be a
     151             :      * shared TupleDesc and the attcacheoff might get changed by another
     152             :      * backend.
     153             :      */
     154             :     memcpy(&cattr, &tupdesc->compact_attrs[attnum], sizeof(CompactAttribute));
     155             : 
     156             :     /*
     157             :      * Populate the temporary CompactAttribute from the corresponding
     158             :      * Form_pg_attribute
     159             :      */
     160             :     populate_compact_attribute_internal(attr, &tmp);
     161             : 
     162             :     /*
     163             :      * Make the attcacheoff match since it's been reset to -1 by
     164             :      * populate_compact_attribute_internal.  Same with attnullability.
     165             :      */
     166             :     tmp.attcacheoff = cattr.attcacheoff;
     167             :     tmp.attnullability = cattr.attnullability;
     168             : 
     169             :     /* Check the freshly populated CompactAttribute matches the TupleDesc's */
     170             :     Assert(memcmp(&tmp, &cattr, sizeof(CompactAttribute)) == 0);
     171             : #endif
     172           0 : }
     173             : 
     174             : /*
     175             :  * CreateTemplateTupleDesc
     176             :  *      This function allocates an empty tuple descriptor structure.
     177             :  *
     178             :  * Tuple type ID information is initially set for an anonymous record type;
     179             :  * caller can overwrite this if needed.
     180             :  */
     181             : TupleDesc
     182    10232232 : CreateTemplateTupleDesc(int natts)
     183             : {
     184             :     TupleDesc   desc;
     185             : 
     186             :     /*
     187             :      * sanity checks
     188             :      */
     189             :     Assert(natts >= 0);
     190             : 
     191             :     /*
     192             :      * Allocate enough memory for the tuple descriptor, the CompactAttribute
     193             :      * array and also an array of FormData_pg_attribute.
     194             :      *
     195             :      * Note: the FormData_pg_attribute array stride is
     196             :      * sizeof(FormData_pg_attribute), since we declare the array elements as
     197             :      * FormData_pg_attribute for notational convenience.  However, we only
     198             :      * guarantee that the first ATTRIBUTE_FIXED_PART_SIZE bytes of each entry
     199             :      * are valid; most code that copies tupdesc entries around copies just
     200             :      * that much.  In principle that could be less due to trailing padding,
     201             :      * although with the current definition of pg_attribute there probably
     202             :      * isn't any padding.
     203             :      */
     204    10232232 :     desc = (TupleDesc) palloc(offsetof(struct TupleDescData, compact_attrs) +
     205    10232232 :                               natts * sizeof(CompactAttribute) +
     206             :                               natts * sizeof(FormData_pg_attribute));
     207             : 
     208             :     /*
     209             :      * Initialize other fields of the tupdesc.
     210             :      */
     211    10232232 :     desc->natts = natts;
     212    10232232 :     desc->constr = NULL;
     213    10232232 :     desc->tdtypeid = RECORDOID;
     214    10232232 :     desc->tdtypmod = -1;
     215    10232232 :     desc->tdrefcount = -1;       /* assume not reference-counted */
     216             : 
     217    10232232 :     return desc;
     218             : }
     219             : 
     220             : /*
     221             :  * CreateTupleDesc
     222             :  *      This function allocates a new TupleDesc by copying a given
     223             :  *      Form_pg_attribute array.
     224             :  *
     225             :  * Tuple type ID information is initially set for an anonymous record type;
     226             :  * caller can overwrite this if needed.
     227             :  */
     228             : TupleDesc
     229     1153622 : CreateTupleDesc(int natts, Form_pg_attribute *attrs)
     230             : {
     231             :     TupleDesc   desc;
     232             :     int         i;
     233             : 
     234     1153622 :     desc = CreateTemplateTupleDesc(natts);
     235             : 
     236    17638154 :     for (i = 0; i < natts; ++i)
     237             :     {
     238    16484532 :         memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
     239    16484532 :         populate_compact_attribute(desc, i);
     240             :     }
     241     1153622 :     return desc;
     242             : }
     243             : 
     244             : /*
     245             :  * CreateTupleDescCopy
     246             :  *      This function creates a new TupleDesc by copying from an existing
     247             :  *      TupleDesc.
     248             :  *
     249             :  * !!! Constraints and defaults are not copied !!!
     250             :  */
     251             : TupleDesc
     252      420710 : CreateTupleDescCopy(TupleDesc tupdesc)
     253             : {
     254             :     TupleDesc   desc;
     255             :     int         i;
     256             : 
     257      420710 :     desc = CreateTemplateTupleDesc(tupdesc->natts);
     258             : 
     259             :     /* Flat-copy the attribute array */
     260      420710 :     memcpy(TupleDescAttr(desc, 0),
     261      420710 :            TupleDescAttr(tupdesc, 0),
     262      420710 :            desc->natts * sizeof(FormData_pg_attribute));
     263             : 
     264             :     /*
     265             :      * Since we're not copying constraints and defaults, clear fields
     266             :      * associated with them.
     267             :      */
     268     2251582 :     for (i = 0; i < desc->natts; i++)
     269             :     {
     270     1830872 :         Form_pg_attribute att = TupleDescAttr(desc, i);
     271             : 
     272     1830872 :         att->attnotnull = false;
     273     1830872 :         att->atthasdef = false;
     274     1830872 :         att->atthasmissing = false;
     275     1830872 :         att->attidentity = '\0';
     276     1830872 :         att->attgenerated = '\0';
     277             : 
     278     1830872 :         populate_compact_attribute(desc, i);
     279             :     }
     280             : 
     281             :     /* We can copy the tuple type identification, too */
     282      420710 :     desc->tdtypeid = tupdesc->tdtypeid;
     283      420710 :     desc->tdtypmod = tupdesc->tdtypmod;
     284             : 
     285      420710 :     return desc;
     286             : }
     287             : 
     288             : /*
     289             :  * CreateTupleDescTruncatedCopy
     290             :  *      This function creates a new TupleDesc with only the first 'natts'
     291             :  *      attributes from an existing TupleDesc
     292             :  *
     293             :  * !!! Constraints and defaults are not copied !!!
     294             :  */
     295             : TupleDesc
     296       35908 : CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
     297             : {
     298             :     TupleDesc   desc;
     299             :     int         i;
     300             : 
     301             :     Assert(natts <= tupdesc->natts);
     302             : 
     303       35908 :     desc = CreateTemplateTupleDesc(natts);
     304             : 
     305             :     /* Flat-copy the attribute array */
     306       35908 :     memcpy(TupleDescAttr(desc, 0),
     307       35908 :            TupleDescAttr(tupdesc, 0),
     308       35908 :            desc->natts * sizeof(FormData_pg_attribute));
     309             : 
     310             :     /*
     311             :      * Since we're not copying constraints and defaults, clear fields
     312             :      * associated with them.
     313             :      */
     314       83006 :     for (i = 0; i < desc->natts; i++)
     315             :     {
     316       47098 :         Form_pg_attribute att = TupleDescAttr(desc, i);
     317             : 
     318       47098 :         att->attnotnull = false;
     319       47098 :         att->atthasdef = false;
     320       47098 :         att->atthasmissing = false;
     321       47098 :         att->attidentity = '\0';
     322       47098 :         att->attgenerated = '\0';
     323             : 
     324       47098 :         populate_compact_attribute(desc, i);
     325             :     }
     326             : 
     327             :     /* We can copy the tuple type identification, too */
     328       35908 :     desc->tdtypeid = tupdesc->tdtypeid;
     329       35908 :     desc->tdtypmod = tupdesc->tdtypmod;
     330             : 
     331       35908 :     return desc;
     332             : }
     333             : 
     334             : /*
     335             :  * CreateTupleDescCopyConstr
     336             :  *      This function creates a new TupleDesc by copying from an existing
     337             :  *      TupleDesc (including its constraints and defaults).
     338             :  */
     339             : TupleDesc
     340      864506 : CreateTupleDescCopyConstr(TupleDesc tupdesc)
     341             : {
     342             :     TupleDesc   desc;
     343      864506 :     TupleConstr *constr = tupdesc->constr;
     344             :     int         i;
     345             : 
     346      864506 :     desc = CreateTemplateTupleDesc(tupdesc->natts);
     347             : 
     348             :     /* Flat-copy the attribute array */
     349      864506 :     memcpy(TupleDescAttr(desc, 0),
     350      864506 :            TupleDescAttr(tupdesc, 0),
     351      864506 :            desc->natts * sizeof(FormData_pg_attribute));
     352             : 
     353    12590610 :     for (i = 0; i < desc->natts; i++)
     354             :     {
     355    11726104 :         populate_compact_attribute(desc, i);
     356             : 
     357    11726104 :         TupleDescCompactAttr(desc, i)->attnullability =
     358    23452208 :             TupleDescCompactAttr(tupdesc, i)->attnullability;
     359             :     }
     360             : 
     361             :     /* Copy the TupleConstr data structure, if any */
     362      864506 :     if (constr)
     363             :     {
     364      806050 :         TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
     365             : 
     366      806050 :         cpy->has_not_null = constr->has_not_null;
     367      806050 :         cpy->has_generated_stored = constr->has_generated_stored;
     368      806050 :         cpy->has_generated_virtual = constr->has_generated_virtual;
     369             : 
     370      806050 :         if ((cpy->num_defval = constr->num_defval) > 0)
     371             :         {
     372        3938 :             cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
     373        3938 :             memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
     374        9626 :             for (i = cpy->num_defval - 1; i >= 0; i--)
     375        5688 :                 cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
     376             :         }
     377             : 
     378      806050 :         if (constr->missing)
     379             :         {
     380         626 :             cpy->missing = (AttrMissing *) palloc(tupdesc->natts * sizeof(AttrMissing));
     381         626 :             memcpy(cpy->missing, constr->missing, tupdesc->natts * sizeof(AttrMissing));
     382        4862 :             for (i = tupdesc->natts - 1; i >= 0; i--)
     383             :             {
     384        4236 :                 if (constr->missing[i].am_present)
     385             :                 {
     386        1130 :                     CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
     387             : 
     388        1130 :                     cpy->missing[i].am_value = datumCopy(constr->missing[i].am_value,
     389        1130 :                                                          attr->attbyval,
     390        1130 :                                                          attr->attlen);
     391             :                 }
     392             :             }
     393             :         }
     394             : 
     395      806050 :         if ((cpy->num_check = constr->num_check) > 0)
     396             :         {
     397        2260 :             cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
     398        2260 :             memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
     399        6110 :             for (i = cpy->num_check - 1; i >= 0; i--)
     400             :             {
     401        3850 :                 cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
     402        3850 :                 cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
     403        3850 :                 cpy->check[i].ccenforced = constr->check[i].ccenforced;
     404        3850 :                 cpy->check[i].ccvalid = constr->check[i].ccvalid;
     405        3850 :                 cpy->check[i].ccnoinherit = constr->check[i].ccnoinherit;
     406             :             }
     407             :         }
     408             : 
     409      806050 :         desc->constr = cpy;
     410             :     }
     411             : 
     412             :     /* We can copy the tuple type identification, too */
     413      864506 :     desc->tdtypeid = tupdesc->tdtypeid;
     414      864506 :     desc->tdtypmod = tupdesc->tdtypmod;
     415             : 
     416      864506 :     return desc;
     417             : }
     418             : 
     419             : /*
     420             :  * TupleDescCopy
     421             :  *      Copy a tuple descriptor into caller-supplied memory.
     422             :  *      The memory may be shared memory mapped at any address, and must
     423             :  *      be sufficient to hold TupleDescSize(src) bytes.
     424             :  *
     425             :  * !!! Constraints and defaults are not copied !!!
     426             :  */
     427             : void
     428         174 : TupleDescCopy(TupleDesc dst, TupleDesc src)
     429             : {
     430             :     int         i;
     431             : 
     432             :     /* Flat-copy the header and attribute arrays */
     433         174 :     memcpy(dst, src, TupleDescSize(src));
     434             : 
     435             :     /*
     436             :      * Since we're not copying constraints and defaults, clear fields
     437             :      * associated with them.
     438             :      */
     439         704 :     for (i = 0; i < dst->natts; i++)
     440             :     {
     441         530 :         Form_pg_attribute att = TupleDescAttr(dst, i);
     442             : 
     443         530 :         att->attnotnull = false;
     444         530 :         att->atthasdef = false;
     445         530 :         att->atthasmissing = false;
     446         530 :         att->attidentity = '\0';
     447         530 :         att->attgenerated = '\0';
     448             : 
     449         530 :         populate_compact_attribute(dst, i);
     450             :     }
     451         174 :     dst->constr = NULL;
     452             : 
     453             :     /*
     454             :      * Also, assume the destination is not to be ref-counted.  (Copying the
     455             :      * source's refcount would be wrong in any case.)
     456             :      */
     457         174 :     dst->tdrefcount = -1;
     458         174 : }
     459             : 
     460             : /*
     461             :  * TupleDescCopyEntry
     462             :  *      This function copies a single attribute structure from one tuple
     463             :  *      descriptor to another.
     464             :  *
     465             :  * !!! Constraints and defaults are not copied !!!
     466             :  */
     467             : void
     468        4242 : TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
     469             :                    TupleDesc src, AttrNumber srcAttno)
     470             : {
     471        4242 :     Form_pg_attribute dstAtt = TupleDescAttr(dst, dstAttno - 1);
     472        4242 :     Form_pg_attribute srcAtt = TupleDescAttr(src, srcAttno - 1);
     473             : 
     474             :     /*
     475             :      * sanity checks
     476             :      */
     477             :     Assert(PointerIsValid(src));
     478             :     Assert(PointerIsValid(dst));
     479             :     Assert(srcAttno >= 1);
     480             :     Assert(srcAttno <= src->natts);
     481             :     Assert(dstAttno >= 1);
     482             :     Assert(dstAttno <= dst->natts);
     483             : 
     484        4242 :     memcpy(dstAtt, srcAtt, ATTRIBUTE_FIXED_PART_SIZE);
     485             : 
     486        4242 :     dstAtt->attnum = dstAttno;
     487             : 
     488             :     /* since we're not copying constraints or defaults, clear these */
     489        4242 :     dstAtt->attnotnull = false;
     490        4242 :     dstAtt->atthasdef = false;
     491        4242 :     dstAtt->atthasmissing = false;
     492        4242 :     dstAtt->attidentity = '\0';
     493        4242 :     dstAtt->attgenerated = '\0';
     494             : 
     495        4242 :     populate_compact_attribute(dst, dstAttno - 1);
     496        4242 : }
     497             : 
     498             : /*
     499             :  * Free a TupleDesc including all substructure
     500             :  */
     501             : void
     502     1424422 : FreeTupleDesc(TupleDesc tupdesc)
     503             : {
     504             :     int         i;
     505             : 
     506             :     /*
     507             :      * Possibly this should assert tdrefcount == 0, to disallow explicit
     508             :      * freeing of un-refcounted tupdescs?
     509             :      */
     510             :     Assert(tupdesc->tdrefcount <= 0);
     511             : 
     512     1424422 :     if (tupdesc->constr)
     513             :     {
     514      437370 :         if (tupdesc->constr->num_defval > 0)
     515             :         {
     516       31810 :             AttrDefault *attrdef = tupdesc->constr->defval;
     517             : 
     518       77318 :             for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
     519       45508 :                 pfree(attrdef[i].adbin);
     520       31810 :             pfree(attrdef);
     521             :         }
     522      437370 :         if (tupdesc->constr->missing)
     523             :         {
     524        3670 :             AttrMissing *attrmiss = tupdesc->constr->missing;
     525             : 
     526       26428 :             for (i = tupdesc->natts - 1; i >= 0; i--)
     527             :             {
     528       22758 :                 if (attrmiss[i].am_present
     529        7810 :                     && !TupleDescAttr(tupdesc, i)->attbyval)
     530        2926 :                     pfree(DatumGetPointer(attrmiss[i].am_value));
     531             :             }
     532        3670 :             pfree(attrmiss);
     533             :         }
     534      437370 :         if (tupdesc->constr->num_check > 0)
     535             :         {
     536       10936 :             ConstrCheck *check = tupdesc->constr->check;
     537             : 
     538       28252 :             for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
     539             :             {
     540       17316 :                 pfree(check[i].ccname);
     541       17316 :                 pfree(check[i].ccbin);
     542             :             }
     543       10936 :             pfree(check);
     544             :         }
     545      437370 :         pfree(tupdesc->constr);
     546             :     }
     547             : 
     548     1424422 :     pfree(tupdesc);
     549     1424422 : }
     550             : 
     551             : /*
     552             :  * Increment the reference count of a tupdesc, and log the reference in
     553             :  * CurrentResourceOwner.
     554             :  *
     555             :  * Do not apply this to tupdescs that are not being refcounted.  (Use the
     556             :  * macro PinTupleDesc for tupdescs of uncertain status.)
     557             :  */
     558             : void
     559    32580972 : IncrTupleDescRefCount(TupleDesc tupdesc)
     560             : {
     561             :     Assert(tupdesc->tdrefcount >= 0);
     562             : 
     563    32580972 :     ResourceOwnerEnlarge(CurrentResourceOwner);
     564    32580972 :     tupdesc->tdrefcount++;
     565    32580972 :     ResourceOwnerRememberTupleDesc(CurrentResourceOwner, tupdesc);
     566    32580972 : }
     567             : 
     568             : /*
     569             :  * Decrement the reference count of a tupdesc, remove the corresponding
     570             :  * reference from CurrentResourceOwner, and free the tupdesc if no more
     571             :  * references remain.
     572             :  *
     573             :  * Do not apply this to tupdescs that are not being refcounted.  (Use the
     574             :  * macro ReleaseTupleDesc for tupdescs of uncertain status.)
     575             :  */
     576             : void
     577    32565806 : DecrTupleDescRefCount(TupleDesc tupdesc)
     578             : {
     579             :     Assert(tupdesc->tdrefcount > 0);
     580             : 
     581    32565806 :     ResourceOwnerForgetTupleDesc(CurrentResourceOwner, tupdesc);
     582    32565806 :     if (--tupdesc->tdrefcount == 0)
     583           4 :         FreeTupleDesc(tupdesc);
     584    32565806 : }
     585             : 
     586             : /*
     587             :  * Compare two TupleDesc structures for logical equality
     588             :  */
     589             : bool
     590      435380 : equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
     591             : {
     592             :     int         i,
     593             :                 n;
     594             : 
     595      435380 :     if (tupdesc1->natts != tupdesc2->natts)
     596        2696 :         return false;
     597      432684 :     if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
     598           0 :         return false;
     599             : 
     600             :     /* tdtypmod and tdrefcount are not checked */
     601             : 
     602     2086988 :     for (i = 0; i < tupdesc1->natts; i++)
     603             :     {
     604     1668270 :         Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i);
     605     1668270 :         Form_pg_attribute attr2 = TupleDescAttr(tupdesc2, i);
     606             : 
     607             :         /*
     608             :          * We do not need to check every single field here: we can disregard
     609             :          * attrelid and attnum (which were used to place the row in the attrs
     610             :          * array in the first place).  It might look like we could dispense
     611             :          * with checking attlen/attbyval/attalign, since these are derived
     612             :          * from atttypid; but in the case of dropped columns we must check
     613             :          * them (since atttypid will be zero for all dropped columns) and in
     614             :          * general it seems safer to check them always.
     615             :          *
     616             :          * We intentionally ignore atthasmissing, since that's not very
     617             :          * relevant in tupdescs, which lack the attmissingval field.
     618             :          */
     619     1668270 :         if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
     620        1292 :             return false;
     621     1666978 :         if (attr1->atttypid != attr2->atttypid)
     622        1056 :             return false;
     623     1665922 :         if (attr1->attlen != attr2->attlen)
     624          10 :             return false;
     625     1665912 :         if (attr1->attndims != attr2->attndims)
     626           0 :             return false;
     627     1665912 :         if (attr1->atttypmod != attr2->atttypmod)
     628          42 :             return false;
     629     1665870 :         if (attr1->attbyval != attr2->attbyval)
     630          68 :             return false;
     631     1665802 :         if (attr1->attalign != attr2->attalign)
     632           0 :             return false;
     633     1665802 :         if (attr1->attstorage != attr2->attstorage)
     634         220 :             return false;
     635     1665582 :         if (attr1->attcompression != attr2->attcompression)
     636          84 :             return false;
     637     1665498 :         if (attr1->attnotnull != attr2->attnotnull)
     638        1756 :             return false;
     639             : 
     640             :         /*
     641             :          * When the column has a not-null constraint, we also need to consider
     642             :          * its validity aspect, which only manifests in CompactAttribute->
     643             :          * attnullability, so verify that.
     644             :          */
     645     1663742 :         if (attr1->attnotnull)
     646             :         {
     647      568504 :             CompactAttribute *cattr1 = TupleDescCompactAttr(tupdesc1, i);
     648      568504 :             CompactAttribute *cattr2 = TupleDescCompactAttr(tupdesc2, i);
     649             : 
     650             :             Assert(cattr1->attnullability != ATTNULLABLE_UNKNOWN);
     651             :             Assert((cattr1->attnullability == ATTNULLABLE_UNKNOWN) ==
     652             :                    (cattr2->attnullability == ATTNULLABLE_UNKNOWN));
     653             : 
     654      568504 :             if (cattr1->attnullability != cattr2->attnullability)
     655          88 :                 return false;
     656             :         }
     657     1663654 :         if (attr1->atthasdef != attr2->atthasdef)
     658        5144 :             return false;
     659     1658510 :         if (attr1->attidentity != attr2->attidentity)
     660         224 :             return false;
     661     1658286 :         if (attr1->attgenerated != attr2->attgenerated)
     662          20 :             return false;
     663     1658266 :         if (attr1->attisdropped != attr2->attisdropped)
     664           0 :             return false;
     665     1658266 :         if (attr1->attislocal != attr2->attislocal)
     666        3138 :             return false;
     667     1655128 :         if (attr1->attinhcount != attr2->attinhcount)
     668         824 :             return false;
     669     1654304 :         if (attr1->attcollation != attr2->attcollation)
     670           0 :             return false;
     671             :         /* variable-length fields are not even present... */
     672             :     }
     673             : 
     674      418718 :     if (tupdesc1->constr != NULL)
     675             :     {
     676      154220 :         TupleConstr *constr1 = tupdesc1->constr;
     677      154220 :         TupleConstr *constr2 = tupdesc2->constr;
     678             : 
     679      154220 :         if (constr2 == NULL)
     680         212 :             return false;
     681      154008 :         if (constr1->has_not_null != constr2->has_not_null)
     682           0 :             return false;
     683      154008 :         if (constr1->has_generated_stored != constr2->has_generated_stored)
     684         602 :             return false;
     685      153406 :         if (constr1->has_generated_virtual != constr2->has_generated_virtual)
     686         446 :             return false;
     687      152960 :         n = constr1->num_defval;
     688      152960 :         if (n != (int) constr2->num_defval)
     689           0 :             return false;
     690             :         /* We assume here that both AttrDefault arrays are in adnum order */
     691      171692 :         for (i = 0; i < n; i++)
     692             :         {
     693       18732 :             AttrDefault *defval1 = constr1->defval + i;
     694       18732 :             AttrDefault *defval2 = constr2->defval + i;
     695             : 
     696       18732 :             if (defval1->adnum != defval2->adnum)
     697           0 :                 return false;
     698       18732 :             if (strcmp(defval1->adbin, defval2->adbin) != 0)
     699           0 :                 return false;
     700             :         }
     701      152960 :         if (constr1->missing)
     702             :         {
     703         702 :             if (!constr2->missing)
     704          84 :                 return false;
     705        3740 :             for (i = 0; i < tupdesc1->natts; i++)
     706             :             {
     707        3268 :                 AttrMissing *missval1 = constr1->missing + i;
     708        3268 :                 AttrMissing *missval2 = constr2->missing + i;
     709             : 
     710        3268 :                 if (missval1->am_present != missval2->am_present)
     711         146 :                     return false;
     712        3122 :                 if (missval1->am_present)
     713             :                 {
     714        1048 :                     CompactAttribute *missatt1 = TupleDescCompactAttr(tupdesc1, i);
     715             : 
     716        1048 :                     if (!datumIsEqual(missval1->am_value, missval2->am_value,
     717        1048 :                                       missatt1->attbyval, missatt1->attlen))
     718           0 :                         return false;
     719             :                 }
     720             :             }
     721             :         }
     722      152258 :         else if (constr2->missing)
     723         360 :             return false;
     724      152370 :         n = constr1->num_check;
     725      152370 :         if (n != (int) constr2->num_check)
     726        1502 :             return false;
     727             : 
     728             :         /*
     729             :          * Similarly, we rely here on the ConstrCheck entries being sorted by
     730             :          * name.  If there are duplicate names, the outcome of the comparison
     731             :          * is uncertain, but that should not happen.
     732             :          */
     733      154230 :         for (i = 0; i < n; i++)
     734             :         {
     735        3464 :             ConstrCheck *check1 = constr1->check + i;
     736        3464 :             ConstrCheck *check2 = constr2->check + i;
     737             : 
     738        3464 :             if (!(strcmp(check1->ccname, check2->ccname) == 0 &&
     739        3464 :                   strcmp(check1->ccbin, check2->ccbin) == 0 &&
     740        3464 :                   check1->ccenforced == check2->ccenforced &&
     741        3446 :                   check1->ccvalid == check2->ccvalid &&
     742        3362 :                   check1->ccnoinherit == check2->ccnoinherit))
     743         102 :                 return false;
     744             :         }
     745             :     }
     746      264498 :     else if (tupdesc2->constr != NULL)
     747        1848 :         return false;
     748      413416 :     return true;
     749             : }
     750             : 
     751             : /*
     752             :  * equalRowTypes
     753             :  *
     754             :  * This determines whether two tuple descriptors have equal row types.  This
     755             :  * only checks those fields in pg_attribute that are applicable for row types,
     756             :  * while ignoring those fields that define the physical row storage or those
     757             :  * that define table column metadata.
     758             :  *
     759             :  * Specifically, this checks:
     760             :  *
     761             :  * - same number of attributes
     762             :  * - same composite type ID (but could both be zero)
     763             :  * - corresponding attributes (in order) have same the name, type, typmod,
     764             :  *   collation
     765             :  *
     766             :  * This is used to check whether two record types are compatible, whether
     767             :  * function return row types are the same, and other similar situations.
     768             :  *
     769             :  * (XXX There was some discussion whether attndims should be checked here, but
     770             :  * for now it has been decided not to.)
     771             :  *
     772             :  * Note: We deliberately do not check the tdtypmod field.  This allows
     773             :  * typcache.c to use this routine to see if a cached record type matches a
     774             :  * requested type.
     775             :  */
     776             : bool
     777      371300 : equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2)
     778             : {
     779      371300 :     if (tupdesc1->natts != tupdesc2->natts)
     780         166 :         return false;
     781      371134 :     if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
     782        1756 :         return false;
     783             : 
     784     6094710 :     for (int i = 0; i < tupdesc1->natts; i++)
     785             :     {
     786     5729130 :         Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i);
     787     5729130 :         Form_pg_attribute attr2 = TupleDescAttr(tupdesc2, i);
     788             : 
     789     5729130 :         if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
     790        3772 :             return false;
     791     5725358 :         if (attr1->atttypid != attr2->atttypid)
     792          22 :             return false;
     793     5725336 :         if (attr1->atttypmod != attr2->atttypmod)
     794           4 :             return false;
     795     5725332 :         if (attr1->attcollation != attr2->attcollation)
     796           0 :             return false;
     797             : 
     798             :         /* Record types derived from tables could have dropped fields. */
     799     5725332 :         if (attr1->attisdropped != attr2->attisdropped)
     800           0 :             return false;
     801             :     }
     802             : 
     803      365580 :     return true;
     804             : }
     805             : 
     806             : /*
     807             :  * hashRowType
     808             :  *
     809             :  * If two tuple descriptors would be considered equal by equalRowTypes()
     810             :  * then their hash value will be equal according to this function.
     811             :  */
     812             : uint32
     813      393602 : hashRowType(TupleDesc desc)
     814             : {
     815             :     uint32      s;
     816             :     int         i;
     817             : 
     818      393602 :     s = hash_combine(0, hash_uint32(desc->natts));
     819      393602 :     s = hash_combine(s, hash_uint32(desc->tdtypeid));
     820     6435544 :     for (i = 0; i < desc->natts; ++i)
     821     6041942 :         s = hash_combine(s, hash_uint32(TupleDescAttr(desc, i)->atttypid));
     822             : 
     823      393602 :     return s;
     824             : }
     825             : 
     826             : /*
     827             :  * TupleDescInitEntry
     828             :  *      This function initializes a single attribute structure in
     829             :  *      a previously allocated tuple descriptor.
     830             :  *
     831             :  * If attributeName is NULL, the attname field is set to an empty string
     832             :  * (this is for cases where we don't know or need a name for the field).
     833             :  * Also, some callers use this function to change the datatype-related fields
     834             :  * in an existing tupdesc; they pass attributeName = NameStr(att->attname)
     835             :  * to indicate that the attname field shouldn't be modified.
     836             :  *
     837             :  * Note that attcollation is set to the default for the specified datatype.
     838             :  * If a nondefault collation is needed, insert it afterwards using
     839             :  * TupleDescInitEntryCollation.
     840             :  */
     841             : void
     842    12687034 : TupleDescInitEntry(TupleDesc desc,
     843             :                    AttrNumber attributeNumber,
     844             :                    const char *attributeName,
     845             :                    Oid oidtypeid,
     846             :                    int32 typmod,
     847             :                    int attdim)
     848             : {
     849             :     HeapTuple   tuple;
     850             :     Form_pg_type typeForm;
     851             :     Form_pg_attribute att;
     852             : 
     853             :     /*
     854             :      * sanity checks
     855             :      */
     856             :     Assert(PointerIsValid(desc));
     857             :     Assert(attributeNumber >= 1);
     858             :     Assert(attributeNumber <= desc->natts);
     859             :     Assert(attdim >= 0);
     860             :     Assert(attdim <= PG_INT16_MAX);
     861             : 
     862             :     /*
     863             :      * initialize the attribute fields
     864             :      */
     865    12687034 :     att = TupleDescAttr(desc, attributeNumber - 1);
     866             : 
     867    12687034 :     att->attrelid = 0;           /* dummy value */
     868             : 
     869             :     /*
     870             :      * Note: attributeName can be NULL, because the planner doesn't always
     871             :      * fill in valid resname values in targetlists, particularly for resjunk
     872             :      * attributes. Also, do nothing if caller wants to re-use the old attname.
     873             :      */
     874    12687034 :     if (attributeName == NULL)
     875    19705810 :         MemSet(NameStr(att->attname), 0, NAMEDATALEN);
     876     8515816 :     else if (attributeName != NameStr(att->attname))
     877     8512362 :         namestrcpy(&(att->attname), attributeName);
     878             : 
     879    12687034 :     att->atttypmod = typmod;
     880             : 
     881    12687034 :     att->attnum = attributeNumber;
     882    12687034 :     att->attndims = attdim;
     883             : 
     884    12687034 :     att->attnotnull = false;
     885    12687034 :     att->atthasdef = false;
     886    12687034 :     att->atthasmissing = false;
     887    12687034 :     att->attidentity = '\0';
     888    12687034 :     att->attgenerated = '\0';
     889    12687034 :     att->attisdropped = false;
     890    12687034 :     att->attislocal = true;
     891    12687034 :     att->attinhcount = 0;
     892             :     /* variable-length fields are not present in tupledescs */
     893             : 
     894    12687034 :     tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
     895    12687034 :     if (!HeapTupleIsValid(tuple))
     896           0 :         elog(ERROR, "cache lookup failed for type %u", oidtypeid);
     897    12687034 :     typeForm = (Form_pg_type) GETSTRUCT(tuple);
     898             : 
     899    12687034 :     att->atttypid = oidtypeid;
     900    12687034 :     att->attlen = typeForm->typlen;
     901    12687034 :     att->attbyval = typeForm->typbyval;
     902    12687034 :     att->attalign = typeForm->typalign;
     903    12687034 :     att->attstorage = typeForm->typstorage;
     904    12687034 :     att->attcompression = InvalidCompressionMethod;
     905    12687034 :     att->attcollation = typeForm->typcollation;
     906             : 
     907    12687034 :     populate_compact_attribute(desc, attributeNumber - 1);
     908             : 
     909    12687034 :     ReleaseSysCache(tuple);
     910    12687034 : }
     911             : 
     912             : /*
     913             :  * TupleDescInitBuiltinEntry
     914             :  *      Initialize a tuple descriptor without catalog access.  Only
     915             :  *      a limited range of builtin types are supported.
     916             :  */
     917             : void
     918       13276 : TupleDescInitBuiltinEntry(TupleDesc desc,
     919             :                           AttrNumber attributeNumber,
     920             :                           const char *attributeName,
     921             :                           Oid oidtypeid,
     922             :                           int32 typmod,
     923             :                           int attdim)
     924             : {
     925             :     Form_pg_attribute att;
     926             : 
     927             :     /* sanity checks */
     928             :     Assert(PointerIsValid(desc));
     929             :     Assert(attributeNumber >= 1);
     930             :     Assert(attributeNumber <= desc->natts);
     931             :     Assert(attdim >= 0);
     932             :     Assert(attdim <= PG_INT16_MAX);
     933             : 
     934             :     /* initialize the attribute fields */
     935       13276 :     att = TupleDescAttr(desc, attributeNumber - 1);
     936       13276 :     att->attrelid = 0;           /* dummy value */
     937             : 
     938             :     /* unlike TupleDescInitEntry, we require an attribute name */
     939             :     Assert(attributeName != NULL);
     940       13276 :     namestrcpy(&(att->attname), attributeName);
     941             : 
     942       13276 :     att->atttypmod = typmod;
     943             : 
     944       13276 :     att->attnum = attributeNumber;
     945       13276 :     att->attndims = attdim;
     946             : 
     947       13276 :     att->attnotnull = false;
     948       13276 :     att->atthasdef = false;
     949       13276 :     att->atthasmissing = false;
     950       13276 :     att->attidentity = '\0';
     951       13276 :     att->attgenerated = '\0';
     952       13276 :     att->attisdropped = false;
     953       13276 :     att->attislocal = true;
     954       13276 :     att->attinhcount = 0;
     955             :     /* variable-length fields are not present in tupledescs */
     956             : 
     957       13276 :     att->atttypid = oidtypeid;
     958             : 
     959             :     /*
     960             :      * Our goal here is to support just enough types to let basic builtin
     961             :      * commands work without catalog access - e.g. so that we can do certain
     962             :      * things even in processes that are not connected to a database.
     963             :      */
     964       13276 :     switch (oidtypeid)
     965             :     {
     966       10668 :         case TEXTOID:
     967             :         case TEXTARRAYOID:
     968       10668 :             att->attlen = -1;
     969       10668 :             att->attbyval = false;
     970       10668 :             att->attalign = TYPALIGN_INT;
     971       10668 :             att->attstorage = TYPSTORAGE_EXTENDED;
     972       10668 :             att->attcompression = InvalidCompressionMethod;
     973       10668 :             att->attcollation = DEFAULT_COLLATION_OID;
     974       10668 :             break;
     975             : 
     976           0 :         case BOOLOID:
     977           0 :             att->attlen = 1;
     978           0 :             att->attbyval = true;
     979           0 :             att->attalign = TYPALIGN_CHAR;
     980           0 :             att->attstorage = TYPSTORAGE_PLAIN;
     981           0 :             att->attcompression = InvalidCompressionMethod;
     982           0 :             att->attcollation = InvalidOid;
     983           0 :             break;
     984             : 
     985           0 :         case INT4OID:
     986           0 :             att->attlen = 4;
     987           0 :             att->attbyval = true;
     988           0 :             att->attalign = TYPALIGN_INT;
     989           0 :             att->attstorage = TYPSTORAGE_PLAIN;
     990           0 :             att->attcompression = InvalidCompressionMethod;
     991           0 :             att->attcollation = InvalidOid;
     992           0 :             break;
     993             : 
     994        2296 :         case INT8OID:
     995        2296 :             att->attlen = 8;
     996        2296 :             att->attbyval = FLOAT8PASSBYVAL;
     997        2296 :             att->attalign = TYPALIGN_DOUBLE;
     998        2296 :             att->attstorage = TYPSTORAGE_PLAIN;
     999        2296 :             att->attcompression = InvalidCompressionMethod;
    1000        2296 :             att->attcollation = InvalidOid;
    1001        2296 :             break;
    1002             : 
    1003         312 :         case OIDOID:
    1004         312 :             att->attlen = 4;
    1005         312 :             att->attbyval = true;
    1006         312 :             att->attalign = TYPALIGN_INT;
    1007         312 :             att->attstorage = TYPSTORAGE_PLAIN;
    1008         312 :             att->attcompression = InvalidCompressionMethod;
    1009         312 :             att->attcollation = InvalidOid;
    1010         312 :             break;
    1011             : 
    1012           0 :         default:
    1013           0 :             elog(ERROR, "unsupported type %u", oidtypeid);
    1014             :     }
    1015             : 
    1016       13276 :     populate_compact_attribute(desc, attributeNumber - 1);
    1017       13276 : }
    1018             : 
    1019             : /*
    1020             :  * TupleDescInitEntryCollation
    1021             :  *
    1022             :  * Assign a nondefault collation to a previously initialized tuple descriptor
    1023             :  * entry.
    1024             :  */
    1025             : void
    1026     6519130 : TupleDescInitEntryCollation(TupleDesc desc,
    1027             :                             AttrNumber attributeNumber,
    1028             :                             Oid collationid)
    1029             : {
    1030             :     /*
    1031             :      * sanity checks
    1032             :      */
    1033             :     Assert(PointerIsValid(desc));
    1034             :     Assert(attributeNumber >= 1);
    1035             :     Assert(attributeNumber <= desc->natts);
    1036             : 
    1037     6519130 :     TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid;
    1038     6519130 : }
    1039             : 
    1040             : /*
    1041             :  * BuildDescFromLists
    1042             :  *
    1043             :  * Build a TupleDesc given lists of column names (as String nodes),
    1044             :  * column type OIDs, typmods, and collation OIDs.
    1045             :  *
    1046             :  * No constraints are generated.
    1047             :  *
    1048             :  * This is for use with functions returning RECORD.
    1049             :  */
    1050             : TupleDesc
    1051        1418 : BuildDescFromLists(const List *names, const List *types, const List *typmods, const List *collations)
    1052             : {
    1053             :     int         natts;
    1054             :     AttrNumber  attnum;
    1055             :     ListCell   *l1;
    1056             :     ListCell   *l2;
    1057             :     ListCell   *l3;
    1058             :     ListCell   *l4;
    1059             :     TupleDesc   desc;
    1060             : 
    1061        1418 :     natts = list_length(names);
    1062             :     Assert(natts == list_length(types));
    1063             :     Assert(natts == list_length(typmods));
    1064             :     Assert(natts == list_length(collations));
    1065             : 
    1066             :     /*
    1067             :      * allocate a new tuple descriptor
    1068             :      */
    1069        1418 :     desc = CreateTemplateTupleDesc(natts);
    1070             : 
    1071        1418 :     attnum = 0;
    1072        4998 :     forfour(l1, names, l2, types, l3, typmods, l4, collations)
    1073             :     {
    1074        3580 :         char       *attname = strVal(lfirst(l1));
    1075        3580 :         Oid         atttypid = lfirst_oid(l2);
    1076        3580 :         int32       atttypmod = lfirst_int(l3);
    1077        3580 :         Oid         attcollation = lfirst_oid(l4);
    1078             : 
    1079        3580 :         attnum++;
    1080             : 
    1081        3580 :         TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
    1082        3580 :         TupleDescInitEntryCollation(desc, attnum, attcollation);
    1083             :     }
    1084             : 
    1085        1418 :     return desc;
    1086             : }
    1087             : 
    1088             : /*
    1089             :  * Get default expression (or NULL if none) for the given attribute number.
    1090             :  */
    1091             : Node *
    1092      151524 : TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
    1093             : {
    1094      151524 :     Node       *result = NULL;
    1095             : 
    1096      151524 :     if (tupdesc->constr)
    1097             :     {
    1098      151524 :         AttrDefault *attrdef = tupdesc->constr->defval;
    1099             : 
    1100      228586 :         for (int i = 0; i < tupdesc->constr->num_defval; i++)
    1101             :         {
    1102      228586 :             if (attrdef[i].adnum == attnum)
    1103             :             {
    1104      151524 :                 result = stringToNode(attrdef[i].adbin);
    1105      151524 :                 break;
    1106             :             }
    1107             :         }
    1108             :     }
    1109             : 
    1110      151524 :     return result;
    1111             : }
    1112             : 
    1113             : /* ResourceOwner callbacks */
    1114             : 
    1115             : static void
    1116       15166 : ResOwnerReleaseTupleDesc(Datum res)
    1117             : {
    1118       15166 :     TupleDesc   tupdesc = (TupleDesc) DatumGetPointer(res);
    1119             : 
    1120             :     /* Like DecrTupleDescRefCount, but don't call ResourceOwnerForget() */
    1121             :     Assert(tupdesc->tdrefcount > 0);
    1122       15166 :     if (--tupdesc->tdrefcount == 0)
    1123         496 :         FreeTupleDesc(tupdesc);
    1124       15166 : }
    1125             : 
    1126             : static char *
    1127           0 : ResOwnerPrintTupleDesc(Datum res)
    1128             : {
    1129           0 :     TupleDesc   tupdesc = (TupleDesc) DatumGetPointer(res);
    1130             : 
    1131           0 :     return psprintf("TupleDesc %p (%u,%d)",
    1132             :                     tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
    1133             : }

Generated by: LCOV version 1.16