mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-01 03:37:26 +02:00
440 lines
9.5 KiB
C
440 lines
9.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* enum.c
|
|
* I/O functions, operators, aggregates etc for enum types
|
|
*
|
|
* Copyright (c) 2006-2009, PostgreSQL Global Development Group
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/utils/adt/enum.c,v 1.8 2009/12/19 00:47:57 momjian 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 = SearchSysCache(ENUMTYPOIDNAME,
|
|
ObjectIdGetDatum(enumtypoid),
|
|
CStringGetDatum(name),
|
|
0, 0);
|
|
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 = SearchSysCache(ENUMOID,
|
|
ObjectIdGetDatum(enumval),
|
|
0, 0, 0);
|
|
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 = SearchSysCache(ENUMTYPOIDNAME,
|
|
ObjectIdGetDatum(enumtypoid),
|
|
CStringGetDatum(name),
|
|
0, 0);
|
|
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 = SearchSysCache(ENUMOID,
|
|
ObjectIdGetDatum(enumval),
|
|
0, 0, 0);
|
|
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 = SearchSysCacheList(ENUMTYPOIDNAME, 1,
|
|
ObjectIdGetDatum(enumtypoid),
|
|
0, 0, 0);
|
|
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 = SearchSysCacheList(ENUMTYPOIDNAME, 1,
|
|
ObjectIdGetDatum(enumtypoid),
|
|
0, 0, 0);
|
|
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 = SearchSysCacheList(ENUMTYPOIDNAME, 1,
|
|
ObjectIdGetDatum(enumtypoid),
|
|
0, 0, 0);
|
|
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;
|
|
}
|