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

Generated by: LCOV version 1.16