Header And Logo

PostgreSQL
| The world's most advanced open source database.

enum.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * enum.c
00004  *    I/O functions, operators, aggregates etc for enum types
00005  *
00006  * Copyright (c) 2006-2013, PostgreSQL Global Development Group
00007  *
00008  *
00009  * IDENTIFICATION
00010  *    src/backend/utils/adt/enum.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 #include "postgres.h"
00015 
00016 #include "access/genam.h"
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "catalog/indexing.h"
00020 #include "catalog/pg_enum.h"
00021 #include "libpq/pqformat.h"
00022 #include "utils/array.h"
00023 #include "utils/builtins.h"
00024 #include "utils/fmgroids.h"
00025 #include "utils/snapmgr.h"
00026 #include "utils/syscache.h"
00027 #include "utils/typcache.h"
00028 
00029 
00030 static Oid  enum_endpoint(Oid enumtypoid, ScanDirection direction);
00031 static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
00032 
00033 
00034 /* Basic I/O support */
00035 
00036 Datum
00037 enum_in(PG_FUNCTION_ARGS)
00038 {
00039     char       *name = PG_GETARG_CSTRING(0);
00040     Oid         enumtypoid = PG_GETARG_OID(1);
00041     Oid         enumoid;
00042     HeapTuple   tup;
00043 
00044     /* must check length to prevent Assert failure within SearchSysCache */
00045     if (strlen(name) >= NAMEDATALEN)
00046         ereport(ERROR,
00047                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00048                  errmsg("invalid input value for enum %s: \"%s\"",
00049                         format_type_be(enumtypoid),
00050                         name)));
00051 
00052     tup = SearchSysCache2(ENUMTYPOIDNAME,
00053                           ObjectIdGetDatum(enumtypoid),
00054                           CStringGetDatum(name));
00055     if (!HeapTupleIsValid(tup))
00056         ereport(ERROR,
00057                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00058                  errmsg("invalid input value for enum %s: \"%s\"",
00059                         format_type_be(enumtypoid),
00060                         name)));
00061 
00062     /*
00063      * This comes from pg_enum.oid and stores system oids in user tables. This
00064      * oid must be preserved by binary upgrades.
00065      */
00066     enumoid = HeapTupleGetOid(tup);
00067 
00068     ReleaseSysCache(tup);
00069 
00070     PG_RETURN_OID(enumoid);
00071 }
00072 
00073 Datum
00074 enum_out(PG_FUNCTION_ARGS)
00075 {
00076     Oid         enumval = PG_GETARG_OID(0);
00077     char       *result;
00078     HeapTuple   tup;
00079     Form_pg_enum en;
00080 
00081     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
00082     if (!HeapTupleIsValid(tup))
00083         ereport(ERROR,
00084                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
00085                  errmsg("invalid internal value for enum: %u",
00086                         enumval)));
00087     en = (Form_pg_enum) GETSTRUCT(tup);
00088 
00089     result = pstrdup(NameStr(en->enumlabel));
00090 
00091     ReleaseSysCache(tup);
00092 
00093     PG_RETURN_CSTRING(result);
00094 }
00095 
00096 /* Binary I/O support */
00097 Datum
00098 enum_recv(PG_FUNCTION_ARGS)
00099 {
00100     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
00101     Oid         enumtypoid = PG_GETARG_OID(1);
00102     Oid         enumoid;
00103     HeapTuple   tup;
00104     char       *name;
00105     int         nbytes;
00106 
00107     name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
00108 
00109     /* must check length to prevent Assert failure within SearchSysCache */
00110     if (strlen(name) >= NAMEDATALEN)
00111         ereport(ERROR,
00112                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00113                  errmsg("invalid input value for enum %s: \"%s\"",
00114                         format_type_be(enumtypoid),
00115                         name)));
00116 
00117     tup = SearchSysCache2(ENUMTYPOIDNAME,
00118                           ObjectIdGetDatum(enumtypoid),
00119                           CStringGetDatum(name));
00120     if (!HeapTupleIsValid(tup))
00121         ereport(ERROR,
00122                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00123                  errmsg("invalid input value for enum %s: \"%s\"",
00124                         format_type_be(enumtypoid),
00125                         name)));
00126 
00127     enumoid = HeapTupleGetOid(tup);
00128 
00129     ReleaseSysCache(tup);
00130 
00131     pfree(name);
00132 
00133     PG_RETURN_OID(enumoid);
00134 }
00135 
00136 Datum
00137 enum_send(PG_FUNCTION_ARGS)
00138 {
00139     Oid         enumval = PG_GETARG_OID(0);
00140     StringInfoData buf;
00141     HeapTuple   tup;
00142     Form_pg_enum en;
00143 
00144     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
00145     if (!HeapTupleIsValid(tup))
00146         ereport(ERROR,
00147                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
00148                  errmsg("invalid internal value for enum: %u",
00149                         enumval)));
00150     en = (Form_pg_enum) GETSTRUCT(tup);
00151 
00152     pq_begintypsend(&buf);
00153     pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
00154 
00155     ReleaseSysCache(tup);
00156 
00157     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
00158 }
00159 
00160 /* Comparison functions and related */
00161 
00162 /*
00163  * enum_cmp_internal is the common engine for all the visible comparison
00164  * functions, except for enum_eq and enum_ne which can just check for OID
00165  * equality directly.
00166  */
00167 static int
00168 enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
00169 {
00170     TypeCacheEntry *tcache;
00171 
00172     /* Equal OIDs are equal no matter what */
00173     if (arg1 == arg2)
00174         return 0;
00175 
00176     /* Fast path: even-numbered Oids are known to compare correctly */
00177     if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
00178     {
00179         if (arg1 < arg2)
00180             return -1;
00181         else
00182             return 1;
00183     }
00184 
00185     /* Locate the typcache entry for the enum type */
00186     tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
00187     if (tcache == NULL)
00188     {
00189         HeapTuple   enum_tup;
00190         Form_pg_enum en;
00191         Oid         typeoid;
00192 
00193         /* Get the OID of the enum type containing arg1 */
00194         enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
00195         if (!HeapTupleIsValid(enum_tup))
00196             ereport(ERROR,
00197                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
00198                      errmsg("invalid internal value for enum: %u",
00199                             arg1)));
00200         en = (Form_pg_enum) GETSTRUCT(enum_tup);
00201         typeoid = en->enumtypid;
00202         ReleaseSysCache(enum_tup);
00203         /* Now locate and remember the typcache entry */
00204         tcache = lookup_type_cache(typeoid, 0);
00205         fcinfo->flinfo->fn_extra = (void *) tcache;
00206     }
00207 
00208     /* The remaining comparison logic is in typcache.c */
00209     return compare_values_of_enum(tcache, arg1, arg2);
00210 }
00211 
00212 Datum
00213 enum_lt(PG_FUNCTION_ARGS)
00214 {
00215     Oid         a = PG_GETARG_OID(0);
00216     Oid         b = PG_GETARG_OID(1);
00217 
00218     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
00219 }
00220 
00221 Datum
00222 enum_le(PG_FUNCTION_ARGS)
00223 {
00224     Oid         a = PG_GETARG_OID(0);
00225     Oid         b = PG_GETARG_OID(1);
00226 
00227     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
00228 }
00229 
00230 Datum
00231 enum_eq(PG_FUNCTION_ARGS)
00232 {
00233     Oid         a = PG_GETARG_OID(0);
00234     Oid         b = PG_GETARG_OID(1);
00235 
00236     PG_RETURN_BOOL(a == b);
00237 }
00238 
00239 Datum
00240 enum_ne(PG_FUNCTION_ARGS)
00241 {
00242     Oid         a = PG_GETARG_OID(0);
00243     Oid         b = PG_GETARG_OID(1);
00244 
00245     PG_RETURN_BOOL(a != b);
00246 }
00247 
00248 Datum
00249 enum_ge(PG_FUNCTION_ARGS)
00250 {
00251     Oid         a = PG_GETARG_OID(0);
00252     Oid         b = PG_GETARG_OID(1);
00253 
00254     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
00255 }
00256 
00257 Datum
00258 enum_gt(PG_FUNCTION_ARGS)
00259 {
00260     Oid         a = PG_GETARG_OID(0);
00261     Oid         b = PG_GETARG_OID(1);
00262 
00263     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
00264 }
00265 
00266 Datum
00267 enum_smaller(PG_FUNCTION_ARGS)
00268 {
00269     Oid         a = PG_GETARG_OID(0);
00270     Oid         b = PG_GETARG_OID(1);
00271 
00272     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
00273 }
00274 
00275 Datum
00276 enum_larger(PG_FUNCTION_ARGS)
00277 {
00278     Oid         a = PG_GETARG_OID(0);
00279     Oid         b = PG_GETARG_OID(1);
00280 
00281     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
00282 }
00283 
00284 Datum
00285 enum_cmp(PG_FUNCTION_ARGS)
00286 {
00287     Oid         a = PG_GETARG_OID(0);
00288     Oid         b = PG_GETARG_OID(1);
00289 
00290     if (a == b)
00291         PG_RETURN_INT32(0);
00292     else if (enum_cmp_internal(a, b, fcinfo) > 0)
00293         PG_RETURN_INT32(1);
00294     else
00295         PG_RETURN_INT32(-1);
00296 }
00297 
00298 /* Enum programming support functions */
00299 
00300 /*
00301  * enum_endpoint: common code for enum_first/enum_last
00302  */
00303 static Oid
00304 enum_endpoint(Oid enumtypoid, ScanDirection direction)
00305 {
00306     Relation    enum_rel;
00307     Relation    enum_idx;
00308     SysScanDesc enum_scan;
00309     HeapTuple   enum_tuple;
00310     ScanKeyData skey;
00311     Oid         minmax;
00312 
00313     /*
00314      * Find the first/last enum member using pg_enum_typid_sortorder_index.
00315      * Note we must not use the syscache, and must use an MVCC snapshot here.
00316      * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
00317      */
00318     ScanKeyInit(&skey,
00319                 Anum_pg_enum_enumtypid,
00320                 BTEqualStrategyNumber, F_OIDEQ,
00321                 ObjectIdGetDatum(enumtypoid));
00322 
00323     enum_rel = heap_open(EnumRelationId, AccessShareLock);
00324     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
00325     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
00326                                            GetTransactionSnapshot(),
00327                                            1, &skey);
00328 
00329     enum_tuple = systable_getnext_ordered(enum_scan, direction);
00330     if (HeapTupleIsValid(enum_tuple))
00331         minmax = HeapTupleGetOid(enum_tuple);
00332     else
00333         minmax = InvalidOid;
00334 
00335     systable_endscan_ordered(enum_scan);
00336     index_close(enum_idx, AccessShareLock);
00337     heap_close(enum_rel, AccessShareLock);
00338 
00339     return minmax;
00340 }
00341 
00342 Datum
00343 enum_first(PG_FUNCTION_ARGS)
00344 {
00345     Oid         enumtypoid;
00346     Oid         min;
00347 
00348     /*
00349      * We rely on being able to get the specific enum type from the calling
00350      * expression tree.  Notice that the actual value of the argument isn't
00351      * examined at all; in particular it might be NULL.
00352      */
00353     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
00354     if (enumtypoid == InvalidOid)
00355         ereport(ERROR,
00356                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00357                  errmsg("could not determine actual enum type")));
00358 
00359     /* Get the OID using the index */
00360     min = enum_endpoint(enumtypoid, ForwardScanDirection);
00361 
00362     if (!OidIsValid(min))
00363         ereport(ERROR,
00364                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00365                  errmsg("enum %s contains no values",
00366                         format_type_be(enumtypoid))));
00367 
00368     PG_RETURN_OID(min);
00369 }
00370 
00371 Datum
00372 enum_last(PG_FUNCTION_ARGS)
00373 {
00374     Oid         enumtypoid;
00375     Oid         max;
00376 
00377     /*
00378      * We rely on being able to get the specific enum type from the calling
00379      * expression tree.  Notice that the actual value of the argument isn't
00380      * examined at all; in particular it might be NULL.
00381      */
00382     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
00383     if (enumtypoid == InvalidOid)
00384         ereport(ERROR,
00385                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00386                  errmsg("could not determine actual enum type")));
00387 
00388     /* Get the OID using the index */
00389     max = enum_endpoint(enumtypoid, BackwardScanDirection);
00390 
00391     if (!OidIsValid(max))
00392         ereport(ERROR,
00393                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00394                  errmsg("enum %s contains no values",
00395                         format_type_be(enumtypoid))));
00396 
00397     PG_RETURN_OID(max);
00398 }
00399 
00400 /* 2-argument variant of enum_range */
00401 Datum
00402 enum_range_bounds(PG_FUNCTION_ARGS)
00403 {
00404     Oid         lower;
00405     Oid         upper;
00406     Oid         enumtypoid;
00407 
00408     if (PG_ARGISNULL(0))
00409         lower = InvalidOid;
00410     else
00411         lower = PG_GETARG_OID(0);
00412     if (PG_ARGISNULL(1))
00413         upper = InvalidOid;
00414     else
00415         upper = PG_GETARG_OID(1);
00416 
00417     /*
00418      * We rely on being able to get the specific enum type from the calling
00419      * expression tree.  The generic type mechanism should have ensured that
00420      * both are of the same type.
00421      */
00422     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
00423     if (enumtypoid == InvalidOid)
00424         ereport(ERROR,
00425                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00426                  errmsg("could not determine actual enum type")));
00427 
00428     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
00429 }
00430 
00431 /* 1-argument variant of enum_range */
00432 Datum
00433 enum_range_all(PG_FUNCTION_ARGS)
00434 {
00435     Oid         enumtypoid;
00436 
00437     /*
00438      * We rely on being able to get the specific enum type from the calling
00439      * expression tree.  Notice that the actual value of the argument isn't
00440      * examined at all; in particular it might be NULL.
00441      */
00442     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
00443     if (enumtypoid == InvalidOid)
00444         ereport(ERROR,
00445                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00446                  errmsg("could not determine actual enum type")));
00447 
00448     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
00449                                               InvalidOid, InvalidOid));
00450 }
00451 
00452 static ArrayType *
00453 enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
00454 {
00455     ArrayType  *result;
00456     Relation    enum_rel;
00457     Relation    enum_idx;
00458     SysScanDesc enum_scan;
00459     HeapTuple   enum_tuple;
00460     ScanKeyData skey;
00461     Datum      *elems;
00462     int         max,
00463                 cnt;
00464     bool        left_found;
00465 
00466     /*
00467      * Scan the enum members in order using pg_enum_typid_sortorder_index.
00468      * Note we must not use the syscache, and must use an MVCC snapshot here.
00469      * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
00470      */
00471     ScanKeyInit(&skey,
00472                 Anum_pg_enum_enumtypid,
00473                 BTEqualStrategyNumber, F_OIDEQ,
00474                 ObjectIdGetDatum(enumtypoid));
00475 
00476     enum_rel = heap_open(EnumRelationId, AccessShareLock);
00477     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
00478     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
00479                                            GetTransactionSnapshot(),
00480                                            1, &skey);
00481 
00482     max = 64;
00483     elems = (Datum *) palloc(max * sizeof(Datum));
00484     cnt = 0;
00485     left_found = !OidIsValid(lower);
00486 
00487     while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
00488     {
00489         Oid         enum_oid = HeapTupleGetOid(enum_tuple);
00490 
00491         if (!left_found && lower == enum_oid)
00492             left_found = true;
00493 
00494         if (left_found)
00495         {
00496             if (cnt >= max)
00497             {
00498                 max *= 2;
00499                 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
00500             }
00501 
00502             elems[cnt++] = ObjectIdGetDatum(enum_oid);
00503         }
00504 
00505         if (OidIsValid(upper) && upper == enum_oid)
00506             break;
00507     }
00508 
00509     systable_endscan_ordered(enum_scan);
00510     index_close(enum_idx, AccessShareLock);
00511     heap_close(enum_rel, AccessShareLock);
00512 
00513     /* and build the result array */
00514     /* note this hardwires some details about the representation of Oid */
00515     result = construct_array(elems, cnt, enumtypoid, sizeof(Oid), true, 'i');
00516 
00517     pfree(elems);
00518 
00519     return result;
00520 }