Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

enum.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.7 2009/01/01 17:23:49 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)));

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

Generated by  Doxygen 1.6.0   Back to index