Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions

typcache.h File Reference

#include "access/tupdesc.h"
#include "fmgr.h"
Include dependency graph for typcache.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  TypeCacheEntry

Defines

#define TYPECACHE_EQ_OPR   0x0001
#define TYPECACHE_LT_OPR   0x0002
#define TYPECACHE_GT_OPR   0x0004
#define TYPECACHE_CMP_PROC   0x0008
#define TYPECACHE_HASH_PROC   0x0010
#define TYPECACHE_EQ_OPR_FINFO   0x0020
#define TYPECACHE_CMP_PROC_FINFO   0x0040
#define TYPECACHE_HASH_PROC_FINFO   0x0080
#define TYPECACHE_TUPDESC   0x0100
#define TYPECACHE_BTREE_OPFAMILY   0x0200
#define TYPECACHE_HASH_OPFAMILY   0x0400
#define TYPECACHE_RANGE_INFO   0x0800

Typedefs

typedef struct TypeCacheEntry TypeCacheEntry

Functions

TypeCacheEntrylookup_type_cache (Oid type_id, int flags)
TupleDesc lookup_rowtype_tupdesc (Oid type_id, int32 typmod)
TupleDesc lookup_rowtype_tupdesc_noerror (Oid type_id, int32 typmod, bool noError)
TupleDesc lookup_rowtype_tupdesc_copy (Oid type_id, int32 typmod)
void assign_record_type_typmod (TupleDesc tupDesc)
int compare_values_of_enum (TypeCacheEntry *tcache, Oid arg1, Oid arg2)

Define Documentation

#define TYPECACHE_BTREE_OPFAMILY   0x0200

Definition at line 107 of file typcache.h.

Referenced by lookup_type_cache().

#define TYPECACHE_CMP_PROC   0x0008
#define TYPECACHE_CMP_PROC_FINFO   0x0040
#define TYPECACHE_EQ_OPR   0x0001
#define TYPECACHE_EQ_OPR_FINFO   0x0020
#define TYPECACHE_GT_OPR   0x0004

Definition at line 100 of file typcache.h.

Referenced by get_rule_orderby(), get_sort_group_operators(), and lookup_type_cache().

#define TYPECACHE_HASH_OPFAMILY   0x0400

Definition at line 108 of file typcache.h.

Referenced by lookup_type_cache().

#define TYPECACHE_HASH_PROC   0x0010

Definition at line 102 of file typcache.h.

Referenced by cache_array_element_properties(), lookup_type_cache(), and op_hashjoinable().

#define TYPECACHE_HASH_PROC_FINFO   0x0080

Definition at line 105 of file typcache.h.

Referenced by array_typanalyze(), hash_array(), hash_range(), and lookup_type_cache().

#define TYPECACHE_LT_OPR   0x0002

Definition at line 99 of file typcache.h.

Referenced by get_rule_orderby(), get_sort_group_operators(), and lookup_type_cache().

#define TYPECACHE_RANGE_INFO   0x0800

Definition at line 109 of file typcache.h.

Referenced by get_range_io_data(), lookup_type_cache(), and range_get_typcache().

#define TYPECACHE_TUPDESC   0x0100

Definition at line 106 of file typcache.h.

Referenced by lookup_rowtype_tupdesc_internal(), and lookup_type_cache().


Typedef Documentation


Function Documentation

void assign_record_type_typmod ( TupleDesc  tupDesc  ) 

Definition at line 782 of file typcache.c.

References Assert, tupleDesc::attrs, CacheMemoryContext, CreateCacheMemoryContext(), CreateTupleDescCopy(), HASHCTL::entrysize, equalTupleDescs(), HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, hash_search(), i, HASHCTL::keysize, lcons(), lfirst, MemoryContextSwitchTo(), MemSet, tupleDesc::natts, NextRecordTypmod, NULL, palloc(), REC_HASH_KEYS, RecordCacheArrayLen, RECORDOID, repalloc(), tupleDesc::tdrefcount, tupleDesc::tdtypeid, tupleDesc::tdtypmod, and RecordCacheEntry::tupdescs.

Referenced by BlessTupleDesc(), ExecEvalWholeRowVar(), internal_get_result_type(), and SPI_returntuple().

{
    RecordCacheEntry *recentry;
    TupleDesc   entDesc;
    Oid         hashkey[REC_HASH_KEYS];
    bool        found;
    int         i;
    ListCell   *l;
    int32       newtypmod;
    MemoryContext oldcxt;

    Assert(tupDesc->tdtypeid == RECORDOID);

    if (RecordCacheHash == NULL)
    {
        /* First time through: initialize the hash table */
        HASHCTL     ctl;

        MemSet(&ctl, 0, sizeof(ctl));
        ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
        ctl.entrysize = sizeof(RecordCacheEntry);
        ctl.hash = tag_hash;
        RecordCacheHash = hash_create("Record information cache", 64,
                                      &ctl, HASH_ELEM | HASH_FUNCTION);

        /* Also make sure CacheMemoryContext exists */
        if (!CacheMemoryContext)
            CreateCacheMemoryContext();
    }

    /* Find or create a hashtable entry for this hash class */
    MemSet(hashkey, 0, sizeof(hashkey));
    for (i = 0; i < tupDesc->natts; i++)
    {
        if (i >= REC_HASH_KEYS)
            break;
        hashkey[i] = tupDesc->attrs[i]->atttypid;
    }
    recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
                                                (void *) hashkey,
                                                HASH_ENTER, &found);
    if (!found)
    {
        /* New entry ... hash_search initialized only the hash key */
        recentry->tupdescs = NIL;
    }

    /* Look for existing record cache entry */
    foreach(l, recentry->tupdescs)
    {
        entDesc = (TupleDesc) lfirst(l);
        if (equalTupleDescs(tupDesc, entDesc))
        {
            tupDesc->tdtypmod = entDesc->tdtypmod;
            return;
        }
    }

    /* Not present, so need to manufacture an entry */
    oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

    if (RecordCacheArray == NULL)
    {
        RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
        RecordCacheArrayLen = 64;
    }
    else if (NextRecordTypmod >= RecordCacheArrayLen)
    {
        int32       newlen = RecordCacheArrayLen * 2;

        RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
                                                  newlen * sizeof(TupleDesc));
        RecordCacheArrayLen = newlen;
    }

    /* if fail in subrs, no damage except possibly some wasted memory... */
    entDesc = CreateTupleDescCopy(tupDesc);
    recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
    /* mark it as a reference-counted tupdesc */
    entDesc->tdrefcount = 1;
    /* now it's safe to advance NextRecordTypmod */
    newtypmod = NextRecordTypmod++;
    entDesc->tdtypmod = newtypmod;
    RecordCacheArray[newtypmod] = entDesc;

    /* report to caller as well */
    tupDesc->tdtypmod = newtypmod;

    MemoryContextSwitchTo(oldcxt);
}

int compare_values_of_enum ( TypeCacheEntry tcache,
Oid  arg1,
Oid  arg2 
)

Definition at line 973 of file typcache.c.

References elog, enum_known_sorted(), TypeCacheEntry::enumData, ERROR, find_enumitem(), format_type_be(), load_enum_cache_data(), NULL, EnumItem::sort_order, and TypeCacheEntry::type_id.

Referenced by enum_cmp_internal().

{
    TypeCacheEnumData *enumdata;
    EnumItem   *item1;
    EnumItem   *item2;

    /*
     * Equal OIDs are certainly equal --- this case was probably handled by
     * our caller, but we may as well check.
     */
    if (arg1 == arg2)
        return 0;

    /* Load up the cache if first time through */
    if (tcache->enumData == NULL)
        load_enum_cache_data(tcache);
    enumdata = tcache->enumData;

    /*
     * If both OIDs are known-sorted, we can just compare them directly.
     */
    if (enum_known_sorted(enumdata, arg1) &&
        enum_known_sorted(enumdata, arg2))
    {
        if (arg1 < arg2)
            return -1;
        else
            return 1;
    }

    /*
     * Slow path: we have to identify their actual sort-order positions.
     */
    item1 = find_enumitem(enumdata, arg1);
    item2 = find_enumitem(enumdata, arg2);

    if (item1 == NULL || item2 == NULL)
    {
        /*
         * We couldn't find one or both values.  That means the enum has
         * changed under us, so re-initialize the cache and try again. We
         * don't bother retrying the known-sorted case in this path.
         */
        load_enum_cache_data(tcache);
        enumdata = tcache->enumData;

        item1 = find_enumitem(enumdata, arg1);
        item2 = find_enumitem(enumdata, arg2);

        /*
         * If we still can't find the values, complain: we must have corrupt
         * data.
         */
        if (item1 == NULL)
            elog(ERROR, "enum value %u not found in cache for enum %s",
                 arg1, format_type_be(tcache->type_id));
        if (item2 == NULL)
            elog(ERROR, "enum value %u not found in cache for enum %s",
                 arg2, format_type_be(tcache->type_id));
    }

    if (item1->sort_order < item2->sort_order)
        return -1;
    else if (item1->sort_order > item2->sort_order)
        return 1;
    else
        return 0;
}

TupleDesc lookup_rowtype_tupdesc ( Oid  type_id,
int32  typmod 
)
TupleDesc lookup_rowtype_tupdesc_copy ( Oid  type_id,
int32  typmod 
)
TupleDesc lookup_rowtype_tupdesc_noerror ( Oid  type_id,
int32  typmod,
bool  noError 
)

Definition at line 748 of file typcache.c.

References IncrTupleDescRefCount(), lookup_rowtype_tupdesc_internal(), and NULL.

Referenced by plperl_sv_to_datum(), and toast_flatten_tuple_attribute().

{
    TupleDesc   tupDesc;

    tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
    if (tupDesc != NULL)
        IncrTupleDescRefCount(tupDesc);
    return tupDesc;
}

TypeCacheEntry* lookup_type_cache ( Oid  type_id,
int  flags 
)

Definition at line 152 of file typcache.c.

References array_element_has_compare(), array_element_has_equality(), array_element_has_hashing(), ARRAY_EQ_OP, ARRAY_GT_OP, ARRAY_LT_OP, Assert, BTEqualStrategyNumber, BTGreaterStrategyNumber, BTLessStrategyNumber, BTORDER_PROC, BTREE_AM_OID, TypeCacheEntry::btree_opf, TypeCacheEntry::btree_opintype, CacheMemoryContext, CacheRegisterRelcacheCallback(), TypeCacheEntry::cmp_proc, TypeCacheEntry::cmp_proc_finfo, CreateCacheMemoryContext(), elog, HASHCTL::entrysize, TypeCacheEntry::eq_opr, TypeCacheEntry::eq_opr_finfo, ereport, errcode(), errmsg(), ERROR, fmgr_info_cxt(), FmgrInfo::fn_oid, get_opclass_family(), get_opclass_input_type(), get_opcode(), get_opfamily_member(), get_opfamily_proc(), GetDefaultOpClass(), GETSTRUCT, TypeCacheEntry::gt_opr, HASHCTL::hash, HASH_AM_OID, hash_create(), HASH_ELEM, HASH_FUNCTION, TypeCacheEntry::hash_opf, TypeCacheEntry::hash_opintype, TypeCacheEntry::hash_proc, TypeCacheEntry::hash_proc_finfo, hash_search(), HASHPROC, HeapTupleIsValid, HTEqualStrategyNumber, InvalidOid, HASHCTL::keysize, load_rangetype_info(), load_typcache_tupdesc(), TypeCacheEntry::lt_opr, MemSet, NameStr, NULL, ObjectIdGetDatum, OidIsValid, RECORD_EQ_OP, record_fields_have_compare(), record_fields_have_equality(), RECORD_GT_OP, RECORD_LT_OP, ReleaseSysCache(), TypeCacheEntry::rngelemtype, SearchSysCache1, TypeCacheEntry::tupDesc, TypeCacheEntry::typalign, TypeCacheEntry::typbyval, TypeCacheEntry::type_id, TYPECACHE_BTREE_OPFAMILY, TYPECACHE_CMP_PROC, TYPECACHE_CMP_PROC_FINFO, TYPECACHE_EQ_OPR, TYPECACHE_EQ_OPR_FINFO, TYPECACHE_GT_OPR, TYPECACHE_HASH_OPFAMILY, TYPECACHE_HASH_PROC, TYPECACHE_HASH_PROC_FINFO, TYPECACHE_LT_OPR, TYPECACHE_RANGE_INFO, TYPECACHE_TUPDESC, TypeCacheRelCallback(), TYPEOID, TypeCacheEntry::typlen, TypeCacheEntry::typrelid, TypeCacheEntry::typstorage, TypeCacheEntry::typtype, TYPTYPE_COMPOSITE, and TYPTYPE_RANGE.

Referenced by array_cmp(), array_contain_compare(), array_eq(), array_replace_internal(), array_typanalyze(), cache_array_element_properties(), cache_record_field_properties(), calc_arraycontsel(), enum_cmp_internal(), ExecInitExpr(), get_range_io_data(), get_rule_orderby(), get_sort_group_operators(), hash_array(), hash_range(), load_rangetype_info(), lookup_rowtype_tupdesc_internal(), op_hashjoinable(), op_mergejoinable(), range_get_typcache(), record_cmp(), record_eq(), scalararraysel(), and scalararraysel_containment().

{
    TypeCacheEntry *typentry;
    bool        found;

    if (TypeCacheHash == NULL)
    {
        /* First time through: initialize the hash table */
        HASHCTL     ctl;

        MemSet(&ctl, 0, sizeof(ctl));
        ctl.keysize = sizeof(Oid);
        ctl.entrysize = sizeof(TypeCacheEntry);
        ctl.hash = oid_hash;
        TypeCacheHash = hash_create("Type information cache", 64,
                                    &ctl, HASH_ELEM | HASH_FUNCTION);

        /* Also set up a callback for relcache SI invalidations */
        CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0);

        /* Also make sure CacheMemoryContext exists */
        if (!CacheMemoryContext)
            CreateCacheMemoryContext();
    }

    /* Try to look up an existing entry */
    typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
                                              (void *) &type_id,
                                              HASH_FIND, NULL);
    if (typentry == NULL)
    {
        /*
         * If we didn't find one, we want to make one.  But first look up the
         * pg_type row, just to make sure we don't make a cache entry for an
         * invalid type OID.
         */
        HeapTuple   tp;
        Form_pg_type typtup;

        tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
        if (!HeapTupleIsValid(tp))
            elog(ERROR, "cache lookup failed for type %u", type_id);
        typtup = (Form_pg_type) GETSTRUCT(tp);
        if (!typtup->typisdefined)
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_OBJECT),
                     errmsg("type \"%s\" is only a shell",
                            NameStr(typtup->typname))));

        /* Now make the typcache entry */
        typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
                                                  (void *) &type_id,
                                                  HASH_ENTER, &found);
        Assert(!found);         /* it wasn't there a moment ago */

        MemSet(typentry, 0, sizeof(TypeCacheEntry));
        typentry->type_id = type_id;
        typentry->typlen = typtup->typlen;
        typentry->typbyval = typtup->typbyval;
        typentry->typalign = typtup->typalign;
        typentry->typstorage = typtup->typstorage;
        typentry->typtype = typtup->typtype;
        typentry->typrelid = typtup->typrelid;

        ReleaseSysCache(tp);
    }

    /*
     * If we haven't already found the opclasses, try to do so
     */
    if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
                  TYPECACHE_CMP_PROC |
                  TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO |
                  TYPECACHE_BTREE_OPFAMILY)) &&
        typentry->btree_opf == InvalidOid)
    {
        Oid         opclass;

        opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
        if (OidIsValid(opclass))
        {
            typentry->btree_opf = get_opclass_family(opclass);
            typentry->btree_opintype = get_opclass_input_type(opclass);
        }
        /* If no btree opclass, we force lookup of the hash opclass */
        if (typentry->btree_opf == InvalidOid)
        {
            if (typentry->hash_opf == InvalidOid)
            {
                opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
                if (OidIsValid(opclass))
                {
                    typentry->hash_opf = get_opclass_family(opclass);
                    typentry->hash_opintype = get_opclass_input_type(opclass);
                }
            }
        }
        else
        {
            /*
             * In case we find a btree opclass where previously we only found
             * a hash opclass, reset eq_opr and derived information so that we
             * can fetch the btree equality operator instead of the hash
             * equality operator.  (They're probably the same operator, but we
             * don't assume that here.)
             */
            typentry->eq_opr = InvalidOid;
            typentry->eq_opr_finfo.fn_oid = InvalidOid;
            typentry->hash_proc = InvalidOid;
            typentry->hash_proc_finfo.fn_oid = InvalidOid;
        }
    }

    if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO |
                  TYPECACHE_HASH_OPFAMILY)) &&
        typentry->hash_opf == InvalidOid)
    {
        Oid         opclass;

        opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
        if (OidIsValid(opclass))
        {
            typentry->hash_opf = get_opclass_family(opclass);
            typentry->hash_opintype = get_opclass_input_type(opclass);
        }
    }

    /* Look for requested operators and functions */
    if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
        typentry->eq_opr == InvalidOid)
    {
        Oid         eq_opr = InvalidOid;

        if (typentry->btree_opf != InvalidOid)
            eq_opr = get_opfamily_member(typentry->btree_opf,
                                         typentry->btree_opintype,
                                         typentry->btree_opintype,
                                         BTEqualStrategyNumber);
        if (eq_opr == InvalidOid &&
            typentry->hash_opf != InvalidOid)
            eq_opr = get_opfamily_member(typentry->hash_opf,
                                         typentry->hash_opintype,
                                         typentry->hash_opintype,
                                         HTEqualStrategyNumber);

        /*
         * If the proposed equality operator is array_eq or record_eq, check
         * to see if the element type or column types support equality. If
         * not, array_eq or record_eq would fail at runtime, so we don't want
         * to report that the type has equality.
         */
        if (eq_opr == ARRAY_EQ_OP &&
            !array_element_has_equality(typentry))
            eq_opr = InvalidOid;
        else if (eq_opr == RECORD_EQ_OP &&
                 !record_fields_have_equality(typentry))
            eq_opr = InvalidOid;

        typentry->eq_opr = eq_opr;

        /*
         * Reset info about hash function whenever we pick up new info about
         * equality operator.  This is so we can ensure that the hash function
         * matches the operator.
         */
        typentry->hash_proc = InvalidOid;
        typentry->hash_proc_finfo.fn_oid = InvalidOid;
    }
    if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
    {
        Oid         lt_opr = InvalidOid;

        if (typentry->btree_opf != InvalidOid)
            lt_opr = get_opfamily_member(typentry->btree_opf,
                                         typentry->btree_opintype,
                                         typentry->btree_opintype,
                                         BTLessStrategyNumber);

        /* As above, make sure array_cmp or record_cmp will succeed */
        if (lt_opr == ARRAY_LT_OP &&
            !array_element_has_compare(typentry))
            lt_opr = InvalidOid;
        else if (lt_opr == RECORD_LT_OP &&
                 !record_fields_have_compare(typentry))
            lt_opr = InvalidOid;

        typentry->lt_opr = lt_opr;
    }
    if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
    {
        Oid         gt_opr = InvalidOid;

        if (typentry->btree_opf != InvalidOid)
            gt_opr = get_opfamily_member(typentry->btree_opf,
                                         typentry->btree_opintype,
                                         typentry->btree_opintype,
                                         BTGreaterStrategyNumber);

        /* As above, make sure array_cmp or record_cmp will succeed */
        if (gt_opr == ARRAY_GT_OP &&
            !array_element_has_compare(typentry))
            gt_opr = InvalidOid;
        else if (gt_opr == RECORD_GT_OP &&
                 !record_fields_have_compare(typentry))
            gt_opr = InvalidOid;

        typentry->gt_opr = gt_opr;
    }
    if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
        typentry->cmp_proc == InvalidOid)
    {
        Oid         cmp_proc = InvalidOid;

        if (typentry->btree_opf != InvalidOid)
            cmp_proc = get_opfamily_proc(typentry->btree_opf,
                                         typentry->btree_opintype,
                                         typentry->btree_opintype,
                                         BTORDER_PROC);

        /* As above, make sure array_cmp or record_cmp will succeed */
        if (cmp_proc == F_BTARRAYCMP &&
            !array_element_has_compare(typentry))
            cmp_proc = InvalidOid;
        else if (cmp_proc == F_BTRECORDCMP &&
                 !record_fields_have_compare(typentry))
            cmp_proc = InvalidOid;

        typentry->cmp_proc = cmp_proc;
    }
    if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO)) &&
        typentry->hash_proc == InvalidOid)
    {
        Oid         hash_proc = InvalidOid;

        /*
         * We insist that the eq_opr, if one has been determined, match the
         * hash opclass; else report there is no hash function.
         */
        if (typentry->hash_opf != InvalidOid &&
            (!OidIsValid(typentry->eq_opr) ||
             typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
                                                     typentry->hash_opintype,
                                                     typentry->hash_opintype,
                                                     HTEqualStrategyNumber)))
            hash_proc = get_opfamily_proc(typentry->hash_opf,
                                          typentry->hash_opintype,
                                          typentry->hash_opintype,
                                          HASHPROC);

        /*
         * As above, make sure hash_array will succeed.  We don't currently
         * support hashing for composite types, but when we do, we'll need
         * more logic here to check that case too.
         */
        if (hash_proc == F_HASH_ARRAY &&
            !array_element_has_hashing(typentry))
            hash_proc = InvalidOid;

        typentry->hash_proc = hash_proc;
    }

    /*
     * Set up fmgr lookup info as requested
     *
     * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
     * which is not quite right (they're really in the hash table's private
     * memory context) but this will do for our purposes.
     */
    if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
        typentry->eq_opr_finfo.fn_oid == InvalidOid &&
        typentry->eq_opr != InvalidOid)
    {
        Oid         eq_opr_func;

        eq_opr_func = get_opcode(typentry->eq_opr);
        if (eq_opr_func != InvalidOid)
            fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
                          CacheMemoryContext);
    }
    if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
        typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
        typentry->cmp_proc != InvalidOid)
    {
        fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
                      CacheMemoryContext);
    }
    if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
        typentry->hash_proc_finfo.fn_oid == InvalidOid &&
        typentry->hash_proc != InvalidOid)
    {
        fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
                      CacheMemoryContext);
    }

    /*
     * If it's a composite type (row type), get tupdesc if requested
     */
    if ((flags & TYPECACHE_TUPDESC) &&
        typentry->tupDesc == NULL &&
        typentry->typtype == TYPTYPE_COMPOSITE)
    {
        load_typcache_tupdesc(typentry);
    }

    /*
     * If requested, get information about a range type
     */
    if ((flags & TYPECACHE_RANGE_INFO) &&
        typentry->rngelemtype == NULL &&
        typentry->typtype == TYPTYPE_RANGE)
    {
        load_rangetype_info(typentry);
    }

    return typentry;
}