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

Generated by: LCOV version 1.14