postgresql/src/backend/utils/adt/enum.c
Robert Haas e26c539e9f Wrap calls to SearchSysCache and related functions using macros.
The purpose of this change is to eliminate the need for every caller
of SearchSysCache, SearchSysCacheCopy, SearchSysCacheExists,
GetSysCacheOid, and SearchSysCacheList to know the maximum number
of allowable keys for a syscache entry (currently 4).  This will
make it far easier to increase the maximum number of keys in a
future release should we choose to do so, and it makes the code
shorter, too.

Design and review by Tom Lane.
2010-02-14 18:42:19 +00:00

428 lines
9.3 KiB
C

/*-------------------------------------------------------------------------
*
* enum.c
* I/O functions, operators, aggregates etc for enum types
*
* Copyright (c) 2006-2010, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/enum.c,v 1.10 2010/02/14 18:42:16 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_enum.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
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 */
Datum
enum_in(PG_FUNCTION_ARGS)
{
char *name = PG_GETARG_CSTRING(0);
Oid enumtypoid = PG_GETARG_OID(1);
Oid enumoid;
HeapTuple tup;
/* must check length to prevent Assert failure within SearchSysCache */
if (strlen(name) >= NAMEDATALEN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input value for enum %s: \"%s\"",
format_type_be(enumtypoid),
name)));
tup = SearchSysCache2(ENUMTYPOIDNAME,
ObjectIdGetDatum(enumtypoid),
CStringGetDatum(name));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input value for enum %s: \"%s\"",
format_type_be(enumtypoid),
name)));
/*
* This comes from pg_enum.oid and stores system oids in user tables.
* This oid must be preserved by binary upgrades.
*/
enumoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
PG_RETURN_OID(enumoid);
}
Datum
enum_out(PG_FUNCTION_ARGS)
{
Oid enumval = PG_GETARG_OID(0);
char *result;
HeapTuple tup;
Form_pg_enum en;
tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid internal value for enum: %u",
enumval)));
en = (Form_pg_enum) GETSTRUCT(tup);
result = pstrdup(NameStr(en->enumlabel));
ReleaseSysCache(tup);
PG_RETURN_CSTRING(result);
}
/* Binary I/O support */
Datum
enum_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Oid enumtypoid = PG_GETARG_OID(1);
Oid enumoid;
HeapTuple tup;
char *name;
int nbytes;
name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
/* must check length to prevent Assert failure within SearchSysCache */
if (strlen(name) >= NAMEDATALEN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input value for enum %s: \"%s\"",
format_type_be(enumtypoid),
name)));
tup = SearchSysCache2(ENUMTYPOIDNAME,
ObjectIdGetDatum(enumtypoid),
CStringGetDatum(name));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input value for enum %s: \"%s\"",
format_type_be(enumtypoid),
name)));
enumoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
pfree(name);
PG_RETURN_OID(enumoid);
}
Datum
enum_send(PG_FUNCTION_ARGS)
{
Oid enumval = PG_GETARG_OID(0);
StringInfoData buf;
HeapTuple tup;
Form_pg_enum en;
tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid internal value for enum: %u",
enumval)));
en = (Form_pg_enum) GETSTRUCT(tup);
pq_begintypsend(&buf);
pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
ReleaseSysCache(tup);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/* Comparison functions and related */
Datum
enum_lt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_BOOL(a < b);
}
Datum
enum_le(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_BOOL(a <= b);
}
Datum
enum_eq(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_BOOL(a == b);
}
Datum
enum_ne(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_BOOL(a != b);
}
Datum
enum_ge(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_BOOL(a >= b);
}
Datum
enum_gt(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_BOOL(a > b);
}
Datum
enum_smaller(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_OID(a <= b ? a : b);
}
Datum
enum_larger(PG_FUNCTION_ARGS)
{
Oid a = PG_GETARG_OID(0);
Oid b = PG_GETARG_OID(1);
PG_RETURN_OID(a >= b ? a : b);
}
Datum
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)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
/* Enum programming support functions */
Datum
enum_first(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
Oid min = InvalidOid;
CatCList *list;
int num,
i;
/*
* We rely on being able to get the specific enum type from the calling
* expression tree. Notice that the actual value of the argument isn't
* examined at all; in particular it might be NULL.
*/
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (enumtypoid == InvalidOid)
ereport(ERROR,
(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);
if (!OidIsValid(min)) /* should not happen */
elog(ERROR, "no values found for enum %s",
format_type_be(enumtypoid));
PG_RETURN_OID(min);
}
Datum
enum_last(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
Oid max = InvalidOid;
CatCList *list;
int num,
i;
/*
* We rely on being able to get the specific enum type from the calling
* expression tree. Notice that the actual value of the argument isn't
* examined at all; in particular it might be NULL.
*/
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (enumtypoid == InvalidOid)
ereport(ERROR,
(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);
if (!OidIsValid(max)) /* should not happen */
elog(ERROR, "no values found for enum %s",
format_type_be(enumtypoid));
PG_RETURN_OID(max);
}
/* 2-argument variant of enum_range */
Datum
enum_range_bounds(PG_FUNCTION_ARGS)
{
Oid lower;
Oid upper;
Oid enumtypoid;
if (PG_ARGISNULL(0))
lower = InvalidOid;
else
lower = PG_GETARG_OID(0);
if (PG_ARGISNULL(1))
upper = InvalidOid;
else
upper = PG_GETARG_OID(1);
/*
* We rely on being able to get the specific enum type from the calling
* expression tree. The generic type mechanism should have ensured that
* both are of the same type.
*/
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (enumtypoid == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine actual enum type")));
PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
}
/* 1-argument variant of enum_range */
Datum
enum_range_all(PG_FUNCTION_ARGS)
{
Oid enumtypoid;
/*
* We rely on being able to get the specific enum type from the calling
* expression tree. Notice that the actual value of the argument isn't
* examined at all; in particular it might be NULL.
*/
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (enumtypoid == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine actual enum type")));
PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
InvalidOid, InvalidOid));
}
static ArrayType *
enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
{
ArrayType *result;
CatCList *list;
int total,
i,
j;
Datum *elems;
list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid));
total = list->n_members;
elems = (Datum *) palloc(total * sizeof(Datum));
j = 0;
for (i = 0; i < total; i++)
{
Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
if ((!OidIsValid(lower) || lower <= val) &&
(!OidIsValid(upper) || val <= upper))
elems[j++] = ObjectIdGetDatum(val);
}
/* shouldn't need the cache anymore */
ReleaseCatCacheList(list);
/* sort results into OID order */
qsort(elems, j, sizeof(Datum), enum_elem_cmp);
/* note this hardwires some details about the representation of Oid */
result = construct_array(elems, j, 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;
}