LCOV - code coverage report
Current view: top level - src/backend/utils/cache - typcache.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 549 617 89.0 %
Date: 2017-09-29 13:40:31 Functions: 32 33 97.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * typcache.c
       4             :  *    POSTGRES type cache code
       5             :  *
       6             :  * The type cache exists to speed lookup of certain information about data
       7             :  * types that is not directly available from a type's pg_type row.  For
       8             :  * example, we use a type's default btree opclass, or the default hash
       9             :  * opclass if no btree opclass exists, to determine which operators should
      10             :  * be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
      11             :  *
      12             :  * Several seemingly-odd choices have been made to support use of the type
      13             :  * cache by generic array and record handling routines, such as array_eq(),
      14             :  * record_cmp(), and hash_array().  Because those routines are used as index
      15             :  * support operations, they cannot leak memory.  To allow them to execute
      16             :  * efficiently, all information that they would like to re-use across calls
      17             :  * is kept in the type cache.
      18             :  *
      19             :  * Once created, a type cache entry lives as long as the backend does, so
      20             :  * there is no need for a call to release a cache entry.  If the type is
      21             :  * dropped, the cache entry simply becomes wasted storage.  This is not
      22             :  * expected to happen often, and assuming that typcache entries are good
      23             :  * permanently allows caching pointers to them in long-lived places.
      24             :  *
      25             :  * We have some provisions for updating cache entries if the stored data
      26             :  * becomes obsolete.  Information dependent on opclasses is cleared if we
      27             :  * detect updates to pg_opclass.  We also support clearing the tuple
      28             :  * descriptor and operator/function parts of a rowtype's cache entry,
      29             :  * since those may need to change as a consequence of ALTER TABLE.
      30             :  * Domain constraint changes are also tracked properly.
      31             :  *
      32             :  *
      33             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      34             :  * Portions Copyright (c) 1994, Regents of the University of California
      35             :  *
      36             :  * IDENTIFICATION
      37             :  *    src/backend/utils/cache/typcache.c
      38             :  *
      39             :  *-------------------------------------------------------------------------
      40             :  */
      41             : #include "postgres.h"
      42             : 
      43             : #include <limits.h>
      44             : 
      45             : #include "access/hash.h"
      46             : #include "access/heapam.h"
      47             : #include "access/htup_details.h"
      48             : #include "access/nbtree.h"
      49             : #include "catalog/indexing.h"
      50             : #include "catalog/pg_am.h"
      51             : #include "catalog/pg_constraint.h"
      52             : #include "catalog/pg_enum.h"
      53             : #include "catalog/pg_operator.h"
      54             : #include "catalog/pg_range.h"
      55             : #include "catalog/pg_type.h"
      56             : #include "commands/defrem.h"
      57             : #include "executor/executor.h"
      58             : #include "optimizer/planner.h"
      59             : #include "utils/builtins.h"
      60             : #include "utils/catcache.h"
      61             : #include "utils/fmgroids.h"
      62             : #include "utils/inval.h"
      63             : #include "utils/lsyscache.h"
      64             : #include "utils/memutils.h"
      65             : #include "utils/rel.h"
      66             : #include "utils/snapmgr.h"
      67             : #include "utils/syscache.h"
      68             : #include "utils/typcache.h"
      69             : 
      70             : 
      71             : /* The main type cache hashtable searched by lookup_type_cache */
      72             : static HTAB *TypeCacheHash = NULL;
      73             : 
      74             : /* List of type cache entries for domain types */
      75             : static TypeCacheEntry *firstDomainTypeEntry = NULL;
      76             : 
      77             : /* Private flag bits in the TypeCacheEntry.flags field */
      78             : #define TCFLAGS_CHECKED_BTREE_OPCLASS       0x0001
      79             : #define TCFLAGS_CHECKED_HASH_OPCLASS        0x0002
      80             : #define TCFLAGS_CHECKED_EQ_OPR              0x0004
      81             : #define TCFLAGS_CHECKED_LT_OPR              0x0008
      82             : #define TCFLAGS_CHECKED_GT_OPR              0x0010
      83             : #define TCFLAGS_CHECKED_CMP_PROC            0x0020
      84             : #define TCFLAGS_CHECKED_HASH_PROC           0x0040
      85             : #define TCFLAGS_CHECKED_ELEM_PROPERTIES     0x0080
      86             : #define TCFLAGS_HAVE_ELEM_EQUALITY          0x0100
      87             : #define TCFLAGS_HAVE_ELEM_COMPARE           0x0200
      88             : #define TCFLAGS_HAVE_ELEM_HASHING           0x0400
      89             : #define TCFLAGS_CHECKED_FIELD_PROPERTIES    0x0800
      90             : #define TCFLAGS_HAVE_FIELD_EQUALITY         0x1000
      91             : #define TCFLAGS_HAVE_FIELD_COMPARE          0x2000
      92             : #define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS  0x4000
      93             : #define TCFLAGS_CHECKED_HASH_EXTENDED_PROC  0x8000
      94             : 
      95             : /*
      96             :  * Data stored about a domain type's constraints.  Note that we do not create
      97             :  * this struct for the common case of a constraint-less domain; we just set
      98             :  * domainData to NULL to indicate that.
      99             :  *
     100             :  * Within a DomainConstraintCache, we store expression plan trees, but the
     101             :  * check_exprstate fields of the DomainConstraintState nodes are just NULL.
     102             :  * When needed, expression evaluation nodes are built by flat-copying the
     103             :  * DomainConstraintState nodes and applying ExecInitExpr to check_expr.
     104             :  * Such a node tree is not part of the DomainConstraintCache, but is
     105             :  * considered to belong to a DomainConstraintRef.
     106             :  */
     107             : struct DomainConstraintCache
     108             : {
     109             :     List       *constraints;    /* list of DomainConstraintState nodes */
     110             :     MemoryContext dccContext;   /* memory context holding all associated data */
     111             :     long        dccRefCount;    /* number of references to this struct */
     112             : };
     113             : 
     114             : /* Private information to support comparisons of enum values */
     115             : typedef struct
     116             : {
     117             :     Oid         enum_oid;       /* OID of one enum value */
     118             :     float4      sort_order;     /* its sort position */
     119             : } EnumItem;
     120             : 
     121             : typedef struct TypeCacheEnumData
     122             : {
     123             :     Oid         bitmap_base;    /* OID corresponding to bit 0 of bitmapset */
     124             :     Bitmapset  *sorted_values;  /* Set of OIDs known to be in order */
     125             :     int         num_values;     /* total number of values in enum */
     126             :     EnumItem    enum_values[FLEXIBLE_ARRAY_MEMBER];
     127             : } TypeCacheEnumData;
     128             : 
     129             : /*
     130             :  * We use a separate table for storing the definitions of non-anonymous
     131             :  * record types.  Once defined, a record type will be remembered for the
     132             :  * life of the backend.  Subsequent uses of the "same" record type (where
     133             :  * sameness means equalTupleDescs) will refer to the existing table entry.
     134             :  *
     135             :  * Stored record types are remembered in a linear array of TupleDescs,
     136             :  * which can be indexed quickly with the assigned typmod.  There is also
     137             :  * a hash table to speed searches for matching TupleDescs.
     138             :  */
     139             : 
     140             : typedef struct RecordCacheEntry
     141             : {
     142             :     TupleDesc   tupdesc;
     143             : } RecordCacheEntry;
     144             : 
     145             : static HTAB *RecordCacheHash = NULL;
     146             : 
     147             : static TupleDesc *RecordCacheArray = NULL;
     148             : static int32 RecordCacheArrayLen = 0;   /* allocated length of array */
     149             : static int32 NextRecordTypmod = 0;  /* number of entries used */
     150             : 
     151             : static void load_typcache_tupdesc(TypeCacheEntry *typentry);
     152             : static void load_rangetype_info(TypeCacheEntry *typentry);
     153             : static void load_domaintype_info(TypeCacheEntry *typentry);
     154             : static int  dcs_cmp(const void *a, const void *b);
     155             : static void decr_dcc_refcount(DomainConstraintCache *dcc);
     156             : static void dccref_deletion_callback(void *arg);
     157             : static List *prep_domain_constraints(List *constraints, MemoryContext execctx);
     158             : static bool array_element_has_equality(TypeCacheEntry *typentry);
     159             : static bool array_element_has_compare(TypeCacheEntry *typentry);
     160             : static bool array_element_has_hashing(TypeCacheEntry *typentry);
     161             : static void cache_array_element_properties(TypeCacheEntry *typentry);
     162             : static bool record_fields_have_equality(TypeCacheEntry *typentry);
     163             : static bool record_fields_have_compare(TypeCacheEntry *typentry);
     164             : static void cache_record_field_properties(TypeCacheEntry *typentry);
     165             : static void TypeCacheRelCallback(Datum arg, Oid relid);
     166             : static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
     167             : static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
     168             : static void load_enum_cache_data(TypeCacheEntry *tcache);
     169             : static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
     170             : static int  enum_oid_cmp(const void *left, const void *right);
     171             : 
     172             : 
     173             : /*
     174             :  * lookup_type_cache
     175             :  *
     176             :  * Fetch the type cache entry for the specified datatype, and make sure that
     177             :  * all the fields requested by bits in 'flags' are valid.
     178             :  *
     179             :  * The result is never NULL --- we will ereport() if the passed type OID is
     180             :  * invalid.  Note however that we may fail to find one or more of the
     181             :  * values requested by 'flags'; the caller needs to check whether the fields
     182             :  * are InvalidOid or not.
     183             :  */
     184             : TypeCacheEntry *
     185       22619 : lookup_type_cache(Oid type_id, int flags)
     186             : {
     187             :     TypeCacheEntry *typentry;
     188             :     bool        found;
     189             : 
     190       22619 :     if (TypeCacheHash == NULL)
     191             :     {
     192             :         /* First time through: initialize the hash table */
     193             :         HASHCTL     ctl;
     194             : 
     195         134 :         MemSet(&ctl, 0, sizeof(ctl));
     196         134 :         ctl.keysize = sizeof(Oid);
     197         134 :         ctl.entrysize = sizeof(TypeCacheEntry);
     198         134 :         TypeCacheHash = hash_create("Type information cache", 64,
     199             :                                     &ctl, HASH_ELEM | HASH_BLOBS);
     200             : 
     201             :         /* Also set up callbacks for SI invalidations */
     202         134 :         CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0);
     203         134 :         CacheRegisterSyscacheCallback(CLAOID, TypeCacheOpcCallback, (Datum) 0);
     204         134 :         CacheRegisterSyscacheCallback(CONSTROID, TypeCacheConstrCallback, (Datum) 0);
     205         134 :         CacheRegisterSyscacheCallback(TYPEOID, TypeCacheConstrCallback, (Datum) 0);
     206             : 
     207             :         /* Also make sure CacheMemoryContext exists */
     208         134 :         if (!CacheMemoryContext)
     209           0 :             CreateCacheMemoryContext();
     210             :     }
     211             : 
     212             :     /* Try to look up an existing entry */
     213       22619 :     typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
     214             :                                               (void *) &type_id,
     215             :                                               HASH_FIND, NULL);
     216       22619 :     if (typentry == NULL)
     217             :     {
     218             :         /*
     219             :          * If we didn't find one, we want to make one.  But first look up the
     220             :          * pg_type row, just to make sure we don't make a cache entry for an
     221             :          * invalid type OID.  If the type OID is not valid, present a
     222             :          * user-facing error, since some code paths such as domain_in() allow
     223             :          * this function to be reached with a user-supplied OID.
     224             :          */
     225             :         HeapTuple   tp;
     226             :         Form_pg_type typtup;
     227             : 
     228         781 :         tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
     229         781 :         if (!HeapTupleIsValid(tp))
     230           0 :             ereport(ERROR,
     231             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     232             :                      errmsg("type with OID %u does not exist", type_id)));
     233         781 :         typtup = (Form_pg_type) GETSTRUCT(tp);
     234         781 :         if (!typtup->typisdefined)
     235           0 :             ereport(ERROR,
     236             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     237             :                      errmsg("type \"%s\" is only a shell",
     238             :                             NameStr(typtup->typname))));
     239             : 
     240             :         /* Now make the typcache entry */
     241         781 :         typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
     242             :                                                   (void *) &type_id,
     243             :                                                   HASH_ENTER, &found);
     244         781 :         Assert(!found);         /* it wasn't there a moment ago */
     245             : 
     246         781 :         MemSet(typentry, 0, sizeof(TypeCacheEntry));
     247         781 :         typentry->type_id = type_id;
     248         781 :         typentry->typlen = typtup->typlen;
     249         781 :         typentry->typbyval = typtup->typbyval;
     250         781 :         typentry->typalign = typtup->typalign;
     251         781 :         typentry->typstorage = typtup->typstorage;
     252         781 :         typentry->typtype = typtup->typtype;
     253         781 :         typentry->typrelid = typtup->typrelid;
     254             : 
     255             :         /* If it's a domain, immediately thread it into the domain cache list */
     256         781 :         if (typentry->typtype == TYPTYPE_DOMAIN)
     257             :         {
     258          79 :             typentry->nextDomain = firstDomainTypeEntry;
     259          79 :             firstDomainTypeEntry = typentry;
     260             :         }
     261             : 
     262         781 :         ReleaseSysCache(tp);
     263             :     }
     264             : 
     265             :     /*
     266             :      * Look up opclasses if we haven't already and any dependent info is
     267             :      * requested.
     268             :      */
     269       22619 :     if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
     270             :                   TYPECACHE_CMP_PROC |
     271             :                   TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO |
     272        7028 :                   TYPECACHE_BTREE_OPFAMILY)) &&
     273        7028 :         !(typentry->flags & TCFLAGS_CHECKED_BTREE_OPCLASS))
     274             :     {
     275             :         Oid         opclass;
     276             : 
     277         555 :         opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
     278         555 :         if (OidIsValid(opclass))
     279             :         {
     280         536 :             typentry->btree_opf = get_opclass_family(opclass);
     281         536 :             typentry->btree_opintype = get_opclass_input_type(opclass);
     282             :         }
     283             :         else
     284             :         {
     285          19 :             typentry->btree_opf = typentry->btree_opintype = InvalidOid;
     286             :         }
     287             : 
     288             :         /*
     289             :          * Reset information derived from btree opclass.  Note in particular
     290             :          * that we'll redetermine the eq_opr even if we previously found one;
     291             :          * this matters in case a btree opclass has been added to a type that
     292             :          * previously had only a hash opclass.
     293             :          */
     294         555 :         typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
     295             :                              TCFLAGS_CHECKED_LT_OPR |
     296             :                              TCFLAGS_CHECKED_GT_OPR |
     297             :                              TCFLAGS_CHECKED_CMP_PROC);
     298         555 :         typentry->flags |= TCFLAGS_CHECKED_BTREE_OPCLASS;
     299             :     }
     300             : 
     301             :     /*
     302             :      * If we need to look up equality operator, and there's no btree opclass,
     303             :      * force lookup of hash opclass.
     304             :      */
     305       29010 :     if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
     306        6929 :         !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR) &&
     307         538 :         typentry->btree_opf == InvalidOid)
     308          18 :         flags |= TYPECACHE_HASH_OPFAMILY;
     309             : 
     310       22619 :     if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO |
     311             :                   TYPECACHE_HASH_EXTENDED_PROC |
     312             :                   TYPECACHE_HASH_EXTENDED_PROC_FINFO |
     313        4518 :                   TYPECACHE_HASH_OPFAMILY)) &&
     314        4518 :         !(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
     315             :     {
     316             :         Oid         opclass;
     317             : 
     318         402 :         opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
     319         402 :         if (OidIsValid(opclass))
     320             :         {
     321         384 :             typentry->hash_opf = get_opclass_family(opclass);
     322         384 :             typentry->hash_opintype = get_opclass_input_type(opclass);
     323             :         }
     324             :         else
     325             :         {
     326          18 :             typentry->hash_opf = typentry->hash_opintype = InvalidOid;
     327             :         }
     328             : 
     329             :         /*
     330             :          * Reset information derived from hash opclass.  We do *not* reset the
     331             :          * eq_opr; if we already found one from the btree opclass, that
     332             :          * decision is still good.
     333             :          */
     334         402 :         typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
     335         402 :         typentry->flags &= ~(TCFLAGS_CHECKED_HASH_EXTENDED_PROC);
     336         402 :         typentry->flags |= TCFLAGS_CHECKED_HASH_OPCLASS;
     337             :     }
     338             : 
     339             :     /*
     340             :      * Look for requested operators and functions, if we haven't already.
     341             :      */
     342       29010 :     if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
     343        6391 :         !(typentry->flags & TCFLAGS_CHECKED_EQ_OPR))
     344             :     {
     345         538 :         Oid         eq_opr = InvalidOid;
     346             : 
     347         538 :         if (typentry->btree_opf != InvalidOid)
     348         520 :             eq_opr = get_opfamily_member(typentry->btree_opf,
     349             :                                          typentry->btree_opintype,
     350             :                                          typentry->btree_opintype,
     351             :                                          BTEqualStrategyNumber);
     352         556 :         if (eq_opr == InvalidOid &&
     353          18 :             typentry->hash_opf != InvalidOid)
     354           7 :             eq_opr = get_opfamily_member(typentry->hash_opf,
     355             :                                          typentry->hash_opintype,
     356             :                                          typentry->hash_opintype,
     357             :                                          HTEqualStrategyNumber);
     358             : 
     359             :         /*
     360             :          * If the proposed equality operator is array_eq or record_eq, check
     361             :          * to see if the element type or column types support equality. If
     362             :          * not, array_eq or record_eq would fail at runtime, so we don't want
     363             :          * to report that the type has equality.
     364             :          */
     365         557 :         if (eq_opr == ARRAY_EQ_OP &&
     366          19 :             !array_element_has_equality(typentry))
     367           0 :             eq_opr = InvalidOid;
     368         547 :         else if (eq_opr == RECORD_EQ_OP &&
     369           9 :                  !record_fields_have_equality(typentry))
     370           2 :             eq_opr = InvalidOid;
     371             : 
     372             :         /* Force update of eq_opr_finfo only if we're changing state */
     373         538 :         if (typentry->eq_opr != eq_opr)
     374         497 :             typentry->eq_opr_finfo.fn_oid = InvalidOid;
     375             : 
     376         538 :         typentry->eq_opr = eq_opr;
     377             : 
     378             :         /*
     379             :          * Reset info about hash functions whenever we pick up new info about
     380             :          * equality operator.  This is so we can ensure that the hash functions
     381             :          * match the operator.
     382             :          */
     383         538 :         typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
     384         538 :         typentry->flags &= ~(TCFLAGS_CHECKED_HASH_EXTENDED_PROC);
     385         538 :         typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
     386             :     }
     387       28611 :     if ((flags & TYPECACHE_LT_OPR) &&
     388        5992 :         !(typentry->flags & TCFLAGS_CHECKED_LT_OPR))
     389             :     {
     390         485 :         Oid         lt_opr = InvalidOid;
     391             : 
     392         485 :         if (typentry->btree_opf != InvalidOid)
     393         469 :             lt_opr = get_opfamily_member(typentry->btree_opf,
     394             :                                          typentry->btree_opintype,
     395             :                                          typentry->btree_opintype,
     396             :                                          BTLessStrategyNumber);
     397             : 
     398             :         /* As above, make sure array_cmp or record_cmp will succeed */
     399         504 :         if (lt_opr == ARRAY_LT_OP &&
     400          19 :             !array_element_has_compare(typentry))
     401           3 :             lt_opr = InvalidOid;
     402         488 :         else if (lt_opr == RECORD_LT_OP &&
     403           6 :                  !record_fields_have_compare(typentry))
     404           2 :             lt_opr = InvalidOid;
     405             : 
     406         485 :         typentry->lt_opr = lt_opr;
     407         485 :         typentry->flags |= TCFLAGS_CHECKED_LT_OPR;
     408             :     }
     409       28435 :     if ((flags & TYPECACHE_GT_OPR) &&
     410        5816 :         !(typentry->flags & TCFLAGS_CHECKED_GT_OPR))
     411             :     {
     412         483 :         Oid         gt_opr = InvalidOid;
     413             : 
     414         483 :         if (typentry->btree_opf != InvalidOid)
     415         467 :             gt_opr = get_opfamily_member(typentry->btree_opf,
     416             :                                          typentry->btree_opintype,
     417             :                                          typentry->btree_opintype,
     418             :                                          BTGreaterStrategyNumber);
     419             : 
     420             :         /* As above, make sure array_cmp or record_cmp will succeed */
     421         502 :         if (gt_opr == ARRAY_GT_OP &&
     422          19 :             !array_element_has_compare(typentry))
     423           3 :             gt_opr = InvalidOid;
     424         486 :         else if (gt_opr == RECORD_GT_OP &&
     425           6 :                  !record_fields_have_compare(typentry))
     426           2 :             gt_opr = InvalidOid;
     427             : 
     428         483 :         typentry->gt_opr = gt_opr;
     429         483 :         typentry->flags |= TCFLAGS_CHECKED_GT_OPR;
     430             :     }
     431       22983 :     if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
     432         364 :         !(typentry->flags & TCFLAGS_CHECKED_CMP_PROC))
     433             :     {
     434          52 :         Oid         cmp_proc = InvalidOid;
     435             : 
     436          52 :         if (typentry->btree_opf != InvalidOid)
     437          47 :             cmp_proc = get_opfamily_proc(typentry->btree_opf,
     438             :                                          typentry->btree_opintype,
     439             :                                          typentry->btree_opintype,
     440             :                                          BTORDER_PROC);
     441             : 
     442             :         /* As above, make sure array_cmp or record_cmp will succeed */
     443          57 :         if (cmp_proc == F_BTARRAYCMP &&
     444           5 :             !array_element_has_compare(typentry))
     445           0 :             cmp_proc = InvalidOid;
     446          56 :         else if (cmp_proc == F_BTRECORDCMP &&
     447           4 :                  !record_fields_have_compare(typentry))
     448           0 :             cmp_proc = InvalidOid;
     449             : 
     450             :         /* Force update of cmp_proc_finfo only if we're changing state */
     451          52 :         if (typentry->cmp_proc != cmp_proc)
     452          46 :             typentry->cmp_proc_finfo.fn_oid = InvalidOid;
     453             : 
     454          52 :         typentry->cmp_proc = cmp_proc;
     455          52 :         typentry->flags |= TCFLAGS_CHECKED_CMP_PROC;
     456             :     }
     457       27122 :     if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO)) &&
     458        4503 :         !(typentry->flags & TCFLAGS_CHECKED_HASH_PROC))
     459             :     {
     460         389 :         Oid         hash_proc = InvalidOid;
     461             : 
     462             :         /*
     463             :          * We insist that the eq_opr, if one has been determined, match the
     464             :          * hash opclass; else report there is no hash function.
     465             :          */
     466         771 :         if (typentry->hash_opf != InvalidOid &&
     467         762 :             (!OidIsValid(typentry->eq_opr) ||
     468         380 :              typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
     469             :                                                      typentry->hash_opintype,
     470             :                                                      typentry->hash_opintype,
     471             :                                                      HTEqualStrategyNumber)))
     472         382 :             hash_proc = get_opfamily_proc(typentry->hash_opf,
     473             :                                           typentry->hash_opintype,
     474             :                                           typentry->hash_opintype,
     475             :                                           HASHSTANDARD_PROC);
     476             : 
     477             :         /*
     478             :          * As above, make sure hash_array will succeed.  We don't currently
     479             :          * support hashing for composite types, but when we do, we'll need
     480             :          * more logic here to check that case too.
     481             :          */
     482         391 :         if (hash_proc == F_HASH_ARRAY &&
     483           2 :             !array_element_has_hashing(typentry))
     484           1 :             hash_proc = InvalidOid;
     485             : 
     486             :         /* Force update of hash_proc_finfo only if we're changing state */
     487         389 :         if (typentry->hash_proc != hash_proc)
     488         359 :             typentry->hash_proc_finfo.fn_oid = InvalidOid;
     489             : 
     490         389 :         typentry->hash_proc = hash_proc;
     491         389 :         typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
     492             :     }
     493       22619 :     if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
     494           2 :                   TYPECACHE_HASH_EXTENDED_PROC_FINFO)) &&
     495           2 :         !(typentry->flags & TCFLAGS_CHECKED_HASH_EXTENDED_PROC))
     496             :     {
     497           1 :         Oid         hash_extended_proc = InvalidOid;
     498             : 
     499             :         /*
     500             :          * We insist that the eq_opr, if one has been determined, match the
     501             :          * hash opclass; else report there is no hash function.
     502             :          */
     503           2 :         if (typentry->hash_opf != InvalidOid &&
     504           1 :             (!OidIsValid(typentry->eq_opr) ||
     505           0 :              typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
     506             :                                                      typentry->hash_opintype,
     507             :                                                      typentry->hash_opintype,
     508             :                                                      HTEqualStrategyNumber)))
     509           1 :             hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
     510             :                                                    typentry->hash_opintype,
     511             :                                                    typentry->hash_opintype,
     512             :                                                    HASHEXTENDED_PROC);
     513             : 
     514             :         /*
     515             :          * As above, make sure hash_array_extended will succeed.  We don't
     516             :          * currently support hashing for composite types, but when we do,
     517             :          * we'll need more logic here to check that case too.
     518             :          */
     519           1 :         if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
     520           0 :             !array_element_has_hashing(typentry))
     521           0 :             hash_extended_proc = InvalidOid;
     522             : 
     523             :         /* Force update of hash_proc_finfo only if we're changing state */
     524           1 :         if (typentry->hash_extended_proc != hash_extended_proc)
     525           1 :             typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
     526             : 
     527           1 :         typentry->hash_extended_proc = hash_extended_proc;
     528           1 :         typentry->flags |= TCFLAGS_CHECKED_HASH_EXTENDED_PROC;
     529             :     }
     530             : 
     531             :     /*
     532             :      * Set up fmgr lookup info as requested
     533             :      *
     534             :      * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
     535             :      * which is not quite right (they're really in the hash table's private
     536             :      * memory context) but this will do for our purposes.
     537             :      *
     538             :      * Note: the code above avoids invalidating the finfo structs unless the
     539             :      * referenced operator/function OID actually changes.  This is to prevent
     540             :      * unnecessary leakage of any subsidiary data attached to an finfo, since
     541             :      * that would cause session-lifespan memory leaks.
     542             :      */
     543       22786 :     if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
     544         193 :         typentry->eq_opr_finfo.fn_oid == InvalidOid &&
     545          26 :         typentry->eq_opr != InvalidOid)
     546             :     {
     547             :         Oid         eq_opr_func;
     548             : 
     549          26 :         eq_opr_func = get_opcode(typentry->eq_opr);
     550          26 :         if (eq_opr_func != InvalidOid)
     551          26 :             fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
     552             :                           CacheMemoryContext);
     553             :     }
     554       22907 :     if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
     555         334 :         typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
     556          46 :         typentry->cmp_proc != InvalidOid)
     557             :     {
     558          28 :         fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
     559             :                       CacheMemoryContext);
     560             :     }
     561       22683 :     if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
     562          81 :         typentry->hash_proc_finfo.fn_oid == InvalidOid &&
     563          17 :         typentry->hash_proc != InvalidOid)
     564             :     {
     565          17 :         fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
     566             :                       CacheMemoryContext);
     567             :     }
     568       22621 :     if ((flags & TYPECACHE_HASH_EXTENDED_PROC_FINFO) &&
     569           3 :         typentry->hash_extended_proc_finfo.fn_oid == InvalidOid &&
     570           1 :         typentry->hash_extended_proc != InvalidOid)
     571             :     {
     572           1 :         fmgr_info_cxt(typentry->hash_extended_proc,
     573             :                       &typentry->hash_extended_proc_finfo,
     574             :                       CacheMemoryContext);
     575             :     }
     576             : 
     577             :     /*
     578             :      * If it's a composite type (row type), get tupdesc if requested
     579             :      */
     580       26733 :     if ((flags & TYPECACHE_TUPDESC) &&
     581        4258 :         typentry->tupDesc == NULL &&
     582         144 :         typentry->typtype == TYPTYPE_COMPOSITE)
     583             :     {
     584         144 :         load_typcache_tupdesc(typentry);
     585             :     }
     586             : 
     587             :     /*
     588             :      * If requested, get information about a range type
     589             :      */
     590       23499 :     if ((flags & TYPECACHE_RANGE_INFO) &&
     591         897 :         typentry->rngelemtype == NULL &&
     592          17 :         typentry->typtype == TYPTYPE_RANGE)
     593             :     {
     594          17 :         load_rangetype_info(typentry);
     595             :     }
     596             : 
     597             :     /*
     598             :      * If requested, get information about a domain type
     599             :      */
     600       24212 :     if ((flags & TYPECACHE_DOMAIN_INFO) &&
     601        1978 :         (typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
     602         385 :         typentry->typtype == TYPTYPE_DOMAIN)
     603             :     {
     604         256 :         load_domaintype_info(typentry);
     605             :     }
     606             : 
     607       22619 :     return typentry;
     608             : }
     609             : 
     610             : /*
     611             :  * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
     612             :  */
     613             : static void
     614         145 : load_typcache_tupdesc(TypeCacheEntry *typentry)
     615             : {
     616             :     Relation    rel;
     617             : 
     618         145 :     if (!OidIsValid(typentry->typrelid)) /* should not happen */
     619           0 :         elog(ERROR, "invalid typrelid for composite type %u",
     620             :              typentry->type_id);
     621         145 :     rel = relation_open(typentry->typrelid, AccessShareLock);
     622         145 :     Assert(rel->rd_rel->reltype == typentry->type_id);
     623             : 
     624             :     /*
     625             :      * Link to the tupdesc and increment its refcount (we assert it's a
     626             :      * refcounted descriptor).  We don't use IncrTupleDescRefCount() for this,
     627             :      * because the reference mustn't be entered in the current resource owner;
     628             :      * it can outlive the current query.
     629             :      */
     630         145 :     typentry->tupDesc = RelationGetDescr(rel);
     631             : 
     632         145 :     Assert(typentry->tupDesc->tdrefcount > 0);
     633         145 :     typentry->tupDesc->tdrefcount++;
     634             : 
     635         145 :     relation_close(rel, AccessShareLock);
     636         145 : }
     637             : 
     638             : /*
     639             :  * load_rangetype_info --- helper routine to set up range type information
     640             :  */
     641             : static void
     642          17 : load_rangetype_info(TypeCacheEntry *typentry)
     643             : {
     644             :     Form_pg_range pg_range;
     645             :     HeapTuple   tup;
     646             :     Oid         subtypeOid;
     647             :     Oid         opclassOid;
     648             :     Oid         canonicalOid;
     649             :     Oid         subdiffOid;
     650             :     Oid         opfamilyOid;
     651             :     Oid         opcintype;
     652             :     Oid         cmpFnOid;
     653             : 
     654             :     /* get information from pg_range */
     655          17 :     tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(typentry->type_id));
     656             :     /* should not fail, since we already checked typtype ... */
     657          17 :     if (!HeapTupleIsValid(tup))
     658           0 :         elog(ERROR, "cache lookup failed for range type %u",
     659             :              typentry->type_id);
     660          17 :     pg_range = (Form_pg_range) GETSTRUCT(tup);
     661             : 
     662          17 :     subtypeOid = pg_range->rngsubtype;
     663          17 :     typentry->rng_collation = pg_range->rngcollation;
     664          17 :     opclassOid = pg_range->rngsubopc;
     665          17 :     canonicalOid = pg_range->rngcanonical;
     666          17 :     subdiffOid = pg_range->rngsubdiff;
     667             : 
     668          17 :     ReleaseSysCache(tup);
     669             : 
     670             :     /* get opclass properties and look up the comparison function */
     671          17 :     opfamilyOid = get_opclass_family(opclassOid);
     672          17 :     opcintype = get_opclass_input_type(opclassOid);
     673             : 
     674          17 :     cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
     675             :                                  BTORDER_PROC);
     676          17 :     if (!RegProcedureIsValid(cmpFnOid))
     677           0 :         elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
     678             :              BTORDER_PROC, opcintype, opcintype, opfamilyOid);
     679             : 
     680             :     /* set up cached fmgrinfo structs */
     681          17 :     fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo,
     682             :                   CacheMemoryContext);
     683          17 :     if (OidIsValid(canonicalOid))
     684           6 :         fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo,
     685             :                       CacheMemoryContext);
     686          17 :     if (OidIsValid(subdiffOid))
     687          10 :         fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo,
     688             :                       CacheMemoryContext);
     689             : 
     690             :     /* Lastly, set up link to the element type --- this marks data valid */
     691          17 :     typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
     692          17 : }
     693             : 
     694             : 
     695             : /*
     696             :  * load_domaintype_info --- helper routine to set up domain constraint info
     697             :  *
     698             :  * Note: we assume we're called in a relatively short-lived context, so it's
     699             :  * okay to leak data into the current context while scanning pg_constraint.
     700             :  * We build the new DomainConstraintCache data in a context underneath
     701             :  * CurrentMemoryContext, and reparent it under CacheMemoryContext when
     702             :  * complete.
     703             :  */
     704             : static void
     705         256 : load_domaintype_info(TypeCacheEntry *typentry)
     706             : {
     707         256 :     Oid         typeOid = typentry->type_id;
     708             :     DomainConstraintCache *dcc;
     709         256 :     bool        notNull = false;
     710             :     DomainConstraintState **ccons;
     711             :     int         cconslen;
     712             :     Relation    conRel;
     713             :     MemoryContext oldcxt;
     714             : 
     715             :     /*
     716             :      * If we're here, any existing constraint info is stale, so release it.
     717             :      * For safety, be sure to null the link before trying to delete the data.
     718             :      */
     719         256 :     if (typentry->domainData)
     720             :     {
     721          92 :         dcc = typentry->domainData;
     722          92 :         typentry->domainData = NULL;
     723          92 :         decr_dcc_refcount(dcc);
     724             :     }
     725             : 
     726             :     /*
     727             :      * We try to optimize the common case of no domain constraints, so don't
     728             :      * create the dcc object and context until we find a constraint.  Likewise
     729             :      * for the temp sorting array.
     730             :      */
     731         256 :     dcc = NULL;
     732         256 :     ccons = NULL;
     733         256 :     cconslen = 0;
     734             : 
     735             :     /*
     736             :      * Scan pg_constraint for relevant constraints.  We want to find
     737             :      * constraints for not just this domain, but any ancestor domains, so the
     738             :      * outer loop crawls up the domain stack.
     739             :      */
     740         256 :     conRel = heap_open(ConstraintRelationId, AccessShareLock);
     741             : 
     742             :     for (;;)
     743             :     {
     744             :         HeapTuple   tup;
     745             :         HeapTuple   conTup;
     746             :         Form_pg_type typTup;
     747         518 :         int         nccons = 0;
     748             :         ScanKeyData key[1];
     749             :         SysScanDesc scan;
     750             : 
     751         518 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
     752         518 :         if (!HeapTupleIsValid(tup))
     753           0 :             elog(ERROR, "cache lookup failed for type %u", typeOid);
     754         518 :         typTup = (Form_pg_type) GETSTRUCT(tup);
     755             : 
     756         518 :         if (typTup->typtype != TYPTYPE_DOMAIN)
     757             :         {
     758             :             /* Not a domain, so done */
     759         256 :             ReleaseSysCache(tup);
     760         256 :             break;
     761             :         }
     762             : 
     763             :         /* Test for NOT NULL Constraint */
     764         262 :         if (typTup->typnotnull)
     765          12 :             notNull = true;
     766             : 
     767             :         /* Look for CHECK Constraints on this domain */
     768         262 :         ScanKeyInit(&key[0],
     769             :                     Anum_pg_constraint_contypid,
     770             :                     BTEqualStrategyNumber, F_OIDEQ,
     771             :                     ObjectIdGetDatum(typeOid));
     772             : 
     773         262 :         scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
     774             :                                   NULL, 1, key);
     775             : 
     776         646 :         while (HeapTupleIsValid(conTup = systable_getnext(scan)))
     777             :         {
     778         122 :             Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
     779             :             Datum       val;
     780             :             bool        isNull;
     781             :             char       *constring;
     782             :             Expr       *check_expr;
     783             :             DomainConstraintState *r;
     784             : 
     785             :             /* Ignore non-CHECK constraints (presently, shouldn't be any) */
     786         122 :             if (c->contype != CONSTRAINT_CHECK)
     787           0 :                 continue;
     788             : 
     789             :             /* Not expecting conbin to be NULL, but we'll test for it anyway */
     790         122 :             val = fastgetattr(conTup, Anum_pg_constraint_conbin,
     791             :                               conRel->rd_att, &isNull);
     792         122 :             if (isNull)
     793           0 :                 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
     794             :                      NameStr(typTup->typname), NameStr(c->conname));
     795             : 
     796             :             /* Convert conbin to C string in caller context */
     797         122 :             constring = TextDatumGetCString(val);
     798             : 
     799             :             /* Create the DomainConstraintCache object and context if needed */
     800         122 :             if (dcc == NULL)
     801             :             {
     802             :                 MemoryContext cxt;
     803             : 
     804         118 :                 cxt = AllocSetContextCreate(CurrentMemoryContext,
     805             :                                             "Domain constraints",
     806             :                                             ALLOCSET_SMALL_SIZES);
     807         118 :                 dcc = (DomainConstraintCache *)
     808             :                     MemoryContextAlloc(cxt, sizeof(DomainConstraintCache));
     809         118 :                 dcc->constraints = NIL;
     810         118 :                 dcc->dccContext = cxt;
     811         118 :                 dcc->dccRefCount = 0;
     812             :             }
     813             : 
     814             :             /* Create node trees in DomainConstraintCache's context */
     815         122 :             oldcxt = MemoryContextSwitchTo(dcc->dccContext);
     816             : 
     817         122 :             check_expr = (Expr *) stringToNode(constring);
     818             : 
     819             :             /* ExecInitExpr will assume we've planned the expression */
     820         122 :             check_expr = expression_planner(check_expr);
     821             : 
     822         122 :             r = makeNode(DomainConstraintState);
     823         122 :             r->constrainttype = DOM_CONSTRAINT_CHECK;
     824         122 :             r->name = pstrdup(NameStr(c->conname));
     825         122 :             r->check_expr = check_expr;
     826         122 :             r->check_exprstate = NULL;
     827             : 
     828         122 :             MemoryContextSwitchTo(oldcxt);
     829             : 
     830             :             /* Accumulate constraints in an array, for sorting below */
     831         122 :             if (ccons == NULL)
     832             :             {
     833         118 :                 cconslen = 8;
     834         118 :                 ccons = (DomainConstraintState **)
     835         118 :                     palloc(cconslen * sizeof(DomainConstraintState *));
     836             :             }
     837           4 :             else if (nccons >= cconslen)
     838             :             {
     839           0 :                 cconslen *= 2;
     840           0 :                 ccons = (DomainConstraintState **)
     841           0 :                     repalloc(ccons, cconslen * sizeof(DomainConstraintState *));
     842             :             }
     843         122 :             ccons[nccons++] = r;
     844             :         }
     845             : 
     846         262 :         systable_endscan(scan);
     847             : 
     848         262 :         if (nccons > 0)
     849             :         {
     850             :             /*
     851             :              * Sort the items for this domain, so that CHECKs are applied in a
     852             :              * deterministic order.
     853             :              */
     854         121 :             if (nccons > 1)
     855           1 :                 qsort(ccons, nccons, sizeof(DomainConstraintState *), dcs_cmp);
     856             : 
     857             :             /*
     858             :              * Now attach them to the overall list.  Use lcons() here because
     859             :              * constraints of parent domains should be applied earlier.
     860             :              */
     861         121 :             oldcxt = MemoryContextSwitchTo(dcc->dccContext);
     862         364 :             while (nccons > 0)
     863         122 :                 dcc->constraints = lcons(ccons[--nccons], dcc->constraints);
     864         121 :             MemoryContextSwitchTo(oldcxt);
     865             :         }
     866             : 
     867             :         /* loop to next domain in stack */
     868         262 :         typeOid = typTup->typbasetype;
     869         262 :         ReleaseSysCache(tup);
     870         262 :     }
     871             : 
     872         256 :     heap_close(conRel, AccessShareLock);
     873             : 
     874             :     /*
     875             :      * Only need to add one NOT NULL check regardless of how many domains in
     876             :      * the stack request it.
     877             :      */
     878         256 :     if (notNull)
     879             :     {
     880             :         DomainConstraintState *r;
     881             : 
     882             :         /* Create the DomainConstraintCache object and context if needed */
     883          12 :         if (dcc == NULL)
     884             :         {
     885             :             MemoryContext cxt;
     886             : 
     887           8 :             cxt = AllocSetContextCreate(CurrentMemoryContext,
     888             :                                         "Domain constraints",
     889             :                                         ALLOCSET_SMALL_SIZES);
     890           8 :             dcc = (DomainConstraintCache *)
     891             :                 MemoryContextAlloc(cxt, sizeof(DomainConstraintCache));
     892           8 :             dcc->constraints = NIL;
     893           8 :             dcc->dccContext = cxt;
     894           8 :             dcc->dccRefCount = 0;
     895             :         }
     896             : 
     897             :         /* Create node trees in DomainConstraintCache's context */
     898          12 :         oldcxt = MemoryContextSwitchTo(dcc->dccContext);
     899             : 
     900          12 :         r = makeNode(DomainConstraintState);
     901             : 
     902          12 :         r->constrainttype = DOM_CONSTRAINT_NOTNULL;
     903          12 :         r->name = pstrdup("NOT NULL");
     904          12 :         r->check_expr = NULL;
     905          12 :         r->check_exprstate = NULL;
     906             : 
     907             :         /* lcons to apply the nullness check FIRST */
     908          12 :         dcc->constraints = lcons(r, dcc->constraints);
     909             : 
     910          12 :         MemoryContextSwitchTo(oldcxt);
     911             :     }
     912             : 
     913             :     /*
     914             :      * If we made a constraint object, move it into CacheMemoryContext and
     915             :      * attach it to the typcache entry.
     916             :      */
     917         256 :     if (dcc)
     918             :     {
     919         126 :         MemoryContextSetParent(dcc->dccContext, CacheMemoryContext);
     920         126 :         typentry->domainData = dcc;
     921         126 :         dcc->dccRefCount++;      /* count the typcache's reference */
     922             :     }
     923             : 
     924             :     /* Either way, the typcache entry's domain data is now valid. */
     925         256 :     typentry->flags |= TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS;
     926         256 : }
     927             : 
     928             : /*
     929             :  * qsort comparator to sort DomainConstraintState pointers by name
     930             :  */
     931             : static int
     932           1 : dcs_cmp(const void *a, const void *b)
     933             : {
     934           1 :     const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
     935           1 :     const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
     936             : 
     937           1 :     return strcmp((*ca)->name, (*cb)->name);
     938             : }
     939             : 
     940             : /*
     941             :  * decr_dcc_refcount --- decrement a DomainConstraintCache's refcount,
     942             :  * and free it if no references remain
     943             :  */
     944             : static void
     945         764 : decr_dcc_refcount(DomainConstraintCache *dcc)
     946             : {
     947         764 :     Assert(dcc->dccRefCount > 0);
     948         764 :     if (--(dcc->dccRefCount) <= 0)
     949          92 :         MemoryContextDelete(dcc->dccContext);
     950         764 : }
     951             : 
     952             : /*
     953             :  * Context reset/delete callback for a DomainConstraintRef
     954             :  */
     955             : static void
     956        1461 : dccref_deletion_callback(void *arg)
     957             : {
     958        1461 :     DomainConstraintRef *ref = (DomainConstraintRef *) arg;
     959        1461 :     DomainConstraintCache *dcc = ref->dcc;
     960             : 
     961             :     /* Paranoia --- be sure link is nulled before trying to release */
     962        1461 :     if (dcc)
     963             :     {
     964         672 :         ref->constraints = NIL;
     965         672 :         ref->dcc = NULL;
     966         672 :         decr_dcc_refcount(dcc);
     967             :     }
     968        1461 : }
     969             : 
     970             : /*
     971             :  * prep_domain_constraints --- prepare domain constraints for execution
     972             :  *
     973             :  * The expression trees stored in the DomainConstraintCache's list are
     974             :  * converted to executable expression state trees stored in execctx.
     975             :  */
     976             : static List *
     977         332 : prep_domain_constraints(List *constraints, MemoryContext execctx)
     978             : {
     979         332 :     List       *result = NIL;
     980             :     MemoryContext oldcxt;
     981             :     ListCell   *lc;
     982             : 
     983         332 :     oldcxt = MemoryContextSwitchTo(execctx);
     984             : 
     985         668 :     foreach(lc, constraints)
     986             :     {
     987         336 :         DomainConstraintState *r = (DomainConstraintState *) lfirst(lc);
     988             :         DomainConstraintState *newr;
     989             : 
     990         336 :         newr = makeNode(DomainConstraintState);
     991         336 :         newr->constrainttype = r->constrainttype;
     992         336 :         newr->name = r->name;
     993         336 :         newr->check_expr = r->check_expr;
     994         336 :         newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
     995             : 
     996         336 :         result = lappend(result, newr);
     997             :     }
     998             : 
     999         332 :     MemoryContextSwitchTo(oldcxt);
    1000             : 
    1001         332 :     return result;
    1002             : }
    1003             : 
    1004             : /*
    1005             :  * InitDomainConstraintRef --- initialize a DomainConstraintRef struct
    1006             :  *
    1007             :  * Caller must tell us the MemoryContext in which the DomainConstraintRef
    1008             :  * lives.  The ref will be cleaned up when that context is reset/deleted.
    1009             :  *
    1010             :  * Caller must also tell us whether it wants check_exprstate fields to be
    1011             :  * computed in the DomainConstraintState nodes attached to this ref.
    1012             :  * If it doesn't, we need not make a copy of the DomainConstraintState list.
    1013             :  */
    1014             : void
    1015        1461 : InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
    1016             :                         MemoryContext refctx, bool need_exprstate)
    1017             : {
    1018             :     /* Look up the typcache entry --- we assume it survives indefinitely */
    1019        1461 :     ref->tcache = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
    1020        1461 :     ref->need_exprstate = need_exprstate;
    1021             :     /* For safety, establish the callback before acquiring a refcount */
    1022        1461 :     ref->refctx = refctx;
    1023        1461 :     ref->dcc = NULL;
    1024        1461 :     ref->callback.func = dccref_deletion_callback;
    1025        1461 :     ref->callback.arg = (void *) ref;
    1026        1461 :     MemoryContextRegisterResetCallback(refctx, &ref->callback);
    1027             :     /* Acquire refcount if there are constraints, and set up exported list */
    1028        1461 :     if (ref->tcache->domainData)
    1029             :     {
    1030         672 :         ref->dcc = ref->tcache->domainData;
    1031         672 :         ref->dcc->dccRefCount++;
    1032         672 :         if (ref->need_exprstate)
    1033         332 :             ref->constraints = prep_domain_constraints(ref->dcc->constraints,
    1034             :                                                        ref->refctx);
    1035             :         else
    1036         340 :             ref->constraints = ref->dcc->constraints;
    1037             :     }
    1038             :     else
    1039         789 :         ref->constraints = NIL;
    1040        1461 : }
    1041             : 
    1042             : /*
    1043             :  * UpdateDomainConstraintRef --- recheck validity of domain constraint info
    1044             :  *
    1045             :  * If the domain's constraint set changed, ref->constraints is updated to
    1046             :  * point at a new list of cached constraints.
    1047             :  *
    1048             :  * In the normal case where nothing happened to the domain, this is cheap
    1049             :  * enough that it's reasonable (and expected) to check before *each* use
    1050             :  * of the constraint info.
    1051             :  */
    1052             : void
    1053        4529 : UpdateDomainConstraintRef(DomainConstraintRef *ref)
    1054             : {
    1055        4529 :     TypeCacheEntry *typentry = ref->tcache;
    1056             : 
    1057             :     /* Make sure typcache entry's data is up to date */
    1058        4529 :     if ((typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
    1059           0 :         typentry->typtype == TYPTYPE_DOMAIN)
    1060           0 :         load_domaintype_info(typentry);
    1061             : 
    1062             :     /* Transfer to ref object if there's new info, adjusting refcounts */
    1063        4529 :     if (ref->dcc != typentry->domainData)
    1064             :     {
    1065             :         /* Paranoia --- be sure link is nulled before trying to release */
    1066           0 :         DomainConstraintCache *dcc = ref->dcc;
    1067             : 
    1068           0 :         if (dcc)
    1069             :         {
    1070             :             /*
    1071             :              * Note: we just leak the previous list of executable domain
    1072             :              * constraints.  Alternatively, we could keep those in a child
    1073             :              * context of ref->refctx and free that context at this point.
    1074             :              * However, in practice this code path will be taken so seldom
    1075             :              * that the extra bookkeeping for a child context doesn't seem
    1076             :              * worthwhile; we'll just allow a leak for the lifespan of refctx.
    1077             :              */
    1078           0 :             ref->constraints = NIL;
    1079           0 :             ref->dcc = NULL;
    1080           0 :             decr_dcc_refcount(dcc);
    1081             :         }
    1082           0 :         dcc = typentry->domainData;
    1083           0 :         if (dcc)
    1084             :         {
    1085           0 :             ref->dcc = dcc;
    1086           0 :             dcc->dccRefCount++;
    1087           0 :             if (ref->need_exprstate)
    1088           0 :                 ref->constraints = prep_domain_constraints(dcc->constraints,
    1089             :                                                            ref->refctx);
    1090             :             else
    1091           0 :                 ref->constraints = dcc->constraints;
    1092             :         }
    1093             :     }
    1094        4529 : }
    1095             : 
    1096             : /*
    1097             :  * DomainHasConstraints --- utility routine to check if a domain has constraints
    1098             :  *
    1099             :  * This is defined to return false, not fail, if type is not a domain.
    1100             :  */
    1101             : bool
    1102         132 : DomainHasConstraints(Oid type_id)
    1103             : {
    1104             :     TypeCacheEntry *typentry;
    1105             : 
    1106             :     /*
    1107             :      * Note: a side effect is to cause the typcache's domain data to become
    1108             :      * valid.  This is fine since we'll likely need it soon if there is any.
    1109             :      */
    1110         132 :     typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
    1111             : 
    1112         132 :     return (typentry->domainData != NULL);
    1113             : }
    1114             : 
    1115             : 
    1116             : /*
    1117             :  * array_element_has_equality and friends are helper routines to check
    1118             :  * whether we should believe that array_eq and related functions will work
    1119             :  * on the given array type or composite type.
    1120             :  *
    1121             :  * The logic above may call these repeatedly on the same type entry, so we
    1122             :  * make use of the typentry->flags field to cache the results once known.
    1123             :  * Also, we assume that we'll probably want all these facts about the type
    1124             :  * if we want any, so we cache them all using only one lookup of the
    1125             :  * component datatype(s).
    1126             :  */
    1127             : 
    1128             : static bool
    1129          19 : array_element_has_equality(TypeCacheEntry *typentry)
    1130             : {
    1131          19 :     if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
    1132          18 :         cache_array_element_properties(typentry);
    1133          19 :     return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
    1134             : }
    1135             : 
    1136             : static bool
    1137          43 : array_element_has_compare(TypeCacheEntry *typentry)
    1138             : {
    1139          43 :     if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
    1140           5 :         cache_array_element_properties(typentry);
    1141          43 :     return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
    1142             : }
    1143             : 
    1144             : static bool
    1145           2 : array_element_has_hashing(TypeCacheEntry *typentry)
    1146             : {
    1147           2 :     if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
    1148           0 :         cache_array_element_properties(typentry);
    1149           2 :     return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
    1150             : }
    1151             : 
    1152             : static void
    1153          23 : cache_array_element_properties(TypeCacheEntry *typentry)
    1154             : {
    1155          23 :     Oid         elem_type = get_base_element_type(typentry->type_id);
    1156             : 
    1157          23 :     if (OidIsValid(elem_type))
    1158             :     {
    1159             :         TypeCacheEntry *elementry;
    1160             : 
    1161          23 :         elementry = lookup_type_cache(elem_type,
    1162             :                                       TYPECACHE_EQ_OPR |
    1163             :                                       TYPECACHE_CMP_PROC |
    1164             :                                       TYPECACHE_HASH_PROC);
    1165          23 :         if (OidIsValid(elementry->eq_opr))
    1166          23 :             typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
    1167          23 :         if (OidIsValid(elementry->cmp_proc))
    1168          20 :             typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
    1169          23 :         if (OidIsValid(elementry->hash_proc))
    1170          22 :             typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
    1171             :     }
    1172          23 :     typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
    1173          23 : }
    1174             : 
    1175             : static bool
    1176           9 : record_fields_have_equality(TypeCacheEntry *typentry)
    1177             : {
    1178           9 :     if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
    1179           8 :         cache_record_field_properties(typentry);
    1180           9 :     return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
    1181             : }
    1182             : 
    1183             : static bool
    1184          16 : record_fields_have_compare(TypeCacheEntry *typentry)
    1185             : {
    1186          16 :     if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
    1187           3 :         cache_record_field_properties(typentry);
    1188          16 :     return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
    1189             : }
    1190             : 
    1191             : static void
    1192          11 : cache_record_field_properties(TypeCacheEntry *typentry)
    1193             : {
    1194             :     /*
    1195             :      * For type RECORD, we can't really tell what will work, since we don't
    1196             :      * have access here to the specific anonymous type.  Just assume that
    1197             :      * everything will (we may get a failure at runtime ...)
    1198             :      */
    1199          11 :     if (typentry->type_id == RECORDOID)
    1200           2 :         typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
    1201             :                             TCFLAGS_HAVE_FIELD_COMPARE);
    1202           9 :     else if (typentry->typtype == TYPTYPE_COMPOSITE)
    1203             :     {
    1204             :         TupleDesc   tupdesc;
    1205             :         int         newflags;
    1206             :         int         i;
    1207             : 
    1208             :         /* Fetch composite type's tupdesc if we don't have it already */
    1209           9 :         if (typentry->tupDesc == NULL)
    1210           1 :             load_typcache_tupdesc(typentry);
    1211           9 :         tupdesc = typentry->tupDesc;
    1212             : 
    1213             :         /* Must bump the refcount while we do additional catalog lookups */
    1214           9 :         IncrTupleDescRefCount(tupdesc);
    1215             : 
    1216             :         /* Have each property if all non-dropped fields have the property */
    1217           9 :         newflags = (TCFLAGS_HAVE_FIELD_EQUALITY |
    1218             :                     TCFLAGS_HAVE_FIELD_COMPARE);
    1219          24 :         for (i = 0; i < tupdesc->natts; i++)
    1220             :         {
    1221             :             TypeCacheEntry *fieldentry;
    1222          17 :             Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
    1223             : 
    1224          17 :             if (attr->attisdropped)
    1225           0 :                 continue;
    1226             : 
    1227          17 :             fieldentry = lookup_type_cache(attr->atttypid,
    1228             :                                            TYPECACHE_EQ_OPR |
    1229             :                                            TYPECACHE_CMP_PROC);
    1230          17 :             if (!OidIsValid(fieldentry->eq_opr))
    1231           2 :                 newflags &= ~TCFLAGS_HAVE_FIELD_EQUALITY;
    1232          17 :             if (!OidIsValid(fieldentry->cmp_proc))
    1233           2 :                 newflags &= ~TCFLAGS_HAVE_FIELD_COMPARE;
    1234             : 
    1235             :             /* We can drop out of the loop once we disprove all bits */
    1236          17 :             if (newflags == 0)
    1237           2 :                 break;
    1238             :         }
    1239           9 :         typentry->flags |= newflags;
    1240             : 
    1241           9 :         DecrTupleDescRefCount(tupdesc);
    1242             :     }
    1243          11 :     typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
    1244          11 : }
    1245             : 
    1246             : 
    1247             : /*
    1248             :  * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
    1249             :  *
    1250             :  * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
    1251             :  * hasn't had its refcount bumped.
    1252             :  */
    1253             : static TupleDesc
    1254        5573 : lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
    1255             : {
    1256        5573 :     if (type_id != RECORDOID)
    1257             :     {
    1258             :         /*
    1259             :          * It's a named composite type, so use the regular typcache.
    1260             :          */
    1261             :         TypeCacheEntry *typentry;
    1262             : 
    1263        4114 :         typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
    1264        4114 :         if (typentry->tupDesc == NULL && !noError)
    1265           0 :             ereport(ERROR,
    1266             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1267             :                      errmsg("type %s is not composite",
    1268             :                             format_type_be(type_id))));
    1269        4114 :         return typentry->tupDesc;
    1270             :     }
    1271             :     else
    1272             :     {
    1273             :         /*
    1274             :          * It's a transient record type, so look in our record-type table.
    1275             :          */
    1276        1459 :         if (typmod < 0 || typmod >= NextRecordTypmod)
    1277             :         {
    1278           0 :             if (!noError)
    1279           0 :                 ereport(ERROR,
    1280             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1281             :                          errmsg("record type has not been registered")));
    1282           0 :             return NULL;
    1283             :         }
    1284        1459 :         return RecordCacheArray[typmod];
    1285             :     }
    1286             : }
    1287             : 
    1288             : /*
    1289             :  * lookup_rowtype_tupdesc
    1290             :  *
    1291             :  * Given a typeid/typmod that should describe a known composite type,
    1292             :  * return the tuple descriptor for the type.  Will ereport on failure.
    1293             :  * (Use ereport because this is reachable with user-specified OIDs,
    1294             :  * for example from record_in().)
    1295             :  *
    1296             :  * Note: on success, we increment the refcount of the returned TupleDesc,
    1297             :  * and log the reference in CurrentResourceOwner.  Caller should call
    1298             :  * ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc.
    1299             :  */
    1300             : TupleDesc
    1301        3745 : lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
    1302             : {
    1303             :     TupleDesc   tupDesc;
    1304             : 
    1305        3745 :     tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
    1306        3745 :     IncrTupleDescRefCount(tupDesc);
    1307        3745 :     return tupDesc;
    1308             : }
    1309             : 
    1310             : /*
    1311             :  * lookup_rowtype_tupdesc_noerror
    1312             :  *
    1313             :  * As above, but if the type is not a known composite type and noError
    1314             :  * is true, returns NULL instead of ereport'ing.  (Note that if a bogus
    1315             :  * type_id is passed, you'll get an ereport anyway.)
    1316             :  */
    1317             : TupleDesc
    1318           0 : lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
    1319             : {
    1320             :     TupleDesc   tupDesc;
    1321             : 
    1322           0 :     tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
    1323           0 :     if (tupDesc != NULL)
    1324           0 :         IncrTupleDescRefCount(tupDesc);
    1325           0 :     return tupDesc;
    1326             : }
    1327             : 
    1328             : /*
    1329             :  * lookup_rowtype_tupdesc_copy
    1330             :  *
    1331             :  * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
    1332             :  * copied into the CurrentMemoryContext and is not reference-counted.
    1333             :  */
    1334             : TupleDesc
    1335        1828 : lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
    1336             : {
    1337             :     TupleDesc   tmp;
    1338             : 
    1339        1828 :     tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
    1340        1828 :     return CreateTupleDescCopyConstr(tmp);
    1341             : }
    1342             : 
    1343             : /*
    1344             :  * Hash function for the hash table of RecordCacheEntry.
    1345             :  */
    1346             : static uint32
    1347        4343 : record_type_typmod_hash(const void *data, size_t size)
    1348             : {
    1349        4343 :     RecordCacheEntry *entry = (RecordCacheEntry *) data;
    1350             : 
    1351        4343 :     return hashTupleDesc(entry->tupdesc);
    1352             : }
    1353             : 
    1354             : /*
    1355             :  * Match function for the hash table of RecordCacheEntry.
    1356             :  */
    1357             : static int
    1358        4277 : record_type_typmod_compare(const void *a, const void *b, size_t size)
    1359             : {
    1360        4277 :     RecordCacheEntry *left = (RecordCacheEntry *) a;
    1361        4277 :     RecordCacheEntry *right = (RecordCacheEntry *) b;
    1362             : 
    1363        4277 :     return equalTupleDescs(left->tupdesc, right->tupdesc) ? 0 : 1;
    1364             : }
    1365             : 
    1366             : /*
    1367             :  * assign_record_type_typmod
    1368             :  *
    1369             :  * Given a tuple descriptor for a RECORD type, find or create a cache entry
    1370             :  * for the type, and set the tupdesc's tdtypmod field to a value that will
    1371             :  * identify this cache entry to lookup_rowtype_tupdesc.
    1372             :  */
    1373             : void
    1374        4343 : assign_record_type_typmod(TupleDesc tupDesc)
    1375             : {
    1376             :     RecordCacheEntry *recentry;
    1377             :     TupleDesc   entDesc;
    1378             :     bool        found;
    1379             :     int32       newtypmod;
    1380             :     MemoryContext oldcxt;
    1381             : 
    1382        4343 :     Assert(tupDesc->tdtypeid == RECORDOID);
    1383             : 
    1384        4343 :     if (RecordCacheHash == NULL)
    1385             :     {
    1386             :         /* First time through: initialize the hash table */
    1387             :         HASHCTL     ctl;
    1388             : 
    1389          39 :         MemSet(&ctl, 0, sizeof(ctl));
    1390          39 :         ctl.keysize = sizeof(TupleDesc);    /* just the pointer */
    1391          39 :         ctl.entrysize = sizeof(RecordCacheEntry);
    1392          39 :         ctl.hash = record_type_typmod_hash;
    1393          39 :         ctl.match = record_type_typmod_compare;
    1394          39 :         RecordCacheHash = hash_create("Record information cache", 64,
    1395             :                                       &ctl,
    1396             :                                       HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
    1397             : 
    1398             :         /* Also make sure CacheMemoryContext exists */
    1399          39 :         if (!CacheMemoryContext)
    1400           0 :             CreateCacheMemoryContext();
    1401             :     }
    1402             : 
    1403             :     /* Find or create a hashtable entry for this tuple descriptor */
    1404        4343 :     recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
    1405             :                                                 (void *) &tupDesc,
    1406             :                                                 HASH_ENTER, &found);
    1407        4343 :     if (found && recentry->tupdesc != NULL)
    1408             :     {
    1409        4155 :         tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
    1410        8498 :         return;
    1411             :     }
    1412             : 
    1413             :     /* Not present, so need to manufacture an entry */
    1414         188 :     recentry->tupdesc = NULL;
    1415         188 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    1416             : 
    1417         188 :     if (RecordCacheArray == NULL)
    1418             :     {
    1419          39 :         RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
    1420          39 :         RecordCacheArrayLen = 64;
    1421             :     }
    1422         149 :     else if (NextRecordTypmod >= RecordCacheArrayLen)
    1423             :     {
    1424           0 :         int32       newlen = RecordCacheArrayLen * 2;
    1425             : 
    1426           0 :         RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
    1427             :                                                   newlen * sizeof(TupleDesc));
    1428           0 :         RecordCacheArrayLen = newlen;
    1429             :     }
    1430             : 
    1431             :     /* if fail in subrs, no damage except possibly some wasted memory... */
    1432         188 :     entDesc = CreateTupleDescCopy(tupDesc);
    1433         188 :     recentry->tupdesc = entDesc;
    1434             :     /* mark it as a reference-counted tupdesc */
    1435         188 :     entDesc->tdrefcount = 1;
    1436             :     /* now it's safe to advance NextRecordTypmod */
    1437         188 :     newtypmod = NextRecordTypmod++;
    1438         188 :     entDesc->tdtypmod = newtypmod;
    1439         188 :     RecordCacheArray[newtypmod] = entDesc;
    1440             : 
    1441             :     /* report to caller as well */
    1442         188 :     tupDesc->tdtypmod = newtypmod;
    1443             : 
    1444         188 :     MemoryContextSwitchTo(oldcxt);
    1445             : }
    1446             : 
    1447             : /*
    1448             :  * TypeCacheRelCallback
    1449             :  *      Relcache inval callback function
    1450             :  *
    1451             :  * Delete the cached tuple descriptor (if any) for the given rel's composite
    1452             :  * type, or for all composite types if relid == InvalidOid.  Also reset
    1453             :  * whatever info we have cached about the composite type's comparability.
    1454             :  *
    1455             :  * This is called when a relcache invalidation event occurs for the given
    1456             :  * relid.  We must scan the whole typcache hash since we don't know the
    1457             :  * type OID corresponding to the relid.  We could do a direct search if this
    1458             :  * were a syscache-flush callback on pg_type, but then we would need all
    1459             :  * ALTER-TABLE-like commands that could modify a rowtype to issue syscache
    1460             :  * invals against the rel's pg_type OID.  The extra SI signaling could very
    1461             :  * well cost more than we'd save, since in most usages there are not very
    1462             :  * many entries in a backend's typcache.  The risk of bugs-of-omission seems
    1463             :  * high, too.
    1464             :  *
    1465             :  * Another possibility, with only localized impact, is to maintain a second
    1466             :  * hashtable that indexes composite-type typcache entries by their typrelid.
    1467             :  * But it's still not clear it's worth the trouble.
    1468             :  */
    1469             : static void
    1470       57478 : TypeCacheRelCallback(Datum arg, Oid relid)
    1471             : {
    1472             :     HASH_SEQ_STATUS status;
    1473             :     TypeCacheEntry *typentry;
    1474             : 
    1475             :     /* TypeCacheHash must exist, else this callback wouldn't be registered */
    1476       57478 :     hash_seq_init(&status, TypeCacheHash);
    1477      655282 :     while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
    1478             :     {
    1479      540326 :         if (typentry->typtype != TYPTYPE_COMPOSITE)
    1480      454039 :             continue;           /* skip non-composites */
    1481             : 
    1482             :         /* Skip if no match, unless we're zapping all composite types */
    1483       86287 :         if (relid != typentry->typrelid && relid != InvalidOid)
    1484       85813 :             continue;
    1485             : 
    1486             :         /* Delete tupdesc if we have it */
    1487         474 :         if (typentry->tupDesc != NULL)
    1488             :         {
    1489             :             /*
    1490             :              * Release our refcount, and free the tupdesc if none remain.
    1491             :              * (Can't use DecrTupleDescRefCount because this reference is not
    1492             :              * logged in current resource owner.)
    1493             :              */
    1494         113 :             Assert(typentry->tupDesc->tdrefcount > 0);
    1495         113 :             if (--typentry->tupDesc->tdrefcount == 0)
    1496         110 :                 FreeTupleDesc(typentry->tupDesc);
    1497         113 :             typentry->tupDesc = NULL;
    1498             :         }
    1499             : 
    1500             :         /* Reset equality/comparison/hashing validity information */
    1501         474 :         typentry->flags = 0;
    1502             :     }
    1503       57478 : }
    1504             : 
    1505             : /*
    1506             :  * TypeCacheOpcCallback
    1507             :  *      Syscache inval callback function
    1508             :  *
    1509             :  * This is called when a syscache invalidation event occurs for any pg_opclass
    1510             :  * row.  In principle we could probably just invalidate data dependent on the
    1511             :  * particular opclass, but since updates on pg_opclass are rare in production
    1512             :  * it doesn't seem worth a lot of complication: we just mark all cached data
    1513             :  * invalid.
    1514             :  *
    1515             :  * Note that we don't bother watching for updates on pg_amop or pg_amproc.
    1516             :  * This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
    1517             :  * is not allowed to be used to add/drop the primary operators and functions
    1518             :  * of an opclass, only cross-type members of a family; and the latter sorts
    1519             :  * of members are not going to get cached here.
    1520             :  */
    1521             : static void
    1522          64 : TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue)
    1523             : {
    1524             :     HASH_SEQ_STATUS status;
    1525             :     TypeCacheEntry *typentry;
    1526             : 
    1527             :     /* TypeCacheHash must exist, else this callback wouldn't be registered */
    1528          64 :     hash_seq_init(&status, TypeCacheHash);
    1529         486 :     while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
    1530             :     {
    1531             :         /* Reset equality/comparison/hashing validity information */
    1532         358 :         typentry->flags = 0;
    1533             :     }
    1534          64 : }
    1535             : 
    1536             : /*
    1537             :  * TypeCacheConstrCallback
    1538             :  *      Syscache inval callback function
    1539             :  *
    1540             :  * This is called when a syscache invalidation event occurs for any
    1541             :  * pg_constraint or pg_type row.  We flush information about domain
    1542             :  * constraints when this happens.
    1543             :  *
    1544             :  * It's slightly annoying that we can't tell whether the inval event was for a
    1545             :  * domain constraint/type record or not; there's usually more update traffic
    1546             :  * for table constraints/types than domain constraints, so we'll do a lot of
    1547             :  * useless flushes.  Still, this is better than the old no-caching-at-all
    1548             :  * approach to domain constraints.
    1549             :  */
    1550             : static void
    1551       35076 : TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue)
    1552             : {
    1553             :     TypeCacheEntry *typentry;
    1554             : 
    1555             :     /*
    1556             :      * Because this is called very frequently, and typically very few of the
    1557             :      * typcache entries are for domains, we don't use hash_seq_search here.
    1558             :      * Instead we thread all the domain-type entries together so that we can
    1559             :      * visit them cheaply.
    1560             :      */
    1561      107654 :     for (typentry = firstDomainTypeEntry;
    1562             :          typentry != NULL;
    1563       37502 :          typentry = typentry->nextDomain)
    1564             :     {
    1565             :         /* Reset domain constraint validity information */
    1566       37502 :         typentry->flags &= ~TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS;
    1567             :     }
    1568       35076 : }
    1569             : 
    1570             : 
    1571             : /*
    1572             :  * Check if given OID is part of the subset that's sortable by comparisons
    1573             :  */
    1574             : static inline bool
    1575          17 : enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
    1576             : {
    1577             :     Oid         offset;
    1578             : 
    1579          17 :     if (arg < enumdata->bitmap_base)
    1580           0 :         return false;
    1581          17 :     offset = arg - enumdata->bitmap_base;
    1582          17 :     if (offset > (Oid) INT_MAX)
    1583           0 :         return false;
    1584          17 :     return bms_is_member((int) offset, enumdata->sorted_values);
    1585             : }
    1586             : 
    1587             : 
    1588             : /*
    1589             :  * compare_values_of_enum
    1590             :  *      Compare two members of an enum type.
    1591             :  *      Return <0, 0, or >0 according as arg1 <, =, or > arg2.
    1592             :  *
    1593             :  * Note: currently, the enumData cache is refreshed only if we are asked
    1594             :  * to compare an enum value that is not already in the cache.  This is okay
    1595             :  * because there is no support for re-ordering existing values, so comparisons
    1596             :  * of previously cached values will return the right answer even if other
    1597             :  * values have been added since we last loaded the cache.
    1598             :  *
    1599             :  * Note: the enum logic has a special-case rule about even-numbered versus
    1600             :  * odd-numbered OIDs, but we take no account of that rule here; this
    1601             :  * routine shouldn't even get called when that rule applies.
    1602             :  */
    1603             : int
    1604          11 : compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
    1605             : {
    1606             :     TypeCacheEnumData *enumdata;
    1607             :     EnumItem   *item1;
    1608             :     EnumItem   *item2;
    1609             : 
    1610             :     /*
    1611             :      * Equal OIDs are certainly equal --- this case was probably handled by
    1612             :      * our caller, but we may as well check.
    1613             :      */
    1614          11 :     if (arg1 == arg2)
    1615           0 :         return 0;
    1616             : 
    1617             :     /* Load up the cache if first time through */
    1618          11 :     if (tcache->enumData == NULL)
    1619           1 :         load_enum_cache_data(tcache);
    1620          11 :     enumdata = tcache->enumData;
    1621             : 
    1622             :     /*
    1623             :      * If both OIDs are known-sorted, we can just compare them directly.
    1624             :      */
    1625          17 :     if (enum_known_sorted(enumdata, arg1) &&
    1626           6 :         enum_known_sorted(enumdata, arg2))
    1627             :     {
    1628           0 :         if (arg1 < arg2)
    1629           0 :             return -1;
    1630             :         else
    1631           0 :             return 1;
    1632             :     }
    1633             : 
    1634             :     /*
    1635             :      * Slow path: we have to identify their actual sort-order positions.
    1636             :      */
    1637          11 :     item1 = find_enumitem(enumdata, arg1);
    1638          11 :     item2 = find_enumitem(enumdata, arg2);
    1639             : 
    1640          11 :     if (item1 == NULL || item2 == NULL)
    1641             :     {
    1642             :         /*
    1643             :          * We couldn't find one or both values.  That means the enum has
    1644             :          * changed under us, so re-initialize the cache and try again. We
    1645             :          * don't bother retrying the known-sorted case in this path.
    1646             :          */
    1647           0 :         load_enum_cache_data(tcache);
    1648           0 :         enumdata = tcache->enumData;
    1649             : 
    1650           0 :         item1 = find_enumitem(enumdata, arg1);
    1651           0 :         item2 = find_enumitem(enumdata, arg2);
    1652             : 
    1653             :         /*
    1654             :          * If we still can't find the values, complain: we must have corrupt
    1655             :          * data.
    1656             :          */
    1657           0 :         if (item1 == NULL)
    1658           0 :             elog(ERROR, "enum value %u not found in cache for enum %s",
    1659             :                  arg1, format_type_be(tcache->type_id));
    1660           0 :         if (item2 == NULL)
    1661           0 :             elog(ERROR, "enum value %u not found in cache for enum %s",
    1662             :                  arg2, format_type_be(tcache->type_id));
    1663             :     }
    1664             : 
    1665          11 :     if (item1->sort_order < item2->sort_order)
    1666           4 :         return -1;
    1667           7 :     else if (item1->sort_order > item2->sort_order)
    1668           7 :         return 1;
    1669             :     else
    1670           0 :         return 0;
    1671             : }
    1672             : 
    1673             : /*
    1674             :  * Load (or re-load) the enumData member of the typcache entry.
    1675             :  */
    1676             : static void
    1677           1 : load_enum_cache_data(TypeCacheEntry *tcache)
    1678             : {
    1679             :     TypeCacheEnumData *enumdata;
    1680             :     Relation    enum_rel;
    1681             :     SysScanDesc enum_scan;
    1682             :     HeapTuple   enum_tuple;
    1683             :     ScanKeyData skey;
    1684             :     EnumItem   *items;
    1685             :     int         numitems;
    1686             :     int         maxitems;
    1687             :     Oid         bitmap_base;
    1688             :     Bitmapset  *bitmap;
    1689             :     MemoryContext oldcxt;
    1690             :     int         bm_size,
    1691             :                 start_pos;
    1692             : 
    1693             :     /* Check that this is actually an enum */
    1694           1 :     if (tcache->typtype != TYPTYPE_ENUM)
    1695           0 :         ereport(ERROR,
    1696             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1697             :                  errmsg("%s is not an enum",
    1698             :                         format_type_be(tcache->type_id))));
    1699             : 
    1700             :     /*
    1701             :      * Read all the information for members of the enum type.  We collect the
    1702             :      * info in working memory in the caller's context, and then transfer it to
    1703             :      * permanent memory in CacheMemoryContext.  This minimizes the risk of
    1704             :      * leaking memory from CacheMemoryContext in the event of an error partway
    1705             :      * through.
    1706             :      */
    1707           1 :     maxitems = 64;
    1708           1 :     items = (EnumItem *) palloc(sizeof(EnumItem) * maxitems);
    1709           1 :     numitems = 0;
    1710             : 
    1711             :     /* Scan pg_enum for the members of the target enum type. */
    1712           1 :     ScanKeyInit(&skey,
    1713             :                 Anum_pg_enum_enumtypid,
    1714             :                 BTEqualStrategyNumber, F_OIDEQ,
    1715             :                 ObjectIdGetDatum(tcache->type_id));
    1716             : 
    1717           1 :     enum_rel = heap_open(EnumRelationId, AccessShareLock);
    1718           1 :     enum_scan = systable_beginscan(enum_rel,
    1719             :                                    EnumTypIdLabelIndexId,
    1720             :                                    true, NULL,
    1721             :                                    1, &skey);
    1722             : 
    1723          10 :     while (HeapTupleIsValid(enum_tuple = systable_getnext(enum_scan)))
    1724             :     {
    1725           8 :         Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enum_tuple);
    1726             : 
    1727           8 :         if (numitems >= maxitems)
    1728             :         {
    1729           0 :             maxitems *= 2;
    1730           0 :             items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
    1731             :         }
    1732           8 :         items[numitems].enum_oid = HeapTupleGetOid(enum_tuple);
    1733           8 :         items[numitems].sort_order = en->enumsortorder;
    1734           8 :         numitems++;
    1735             :     }
    1736             : 
    1737           1 :     systable_endscan(enum_scan);
    1738           1 :     heap_close(enum_rel, AccessShareLock);
    1739             : 
    1740             :     /* Sort the items into OID order */
    1741           1 :     qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);
    1742             : 
    1743             :     /*
    1744             :      * Here, we create a bitmap listing a subset of the enum's OIDs that are
    1745             :      * known to be in order and can thus be compared with just OID comparison.
    1746             :      *
    1747             :      * The point of this is that the enum's initial OIDs were certainly in
    1748             :      * order, so there is some subset that can be compared via OID comparison;
    1749             :      * and we'd rather not do binary searches unnecessarily.
    1750             :      *
    1751             :      * This is somewhat heuristic, and might identify a subset of OIDs that
    1752             :      * isn't exactly what the type started with.  That's okay as long as the
    1753             :      * subset is correctly sorted.
    1754             :      */
    1755           1 :     bitmap_base = InvalidOid;
    1756           1 :     bitmap = NULL;
    1757           1 :     bm_size = 1;                /* only save sets of at least 2 OIDs */
    1758             : 
    1759           3 :     for (start_pos = 0; start_pos < numitems - 1; start_pos++)
    1760             :     {
    1761             :         /*
    1762             :          * Identify longest sorted subsequence starting at start_pos
    1763             :          */
    1764           3 :         Bitmapset  *this_bitmap = bms_make_singleton(0);
    1765           3 :         int         this_bm_size = 1;
    1766           3 :         Oid         start_oid = items[start_pos].enum_oid;
    1767           3 :         float4      prev_order = items[start_pos].sort_order;
    1768             :         int         i;
    1769             : 
    1770          21 :         for (i = start_pos + 1; i < numitems; i++)
    1771             :         {
    1772             :             Oid         offset;
    1773             : 
    1774          18 :             offset = items[i].enum_oid - start_oid;
    1775             :             /* quit if bitmap would be too large; cutoff is arbitrary */
    1776          18 :             if (offset >= 8192)
    1777           0 :                 break;
    1778             :             /* include the item if it's in-order */
    1779          18 :             if (items[i].sort_order > prev_order)
    1780             :             {
    1781           9 :                 prev_order = items[i].sort_order;
    1782           9 :                 this_bitmap = bms_add_member(this_bitmap, (int) offset);
    1783           9 :                 this_bm_size++;
    1784             :             }
    1785             :         }
    1786             : 
    1787             :         /* Remember it if larger than previous best */
    1788           3 :         if (this_bm_size > bm_size)
    1789             :         {
    1790           1 :             bms_free(bitmap);
    1791           1 :             bitmap_base = start_oid;
    1792           1 :             bitmap = this_bitmap;
    1793           1 :             bm_size = this_bm_size;
    1794             :         }
    1795             :         else
    1796           2 :             bms_free(this_bitmap);
    1797             : 
    1798             :         /*
    1799             :          * Done if it's not possible to find a longer sequence in the rest of
    1800             :          * the list.  In typical cases this will happen on the first
    1801             :          * iteration, which is why we create the bitmaps on the fly instead of
    1802             :          * doing a second pass over the list.
    1803             :          */
    1804           3 :         if (bm_size >= (numitems - start_pos - 1))
    1805           1 :             break;
    1806             :     }
    1807             : 
    1808             :     /* OK, copy the data into CacheMemoryContext */
    1809           1 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    1810           1 :     enumdata = (TypeCacheEnumData *)
    1811           1 :         palloc(offsetof(TypeCacheEnumData, enum_values) +
    1812           1 :                numitems * sizeof(EnumItem));
    1813           1 :     enumdata->bitmap_base = bitmap_base;
    1814           1 :     enumdata->sorted_values = bms_copy(bitmap);
    1815           1 :     enumdata->num_values = numitems;
    1816           1 :     memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
    1817           1 :     MemoryContextSwitchTo(oldcxt);
    1818             : 
    1819           1 :     pfree(items);
    1820           1 :     bms_free(bitmap);
    1821             : 
    1822             :     /* And link the finished cache struct into the typcache */
    1823           1 :     if (tcache->enumData != NULL)
    1824           0 :         pfree(tcache->enumData);
    1825           1 :     tcache->enumData = enumdata;
    1826           1 : }
    1827             : 
    1828             : /*
    1829             :  * Locate the EnumItem with the given OID, if present
    1830             :  */
    1831             : static EnumItem *
    1832          22 : find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
    1833             : {
    1834             :     EnumItem    srch;
    1835             : 
    1836             :     /* On some versions of Solaris, bsearch of zero items dumps core */
    1837          22 :     if (enumdata->num_values <= 0)
    1838           0 :         return NULL;
    1839             : 
    1840          22 :     srch.enum_oid = arg;
    1841          22 :     return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
    1842             :                    sizeof(EnumItem), enum_oid_cmp);
    1843             : }
    1844             : 
    1845             : /*
    1846             :  * qsort comparison function for OID-ordered EnumItems
    1847             :  */
    1848             : static int
    1849          79 : enum_oid_cmp(const void *left, const void *right)
    1850             : {
    1851          79 :     const EnumItem *l = (const EnumItem *) left;
    1852          79 :     const EnumItem *r = (const EnumItem *) right;
    1853             : 
    1854          79 :     if (l->enum_oid < r->enum_oid)
    1855          29 :         return -1;
    1856          50 :     else if (l->enum_oid > r->enum_oid)
    1857          28 :         return 1;
    1858             :     else
    1859          22 :         return 0;
    1860             : }

Generated by: LCOV version 1.11