summaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/enum.c259
-rw-r--r--src/backend/utils/cache/typcache.c322
2 files changed, 498 insertions, 83 deletions
diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c
index e5747a46bc..dd168dd973 100644
--- a/src/backend/utils/adt/enum.c
+++ b/src/backend/utils/adt/enum.c
@@ -13,18 +13,22 @@
*/
#include "postgres.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
#include "fmgr.h"
+#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
-#include "utils/lsyscache.h"
+#include "utils/fmgroids.h"
+#include "utils/snapmgr.h"
#include "utils/syscache.h"
-#include "libpq/pqformat.h"
-#include "miscadmin.h"
+#include "utils/typcache.h"
+static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction);
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
-static int enum_elem_cmp(const void *left, const void *right);
/* Basic I/O support */
@@ -155,13 +159,63 @@ enum_send(PG_FUNCTION_ARGS)
/* Comparison functions and related */
+/*
+ * enum_cmp_internal is the common engine for all the visible comparison
+ * functions, except for enum_eq and enum_ne which can just check for OID
+ * equality directly.
+ */
+static int
+enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
+{
+ TypeCacheEntry *tcache;
+
+ /* Equal OIDs are equal no matter what */
+ if (arg1 == arg2)
+ return 0;
+
+ /* Fast path: even-numbered Oids are known to compare correctly */
+ if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
+ {
+ if (arg1 < arg2)
+ return -1;
+ else
+ return 1;
+ }
+
+ /* Locate the typcache entry for the enum type */
+ tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+ if (tcache == NULL)
+ {
+ HeapTuple enum_tup;
+ Form_pg_enum en;
+ Oid typeoid;
+
+ /* Get the OID of the enum type containing arg1 */
+ enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
+ if (!HeapTupleIsValid(enum_tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("invalid internal value for enum: %u",
+ arg1)));
+ en = (Form_pg_enum) GETSTRUCT(enum_tup);
+ typeoid = en->enumtypid;
+ ReleaseSysCache(enum_tup);
+ /* Now locate and remember the typcache entry */
+ tcache = lookup_type_cache(typeoid, 0);
+ fcinfo->flinfo->fn_extra = (void *) tcache;
+ }
+
+ /* The remaining comparison logic is in typcache.c */
+ return compare_values_of_enum(tcache, arg1, arg2);
+}
+
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a < b);
+ PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
}
Datum
@@ -170,7 +224,7 @@ enum_le(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a <= b);
+ PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
}
Datum
@@ -197,7 +251,7 @@ enum_ge(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a >= b);
+ PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
}
Datum
@@ -206,7 +260,7 @@ enum_gt(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_BOOL(a > b);
+ PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
}
Datum
@@ -215,7 +269,7 @@ enum_smaller(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_OID(a <= b ? a : b);
+ PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
}
Datum
@@ -224,7 +278,7 @@ enum_larger(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- PG_RETURN_OID(a >= b ? a : b);
+ PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
}
Datum
@@ -233,24 +287,63 @@ enum_cmp(PG_FUNCTION_ARGS)
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
- if (a > b)
- PG_RETURN_INT32(1);
- else if (a == b)
+ if (a == b)
PG_RETURN_INT32(0);
+ else if (enum_cmp_internal(a, b, fcinfo) > 0)
+ PG_RETURN_INT32(1);
else
PG_RETURN_INT32(-1);
}
/* Enum programming support functions */
+/*
+ * enum_endpoint: common code for enum_first/enum_last
+ */
+static Oid
+enum_endpoint(Oid enumtypoid, ScanDirection direction)
+{
+ Relation enum_rel;
+ Relation enum_idx;
+ SysScanDesc enum_scan;
+ HeapTuple enum_tuple;
+ ScanKeyData skey;
+ Oid minmax;
+
+ /*
+ * Find the first/last enum member using pg_enum_typid_sortorder_index.
+ * Note we must not use the syscache, and must use an MVCC snapshot here.
+ * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
+ */
+ ScanKeyInit(&skey,
+ Anum_pg_enum_enumtypid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(enumtypoid));
+
+ enum_rel = heap_open(EnumRelationId, AccessShareLock);
+ enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
+ enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
+ GetTransactionSnapshot(),
+ 1, &skey);
+
+ enum_tuple = systable_getnext_ordered(enum_scan, direction);
+ if (HeapTupleIsValid(enum_tuple))
+ minmax = HeapTupleGetOid(enum_tuple);
+ else
+ minmax = InvalidOid;
+
+ systable_endscan_ordered(enum_scan);
+ index_close(enum_idx, AccessShareLock);
+ heap_close(enum_rel, AccessShareLock);
+
+ return minmax;
+}
+
Datum
enum_first(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
- Oid min = InvalidOid;
- CatCList *list;
- int num,
- i;
+ Oid min;
/*
* We rely on being able to get the specific enum type from the calling
@@ -263,21 +356,14 @@ enum_first(PG_FUNCTION_ARGS)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine actual enum type")));
- list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
- num = list->n_members;
- for (i = 0; i < num; i++)
- {
- Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
-
- if (!OidIsValid(min) || valoid < min)
- min = valoid;
- }
-
- ReleaseCatCacheList(list);
+ /* Get the OID using the index */
+ min = enum_endpoint(enumtypoid, ForwardScanDirection);
- if (!OidIsValid(min)) /* should not happen */
- elog(ERROR, "no values found for enum %s",
- format_type_be(enumtypoid));
+ if (!OidIsValid(min))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("enum %s contains no values",
+ format_type_be(enumtypoid))));
PG_RETURN_OID(min);
}
@@ -286,10 +372,7 @@ Datum
enum_last(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
- Oid max = InvalidOid;
- CatCList *list;
- int num,
- i;
+ Oid max;
/*
* We rely on being able to get the specific enum type from the calling
@@ -302,21 +385,14 @@ enum_last(PG_FUNCTION_ARGS)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine actual enum type")));
- list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
- num = list->n_members;
- for (i = 0; i < num; i++)
- {
- Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
-
- if (!OidIsValid(max) || valoid > max)
- max = valoid;
- }
-
- ReleaseCatCacheList(list);
+ /* Get the OID using the index */
+ max = enum_endpoint(enumtypoid, BackwardScanDirection);
- if (!OidIsValid(max)) /* should not happen */
- elog(ERROR, "no values found for enum %s",
- format_type_be(enumtypoid));
+ if (!OidIsValid(max))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("enum %s contains no values",
+ format_type_be(enumtypoid))));
PG_RETURN_OID(max);
}
@@ -377,51 +453,68 @@ static ArrayType *
enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
{
ArrayType *result;
- CatCList *list;
- int total,
- i,
- j;
+ Relation enum_rel;
+ Relation enum_idx;
+ SysScanDesc enum_scan;
+ HeapTuple enum_tuple;
+ ScanKeyData skey;
Datum *elems;
+ int max,
+ cnt;
+ bool left_found;
- list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
- total = list->n_members;
+ /*
+ * Scan the enum members in order using pg_enum_typid_sortorder_index.
+ * Note we must not use the syscache, and must use an MVCC snapshot here.
+ * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
+ */
+ ScanKeyInit(&skey,
+ Anum_pg_enum_enumtypid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(enumtypoid));
+
+ enum_rel = heap_open(EnumRelationId, AccessShareLock);
+ enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
+ enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
+ GetTransactionSnapshot(),
+ 1, &skey);
+
+ max = 64;
+ elems = (Datum *) palloc(max * sizeof(Datum));
+ cnt = 0;
+ left_found = !OidIsValid(lower);
+
+ while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
+ {
+ Oid enum_oid = HeapTupleGetOid(enum_tuple);
- elems = (Datum *) palloc(total * sizeof(Datum));
+ if (!left_found && lower == enum_oid)
+ left_found = true;
- j = 0;
- for (i = 0; i < total; i++)
- {
- Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
+ if (left_found)
+ {
+ if (cnt >= max)
+ {
+ max *= 2;
+ elems = (Datum *) repalloc(elems, max * sizeof(Datum));
+ }
- if ((!OidIsValid(lower) || lower <= val) &&
- (!OidIsValid(upper) || val <= upper))
- elems[j++] = ObjectIdGetDatum(val);
- }
+ elems[cnt++] = ObjectIdGetDatum(enum_oid);
+ }
- /* shouldn't need the cache anymore */
- ReleaseCatCacheList(list);
+ if (OidIsValid(upper) && upper == enum_oid)
+ break;
+ }
- /* sort results into OID order */
- qsort(elems, j, sizeof(Datum), enum_elem_cmp);
+ systable_endscan_ordered(enum_scan);
+ index_close(enum_idx, AccessShareLock);
+ heap_close(enum_rel, AccessShareLock);
+ /* and build the result array */
/* note this hardwires some details about the representation of Oid */
- result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
+ result = construct_array(elems, cnt, enumtypoid, sizeof(Oid), true, 'i');
pfree(elems);
return result;
}
-
-/* qsort comparison function for Datums that are OIDs */
-static int
-enum_elem_cmp(const void *left, const void *right)
-{
- Oid l = DatumGetObjectId(*((const Datum *) left));
- Oid r = DatumGetObjectId(*((const Datum *) right));
-
- if (l < r)
- return -1;
- if (l > r)
- return 1;
- return 0;
-}
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 2009614489..bc4abc451a 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -42,22 +42,44 @@
*/
#include "postgres.h"
+#include <limits.h>
+
#include "access/hash.h"
#include "access/heapam.h"
#include "access/nbtree.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_enum.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "utils/builtins.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/tqual.h"
#include "utils/typcache.h"
/* The main type cache hashtable searched by lookup_type_cache */
static HTAB *TypeCacheHash = NULL;
+/* Private information to support comparisons of enum values */
+typedef struct
+{
+ Oid enum_oid; /* OID of one enum value */
+ float4 sort_order; /* its sort position */
+} EnumItem;
+
+typedef struct TypeCacheEnumData
+{
+ Oid bitmap_base; /* OID corresponding to bit 0 of bitmapset */
+ Bitmapset *sorted_values; /* Set of OIDs known to be in order */
+ int num_values; /* total number of values in enum */
+ EnumItem enum_values[1]; /* VARIABLE LENGTH ARRAY */
+} TypeCacheEnumData;
+
/*
* We use a separate table for storing the definitions of non-anonymous
* record types. Once defined, a record type will be remembered for the
@@ -88,6 +110,9 @@ static int32 RecordCacheArrayLen = 0; /* allocated length of array */
static int32 NextRecordTypmod = 0; /* number of entries used */
static void TypeCacheRelCallback(Datum arg, Oid relid);
+static void load_enum_cache_data(TypeCacheEntry *tcache);
+static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
+static int enum_oid_cmp(const void *left, const void *right);
/*
@@ -551,3 +576,300 @@ TypeCacheRelCallback(Datum arg, Oid relid)
}
}
}
+
+
+/*
+ * Check if given OID is part of the subset that's sortable by comparisons
+ */
+static inline bool
+enum_known_sorted(TypeCacheEnumData *enumdata, Oid arg)
+{
+ 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);
+}
+
+
+/*
+ * compare_values_of_enum
+ * Compare two members of an enum type.
+ * Return <0, 0, or >0 according as arg1 <, =, or > arg2.
+ *
+ * Note: currently, the enumData cache is refreshed only if we are asked
+ * to compare an enum value that is not already in the cache. This is okay
+ * because there is no support for re-ordering existing values, so comparisons
+ * of previously cached values will return the right answer even if other
+ * values have been added since we last loaded the cache.
+ *
+ * Note: the enum logic has a special-case rule about even-numbered versus
+ * odd-numbered OIDs, but we take no account of that rule here; this
+ * routine shouldn't even get called when that rule applies.
+ */
+int
+compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
+{
+ 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;
+}
+
+/*
+ * Load (or re-load) the enumData member of the typcache entry.
+ */
+static void
+load_enum_cache_data(TypeCacheEntry *tcache)
+{
+ 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, GetTransactionSnapshot(),
+ 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;
+}
+
+/*
+ * Locate the EnumItem with the given OID, if present
+ */
+static EnumItem *
+find_enumitem(TypeCacheEnumData *enumdata, Oid arg)
+{
+ 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);
+}
+
+/*
+ * qsort comparison function for OID-ordered EnumItems
+ */
+static int
+enum_oid_cmp(const void *left, const void *right)
+{
+ 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;
+}