Header And Logo

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

Data Structures | Defines | Typedefs | Functions | Variables

typcache.c File Reference

#include "postgres.h"
#include <limits.h>
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for typcache.c:

Go to the source code of this file.

Data Structures

struct  EnumItem
struct  TypeCacheEnumData
struct  RecordCacheEntry

Defines

#define TCFLAGS_CHECKED_ELEM_PROPERTIES   0x0001
#define TCFLAGS_HAVE_ELEM_EQUALITY   0x0002
#define TCFLAGS_HAVE_ELEM_COMPARE   0x0004
#define TCFLAGS_HAVE_ELEM_HASHING   0x0008
#define TCFLAGS_CHECKED_FIELD_PROPERTIES   0x0010
#define TCFLAGS_HAVE_FIELD_EQUALITY   0x0020
#define TCFLAGS_HAVE_FIELD_COMPARE   0x0040
#define REC_HASH_KEYS   16

Typedefs

typedef struct TypeCacheEnumData TypeCacheEnumData
typedef struct RecordCacheEntry RecordCacheEntry

Functions

static void load_typcache_tupdesc (TypeCacheEntry *typentry)
static void load_rangetype_info (TypeCacheEntry *typentry)
static bool array_element_has_equality (TypeCacheEntry *typentry)
static bool array_element_has_compare (TypeCacheEntry *typentry)
static bool array_element_has_hashing (TypeCacheEntry *typentry)
static void cache_array_element_properties (TypeCacheEntry *typentry)
static bool record_fields_have_equality (TypeCacheEntry *typentry)
static bool record_fields_have_compare (TypeCacheEntry *typentry)
static void cache_record_field_properties (TypeCacheEntry *typentry)
static void TypeCacheRelCallback (Datum arg, Oid relid)
static void load_enum_cache_data (TypeCacheEntry *tcache)
static EnumItemfind_enumitem (TypeCacheEnumData *enumdata, Oid arg)
static int enum_oid_cmp (const void *left, const void *right)
TypeCacheEntrylookup_type_cache (Oid type_id, int flags)
static TupleDesc lookup_rowtype_tupdesc_internal (Oid type_id, int32 typmod, bool noError)
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)
static bool enum_known_sorted (TypeCacheEnumData *enumdata, Oid arg)
int compare_values_of_enum (TypeCacheEntry *tcache, Oid arg1, Oid arg2)

Variables

static HTABTypeCacheHash = NULL
static HTABRecordCacheHash = NULL
static TupleDescRecordCacheArray = NULL
static int32 RecordCacheArrayLen = 0
static int32 NextRecordTypmod = 0

Define Documentation

#define REC_HASH_KEYS   16

Definition at line 108 of file typcache.c.

Referenced by assign_record_type_typmod().

#define TCFLAGS_CHECKED_ELEM_PROPERTIES   0x0001
#define TCFLAGS_CHECKED_FIELD_PROPERTIES   0x0010

Definition at line 77 of file typcache.c.

Referenced by record_fields_have_compare(), and record_fields_have_equality().

#define TCFLAGS_HAVE_ELEM_COMPARE   0x0004

Definition at line 75 of file typcache.c.

Referenced by array_element_has_compare().

#define TCFLAGS_HAVE_ELEM_EQUALITY   0x0002

Definition at line 74 of file typcache.c.

Referenced by array_element_has_equality().

#define TCFLAGS_HAVE_ELEM_HASHING   0x0008

Definition at line 76 of file typcache.c.

Referenced by array_element_has_hashing().

#define TCFLAGS_HAVE_FIELD_COMPARE   0x0040

Definition at line 79 of file typcache.c.

Referenced by record_fields_have_compare().

#define TCFLAGS_HAVE_FIELD_EQUALITY   0x0020

Definition at line 78 of file typcache.c.

Referenced by cache_record_field_properties(), and record_fields_have_equality().


Typedef Documentation


Function Documentation

static bool array_element_has_compare ( TypeCacheEntry typentry  )  [static]
static bool array_element_has_equality ( TypeCacheEntry typentry  )  [static]
static bool array_element_has_hashing ( TypeCacheEntry typentry  )  [static]
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);
}

static void cache_array_element_properties ( TypeCacheEntry typentry  )  [static]

Definition at line 591 of file typcache.c.

References TypeCacheEntry::cmp_proc, TypeCacheEntry::eq_opr, TypeCacheEntry::flags, get_base_element_type(), TypeCacheEntry::hash_proc, lookup_type_cache(), OidIsValid, TypeCacheEntry::type_id, TYPECACHE_CMP_PROC, TYPECACHE_EQ_OPR, and TYPECACHE_HASH_PROC.

Referenced by array_element_has_compare(), array_element_has_equality(), and array_element_has_hashing().

{
    Oid         elem_type = get_base_element_type(typentry->type_id);

    if (OidIsValid(elem_type))
    {
        TypeCacheEntry *elementry;

        elementry = lookup_type_cache(elem_type,
                                      TYPECACHE_EQ_OPR |
                                      TYPECACHE_CMP_PROC |
                                      TYPECACHE_HASH_PROC);
        if (OidIsValid(elementry->eq_opr))
            typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
        if (OidIsValid(elementry->cmp_proc))
            typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
        if (OidIsValid(elementry->hash_proc))
            typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
    }
    typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}

static void cache_record_field_properties ( TypeCacheEntry typentry  )  [static]

Definition at line 630 of file typcache.c.

References tupleDesc::attrs, TypeCacheEntry::cmp_proc, TypeCacheEntry::eq_opr, TypeCacheEntry::flags, i, load_typcache_tupdesc(), lookup_type_cache(), tupleDesc::natts, NULL, OidIsValid, RECORDOID, TCFLAGS_HAVE_FIELD_EQUALITY, TypeCacheEntry::tupDesc, TypeCacheEntry::type_id, TYPECACHE_CMP_PROC, TYPECACHE_EQ_OPR, TypeCacheEntry::typtype, and TYPTYPE_COMPOSITE.

Referenced by record_fields_have_compare(), and record_fields_have_equality().

{
    /*
     * For type RECORD, we can't really tell what will work, since we don't
     * have access here to the specific anonymous type.  Just assume that
     * everything will (we may get a failure at runtime ...)
     */
    if (typentry->type_id == RECORDOID)
        typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
                            TCFLAGS_HAVE_FIELD_COMPARE);
    else if (typentry->typtype == TYPTYPE_COMPOSITE)
    {
        TupleDesc   tupdesc;
        int         newflags;
        int         i;

        /* Fetch composite type's tupdesc if we don't have it already */
        if (typentry->tupDesc == NULL)
            load_typcache_tupdesc(typentry);
        tupdesc = typentry->tupDesc;

        /* Have each property if all non-dropped fields have the property */
        newflags = (TCFLAGS_HAVE_FIELD_EQUALITY |
                    TCFLAGS_HAVE_FIELD_COMPARE);
        for (i = 0; i < tupdesc->natts; i++)
        {
            TypeCacheEntry *fieldentry;

            if (tupdesc->attrs[i]->attisdropped)
                continue;

            fieldentry = lookup_type_cache(tupdesc->attrs[i]->atttypid,
                                           TYPECACHE_EQ_OPR |
                                           TYPECACHE_CMP_PROC);
            if (!OidIsValid(fieldentry->eq_opr))
                newflags &= ~TCFLAGS_HAVE_FIELD_EQUALITY;
            if (!OidIsValid(fieldentry->cmp_proc))
                newflags &= ~TCFLAGS_HAVE_FIELD_COMPARE;

            /* We can drop out of the loop once we disprove all bits */
            if (newflags == 0)
                break;
        }
        typentry->flags |= newflags;
    }
    typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
}

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;
}

static bool enum_known_sorted ( TypeCacheEnumData enumdata,
Oid  arg 
) [inline, static]

Definition at line 944 of file typcache.c.

References TypeCacheEnumData::bitmap_base, bms_is_member(), and TypeCacheEnumData::sorted_values.

Referenced by compare_values_of_enum().

{
    Oid         offset;

    if (arg < enumdata->bitmap_base)
        return false;
    offset = arg - enumdata->bitmap_base;
    if (offset > (Oid) INT_MAX)
        return false;
    return bms_is_member((int) offset, enumdata->sorted_values);
}

static int enum_oid_cmp ( const void *  left,
const void *  right 
) [static]

Definition at line 1223 of file typcache.c.

References EnumItem::enum_oid.

Referenced by find_enumitem(), and load_enum_cache_data().

{
    const EnumItem *l = (const EnumItem *) left;
    const EnumItem *r = (const EnumItem *) right;

    if (l->enum_oid < r->enum_oid)
        return -1;
    else if (l->enum_oid > r->enum_oid)
        return 1;
    else
        return 0;
}

static EnumItem * find_enumitem ( TypeCacheEnumData enumdata,
Oid  arg 
) [static]

Definition at line 1206 of file typcache.c.

References EnumItem::enum_oid, enum_oid_cmp(), TypeCacheEnumData::enum_values, and TypeCacheEnumData::num_values.

Referenced by compare_values_of_enum().

{
    EnumItem    srch;

    /* On some versions of Solaris, bsearch of zero items dumps core */
    if (enumdata->num_values <= 0)
        return NULL;

    srch.enum_oid = arg;
    return bsearch(&srch, enumdata->enum_values, enumdata->num_values,
                   sizeof(EnumItem), enum_oid_cmp);
}

static void load_enum_cache_data ( TypeCacheEntry tcache  )  [static]

Definition at line 1046 of file typcache.c.

References AccessShareLock, Anum_pg_enum_enumtypid, TypeCacheEnumData::bitmap_base, bms_add_member(), bms_copy(), bms_free(), bms_make_singleton(), BTEqualStrategyNumber, CacheMemoryContext, EnumItem::enum_oid, enum_oid_cmp(), TypeCacheEnumData::enum_values, TypeCacheEntry::enumData, EnumRelationId, EnumTypIdLabelIndexId, ereport, errcode(), errmsg(), ERROR, format_type_be(), GetLatestSnapshot(), GETSTRUCT, heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, i, MemoryContextSwitchTo(), NULL, TypeCacheEnumData::num_values, ObjectIdGetDatum, offsetof, palloc(), pfree(), qsort, repalloc(), ScanKeyInit(), EnumItem::sort_order, TypeCacheEnumData::sorted_values, systable_beginscan(), systable_endscan(), systable_getnext(), TypeCacheEntry::type_id, TypeCacheEntry::typtype, and TYPTYPE_ENUM.

Referenced by compare_values_of_enum().

{
    TypeCacheEnumData *enumdata;
    Relation    enum_rel;
    SysScanDesc enum_scan;
    HeapTuple   enum_tuple;
    ScanKeyData skey;
    EnumItem   *items;
    int         numitems;
    int         maxitems;
    Oid         bitmap_base;
    Bitmapset  *bitmap;
    MemoryContext oldcxt;
    int         bm_size,
                start_pos;

    /* Check that this is actually an enum */
    if (tcache->typtype != TYPTYPE_ENUM)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("%s is not an enum",
                        format_type_be(tcache->type_id))));

    /*
     * Read all the information for members of the enum type.  We collect the
     * info in working memory in the caller's context, and then transfer it to
     * permanent memory in CacheMemoryContext.  This minimizes the risk of
     * leaking memory from CacheMemoryContext in the event of an error partway
     * through.
     */
    maxitems = 64;
    items = (EnumItem *) palloc(sizeof(EnumItem) * maxitems);
    numitems = 0;

    /*
     * Scan pg_enum for the members of the target enum type.  We use a current
     * MVCC snapshot, *not* SnapshotNow, so that we see a consistent set of
     * rows even if someone commits a renumbering of the enum meanwhile. See
     * comments for RenumberEnumType in catalog/pg_enum.c for more info.
     */
    ScanKeyInit(&skey,
                Anum_pg_enum_enumtypid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(tcache->type_id));

    enum_rel = heap_open(EnumRelationId, AccessShareLock);
    enum_scan = systable_beginscan(enum_rel,
                                   EnumTypIdLabelIndexId,
                                   true, GetLatestSnapshot(),
                                   1, &skey);

    while (HeapTupleIsValid(enum_tuple = systable_getnext(enum_scan)))
    {
        Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enum_tuple);

        if (numitems >= maxitems)
        {
            maxitems *= 2;
            items = (EnumItem *) repalloc(items, sizeof(EnumItem) * maxitems);
        }
        items[numitems].enum_oid = HeapTupleGetOid(enum_tuple);
        items[numitems].sort_order = en->enumsortorder;
        numitems++;
    }

    systable_endscan(enum_scan);
    heap_close(enum_rel, AccessShareLock);

    /* Sort the items into OID order */
    qsort(items, numitems, sizeof(EnumItem), enum_oid_cmp);

    /*
     * Here, we create a bitmap listing a subset of the enum's OIDs that are
     * known to be in order and can thus be compared with just OID comparison.
     *
     * The point of this is that the enum's initial OIDs were certainly in
     * order, so there is some subset that can be compared via OID comparison;
     * and we'd rather not do binary searches unnecessarily.
     *
     * This is somewhat heuristic, and might identify a subset of OIDs that
     * isn't exactly what the type started with.  That's okay as long as the
     * subset is correctly sorted.
     */
    bitmap_base = InvalidOid;
    bitmap = NULL;
    bm_size = 1;                /* only save sets of at least 2 OIDs */

    for (start_pos = 0; start_pos < numitems - 1; start_pos++)
    {
        /*
         * Identify longest sorted subsequence starting at start_pos
         */
        Bitmapset  *this_bitmap = bms_make_singleton(0);
        int         this_bm_size = 1;
        Oid         start_oid = items[start_pos].enum_oid;
        float4      prev_order = items[start_pos].sort_order;
        int         i;

        for (i = start_pos + 1; i < numitems; i++)
        {
            Oid         offset;

            offset = items[i].enum_oid - start_oid;
            /* quit if bitmap would be too large; cutoff is arbitrary */
            if (offset >= 8192)
                break;
            /* include the item if it's in-order */
            if (items[i].sort_order > prev_order)
            {
                prev_order = items[i].sort_order;
                this_bitmap = bms_add_member(this_bitmap, (int) offset);
                this_bm_size++;
            }
        }

        /* Remember it if larger than previous best */
        if (this_bm_size > bm_size)
        {
            bms_free(bitmap);
            bitmap_base = start_oid;
            bitmap = this_bitmap;
            bm_size = this_bm_size;
        }
        else
            bms_free(this_bitmap);

        /*
         * Done if it's not possible to find a longer sequence in the rest of
         * the list.  In typical cases this will happen on the first
         * iteration, which is why we create the bitmaps on the fly instead of
         * doing a second pass over the list.
         */
        if (bm_size >= (numitems - start_pos - 1))
            break;
    }

    /* OK, copy the data into CacheMemoryContext */
    oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    enumdata = (TypeCacheEnumData *)
        palloc(offsetof(TypeCacheEnumData, enum_values) +
               numitems * sizeof(EnumItem));
    enumdata->bitmap_base = bitmap_base;
    enumdata->sorted_values = bms_copy(bitmap);
    enumdata->num_values = numitems;
    memcpy(enumdata->enum_values, items, numitems * sizeof(EnumItem));
    MemoryContextSwitchTo(oldcxt);

    pfree(items);
    bms_free(bitmap);

    /* And link the finished cache struct into the typcache */
    if (tcache->enumData != NULL)
        pfree(tcache->enumData);
    tcache->enumData = enumdata;
}

static void load_rangetype_info ( TypeCacheEntry typentry  )  [static]

Definition at line 501 of file typcache.c.

References BTORDER_PROC, CacheMemoryContext, elog, ERROR, fmgr_info_cxt(), get_opclass_family(), get_opclass_input_type(), get_opfamily_proc(), GETSTRUCT, HeapTupleIsValid, lookup_type_cache(), ObjectIdGetDatum, OidIsValid, RANGETYPE, RegProcedureIsValid, ReleaseSysCache(), TypeCacheEntry::rng_canonical_finfo, TypeCacheEntry::rng_cmp_proc_finfo, TypeCacheEntry::rng_collation, TypeCacheEntry::rng_subdiff_finfo, TypeCacheEntry::rngelemtype, SearchSysCache1, and TypeCacheEntry::type_id.

Referenced by lookup_type_cache().

{
    Form_pg_range pg_range;
    HeapTuple   tup;
    Oid         subtypeOid;
    Oid         opclassOid;
    Oid         canonicalOid;
    Oid         subdiffOid;
    Oid         opfamilyOid;
    Oid         opcintype;
    Oid         cmpFnOid;

    /* get information from pg_range */
    tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(typentry->type_id));
    /* should not fail, since we already checked typtype ... */
    if (!HeapTupleIsValid(tup))
        elog(ERROR, "cache lookup failed for range type %u",
             typentry->type_id);
    pg_range = (Form_pg_range) GETSTRUCT(tup);

    subtypeOid = pg_range->rngsubtype;
    typentry->rng_collation = pg_range->rngcollation;
    opclassOid = pg_range->rngsubopc;
    canonicalOid = pg_range->rngcanonical;
    subdiffOid = pg_range->rngsubdiff;

    ReleaseSysCache(tup);

    /* get opclass properties and look up the comparison function */
    opfamilyOid = get_opclass_family(opclassOid);
    opcintype = get_opclass_input_type(opclassOid);

    cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
                                 BTORDER_PROC);
    if (!RegProcedureIsValid(cmpFnOid))
        elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
             BTORDER_PROC, opcintype, opcintype, opfamilyOid);

    /* set up cached fmgrinfo structs */
    fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo,
                  CacheMemoryContext);
    if (OidIsValid(canonicalOid))
        fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo,
                      CacheMemoryContext);
    if (OidIsValid(subdiffOid))
        fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo,
                      CacheMemoryContext);

    /* Lastly, set up link to the element type --- this marks data valid */
    typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}

static void load_typcache_tupdesc ( TypeCacheEntry typentry  )  [static]

Definition at line 473 of file typcache.c.

References AccessShareLock, Assert, elog, ERROR, OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, tupleDesc::tdrefcount, TypeCacheEntry::tupDesc, TypeCacheEntry::type_id, and TypeCacheEntry::typrelid.

Referenced by cache_record_field_properties(), and lookup_type_cache().

{
    Relation    rel;

    if (!OidIsValid(typentry->typrelid))        /* should not happen */
        elog(ERROR, "invalid typrelid for composite type %u",
             typentry->type_id);
    rel = relation_open(typentry->typrelid, AccessShareLock);
    Assert(rel->rd_rel->reltype == typentry->type_id);

    /*
     * Link to the tupdesc and increment its refcount (we assert it's a
     * refcounted descriptor).  We don't use IncrTupleDescRefCount() for this,
     * because the reference mustn't be entered in the current resource owner;
     * it can outlive the current query.
     */
    typentry->tupDesc = RelationGetDescr(rel);

    Assert(typentry->tupDesc->tdrefcount > 0);
    typentry->tupDesc->tdrefcount++;

    relation_close(rel, AccessShareLock);
}

TupleDesc lookup_rowtype_tupdesc ( Oid  type_id,
int32  typmod 
)
TupleDesc lookup_rowtype_tupdesc_copy ( Oid  type_id,
int32  typmod 
)
static TupleDesc lookup_rowtype_tupdesc_internal ( Oid  type_id,
int32  typmod,
bool  noError 
) [static]

Definition at line 686 of file typcache.c.

References ereport, errcode(), errmsg(), ERROR, format_type_be(), lookup_type_cache(), NextRecordTypmod, NULL, RECORDOID, TypeCacheEntry::tupDesc, and TYPECACHE_TUPDESC.

Referenced by lookup_rowtype_tupdesc(), lookup_rowtype_tupdesc_copy(), and lookup_rowtype_tupdesc_noerror().

{
    if (type_id != RECORDOID)
    {
        /*
         * It's a named composite type, so use the regular typcache.
         */
        TypeCacheEntry *typentry;

        typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
        if (typentry->tupDesc == NULL && !noError)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("type %s is not composite",
                            format_type_be(type_id))));
        return typentry->tupDesc;
    }
    else
    {
        /*
         * It's a transient record type, so look in our record-type table.
         */
        if (typmod < 0 || typmod >= NextRecordTypmod)
        {
            if (!noError)
                ereport(ERROR,
                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                         errmsg("record type has not been registered")));
            return NULL;
        }
        return RecordCacheArray[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;
}

static bool record_fields_have_compare ( TypeCacheEntry typentry  )  [static]
static bool record_fields_have_equality ( TypeCacheEntry typentry  )  [static]
static void TypeCacheRelCallback ( Datum  arg,
Oid  relid 
) [static]

Definition at line 896 of file typcache.c.

References Assert, TypeCacheEntry::cmp_proc, TypeCacheEntry::cmp_proc_finfo, TypeCacheEntry::eq_opr, TypeCacheEntry::eq_opr_finfo, TypeCacheEntry::flags, FmgrInfo::fn_oid, FreeTupleDesc(), TypeCacheEntry::gt_opr, TypeCacheEntry::hash_proc, TypeCacheEntry::hash_proc_finfo, hash_seq_init(), hash_seq_search(), InvalidOid, TypeCacheEntry::lt_opr, NULL, tupleDesc::tdrefcount, TypeCacheEntry::tupDesc, TypeCacheEntry::typrelid, TypeCacheEntry::typtype, and TYPTYPE_COMPOSITE.

Referenced by lookup_type_cache().

{
    HASH_SEQ_STATUS status;
    TypeCacheEntry *typentry;

    /* TypeCacheHash must exist, else this callback wouldn't be registered */
    hash_seq_init(&status, TypeCacheHash);
    while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
    {
        if (typentry->typtype != TYPTYPE_COMPOSITE)
            continue;           /* skip non-composites */

        /* Skip if no match, unless we're zapping all composite types */
        if (relid != typentry->typrelid && relid != InvalidOid)
            continue;

        /* Delete tupdesc if we have it */
        if (typentry->tupDesc != NULL)
        {
            /*
             * Release our refcount, and free the tupdesc if none remain.
             * (Can't use DecrTupleDescRefCount because this reference is not
             * logged in current resource owner.)
             */
            Assert(typentry->tupDesc->tdrefcount > 0);
            if (--typentry->tupDesc->tdrefcount == 0)
                FreeTupleDesc(typentry->tupDesc);
            typentry->tupDesc = NULL;
        }

        /* Reset equality/comparison/hashing information */
        typentry->eq_opr = InvalidOid;
        typentry->lt_opr = InvalidOid;
        typentry->gt_opr = InvalidOid;
        typentry->cmp_proc = InvalidOid;
        typentry->hash_proc = InvalidOid;
        typentry->eq_opr_finfo.fn_oid = InvalidOid;
        typentry->cmp_proc_finfo.fn_oid = InvalidOid;
        typentry->hash_proc_finfo.fn_oid = InvalidOid;
        typentry->flags = 0;
    }
}


Variable Documentation

int32 NextRecordTypmod = 0 [static]

Definition at line 123 of file typcache.c.

Referenced by assign_record_type_typmod(), and lookup_rowtype_tupdesc_internal().

TupleDesc* RecordCacheArray = NULL [static]

Definition at line 121 of file typcache.c.

int32 RecordCacheArrayLen = 0 [static]

Definition at line 122 of file typcache.c.

Referenced by assign_record_type_typmod().

HTAB* RecordCacheHash = NULL [static]

Definition at line 119 of file typcache.c.

HTAB* TypeCacheHash = NULL [static]

Definition at line 70 of file typcache.c.