#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"
Go to the source code of this file.
| #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 |
Definition at line 73 of file typcache.c.
Referenced by array_element_has_compare(), array_element_has_equality(), and array_element_has_hashing().
| #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 struct RecordCacheEntry RecordCacheEntry |
| typedef struct TypeCacheEnumData TypeCacheEnumData |
| static bool array_element_has_compare | ( | TypeCacheEntry * | typentry | ) | [static] |
Definition at line 575 of file typcache.c.
References cache_array_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_COMPARE.
Referenced by lookup_type_cache().
{
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
cache_array_element_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
}
| static bool array_element_has_equality | ( | TypeCacheEntry * | typentry | ) | [static] |
Definition at line 567 of file typcache.c.
References cache_array_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_EQUALITY.
Referenced by lookup_type_cache().
{
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
cache_array_element_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
}
| static bool array_element_has_hashing | ( | TypeCacheEntry * | typentry | ) | [static] |
Definition at line 583 of file typcache.c.
References cache_array_element_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_ELEM_PROPERTIES, and TCFLAGS_HAVE_ELEM_HASHING.
Referenced by lookup_type_cache().
{
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
cache_array_element_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
}
| 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().
| 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);
}
Definition at line 731 of file typcache.c.
References IncrTupleDescRefCount(), and lookup_rowtype_tupdesc_internal().
Referenced by ATExecAddOf(), coerce_record_to_complex(), composite_to_json(), exec_move_row_from_datum(), ExecEvalWholeRowSlow(), ExecEvalWholeRowVar(), get_cached_rowtype(), get_rule_expr(), get_tupdesc_from_datum(), GetAttributeByName(), GetAttributeByNum(), hstore_from_record(), hstore_populate_record(), json_populate_record(), plperl_hash_from_datum(), pltcl_func_handler(), PLy_exec_function(), PLy_function_build_args(), PLyObject_ToComposite(), record_cmp(), record_eq(), record_in(), record_out(), record_recv(), record_send(), rowtype_field_matches(), and transformOfType().
{
TupleDesc tupDesc;
tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
IncrTupleDescRefCount(tupDesc);
return tupDesc;
}
Definition at line 765 of file typcache.c.
References CreateTupleDescCopyConstr(), and lookup_rowtype_tupdesc_internal().
Referenced by ExecInitExpr(), ExecMakeTableFunctionResult(), expandRecordVariable(), ExpandRowReference(), get_expr_result_type(), get_name_for_var_field(), internal_get_result_type(), and TypeGetTupleDesc().
{
TupleDesc tmp;
tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
return CreateTupleDescCopyConstr(tmp);
}
| 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];
}
}
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] |
Definition at line 622 of file typcache.c.
References cache_record_field_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_FIELD_PROPERTIES, and TCFLAGS_HAVE_FIELD_COMPARE.
Referenced by lookup_type_cache().
{
if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
cache_record_field_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
}
| static bool record_fields_have_equality | ( | TypeCacheEntry * | typentry | ) | [static] |
Definition at line 614 of file typcache.c.
References cache_record_field_properties(), TypeCacheEntry::flags, TCFLAGS_CHECKED_FIELD_PROPERTIES, and TCFLAGS_HAVE_FIELD_EQUALITY.
Referenced by lookup_type_cache().
{
if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
cache_record_field_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
}
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;
}
}
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.
1.7.1