1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-24 00:23:06 +03:00

Support enum data types. Along the way, use macros for the values of

pg_type.typtype whereever practical.  Tom Dunstan, with some kibitzing
from Tom Lane.
This commit is contained in:
Tom Lane
2007-04-02 03:49:42 +00:00
parent a482a3e58b
commit 57690c6803
74 changed files with 2398 additions and 332 deletions

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.584 2007/03/26 16:58:39 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.585 2007/04/02 03:49:38 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -248,6 +248,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
TableFuncElementList opt_type_modifiers
prep_type_clause
execute_param_clause using_clause returning_clause
enum_val_list
%type <range> OptTempTableName
%type <into> into_clause create_as_target
@@ -383,7 +384,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
DESC DISABLE_P DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
@@ -2922,6 +2923,13 @@ DefineStmt:
n->coldeflist = $6;
$$ = (Node *)n;
}
| CREATE TYPE_P any_name AS ENUM_P '(' enum_val_list ')'
{
CreateEnumStmt *n = makeNode(CreateEnumStmt);
n->typename = $3;
n->vals = $7;
$$ = (Node *)n;
}
;
definition: '(' def_list ')' { $$ = $2; }
@@ -2966,6 +2974,12 @@ old_aggr_elem: IDENT '=' def_arg
}
;
enum_val_list: Sconst
{ $$ = list_make1(makeString($1)); }
| enum_val_list ',' Sconst
{ $$ = lappend($1, makeString($3)); }
;
/*****************************************************************************
*
@@ -8760,6 +8774,7 @@ unreserved_keyword:
| ENABLE_P
| ENCODING
| ENCRYPTED
| ENUM_P
| ESCAPE
| EXCLUDING
| EXCLUSIVE

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.185 2007/03/19 23:38:29 wieck Exp $
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.186 2007/04/02 03:49:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -136,6 +136,7 @@ static const ScanKeyword ScanKeywords[] = {
{"encoding", ENCODING},
{"encrypted", ENCRYPTED},
{"end", END_P},
{"enum", ENUM_P},
{"escape", ESCAPE},
{"except", EXCEPT},
{"excluding", EXCLUDING},

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.152 2007/03/27 23:21:10 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.153 2007/04/02 03:49:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -132,7 +132,8 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYOID ||
targetTypeId == ANYELEMENTOID ||
(targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID))
(targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) ||
(targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID))
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
@@ -143,7 +144,8 @@ coerce_type(ParseState *pstate, Node *node,
* since an UNKNOWN value is still a perfectly valid Datum. However
* an UNKNOWN value is definitely *not* an array, and so we mustn't
* accept it for ANYARRAY. (Instead, we will call anyarray_in below,
* which will produce an error.)
* which will produce an error.) Likewise, UNKNOWN input is no good
* for ANYENUM.
*
* NB: we do NOT want a RelabelType here.
*/
@@ -406,9 +408,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
if (targetTypeId == ANYOID)
continue;
/* accept if target is ANYARRAY or ANYELEMENT, for now */
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYELEMENTOID)
/* accept if target is polymorphic, for now */
if (IsPolymorphicType(targetTypeId))
{
have_generics = true; /* do more checking later */
continue;
@@ -1048,6 +1049,9 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
* the actual ANYELEMENT datatype is in fact the element type for
* the actual ANYARRAY datatype.
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
*
* If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
* or ANYARRAY argument, assume it is okay.
@@ -1070,6 +1074,7 @@ check_generic_type_consistency(Oid *actual_arg_types,
Oid array_typeid = InvalidOid;
Oid array_typelem;
bool have_anyelement = false;
bool have_anyenum = false;
/*
* Loop through the arguments to see if we have any that are ANYARRAY or
@@ -1079,9 +1084,12 @@ check_generic_type_consistency(Oid *actual_arg_types,
{
Oid actual_type = actual_arg_types[j];
if (declared_arg_types[j] == ANYELEMENTOID)
if (declared_arg_types[j] == ANYELEMENTOID ||
declared_arg_types[j] == ANYENUMOID)
{
have_anyelement = true;
if (declared_arg_types[j] == ANYENUMOID)
have_anyenum = true;
if (actual_type == UNKNOWNOID)
continue;
if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
@@ -1127,6 +1135,13 @@ check_generic_type_consistency(Oid *actual_arg_types,
}
}
if (have_anyenum)
{
/* require the element type to be an enum */
if (!type_is_enum(elem_typeid))
return false;
}
/* Looks valid */
return true;
}
@@ -1136,18 +1151,18 @@ check_generic_type_consistency(Oid *actual_arg_types,
* Make sure a polymorphic function is legally callable, and
* deduce actual argument and result types.
*
* If ANYARRAY or ANYELEMENT is used for a function's arguments or
* If ANYARRAY, ANYELEMENT, or ANYENUM is used for a function's arguments or
* return type, we make sure the actual data types are consistent with
* each other. The argument consistency rules are shown above for
* check_generic_type_consistency().
*
* If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
* or ANYARRAY argument, we attempt to deduce the actual type it should
* have. If successful, we alter that position of declared_arg_types[]
* so that make_fn_arguments will coerce the literal to the right thing.
* If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
* argument, we attempt to deduce the actual type it should have. If
* successful, we alter that position of declared_arg_types[] so that
* make_fn_arguments will coerce the literal to the right thing.
*
* Rules are applied to the function's return type (possibly altering it)
* if it is declared ANYARRAY or ANYELEMENT:
* if it is declared as a polymorphic type:
*
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
@@ -1167,6 +1182,9 @@ check_generic_type_consistency(Oid *actual_arg_types,
* 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
* generate an ERROR. This condition is prevented by CREATE FUNCTION
* and is therefore not expected here.
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
*/
Oid
enforce_generic_type_consistency(Oid *actual_arg_types,
@@ -1180,7 +1198,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid array_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID);
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYENUMOID);
bool have_anyenum = (rettype == ANYENUMOID);
/*
* Loop through the arguments to see if we have any that are ANYARRAY or
@@ -1190,9 +1210,12 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
{
Oid actual_type = actual_arg_types[j];
if (declared_arg_types[j] == ANYELEMENTOID)
if (declared_arg_types[j] == ANYELEMENTOID ||
declared_arg_types[j] == ANYENUMOID)
{
have_generics = have_anyelement = true;
if (declared_arg_types[j] == ANYENUMOID)
have_anyenum = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1227,8 +1250,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
}
/*
* Fast Track: if none of the arguments are ANYARRAY or ANYELEMENT, return
* the unmodified rettype.
* Fast Track: if none of the arguments are polymorphic, return the
* unmodified rettype. We assume it can't be polymorphic either.
*/
if (!have_generics)
return rettype;
@@ -1274,7 +1297,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
/* Only way to get here is if all the generic args are UNKNOWN */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine anyarray/anyelement type because input has type \"unknown\"")));
errmsg("could not determine polymorphic type because input has type \"unknown\"")));
}
if (have_anyenum)
{
/* require the element type to be an enum */
if (!type_is_enum(elem_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type matched to anyenum is not an enum type: %s",
format_type_be(elem_typeid))));
}
/*
@@ -1289,7 +1322,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
if (actual_type != UNKNOWNOID)
continue;
if (declared_arg_types[j] == ANYELEMENTOID)
if (declared_arg_types[j] == ANYELEMENTOID ||
declared_arg_types[j] == ANYENUMOID)
declared_arg_types[j] = elem_typeid;
else if (declared_arg_types[j] == ANYARRAYOID)
{
@@ -1307,7 +1341,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
}
}
/* if we return ANYARRAYOID use the appropriate argument type */
/* if we return ANYARRAY use the appropriate argument type */
if (rettype == ANYARRAYOID)
{
if (!OidIsValid(array_typeid))
@@ -1322,8 +1356,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
return array_typeid;
}
/* if we return ANYELEMENTOID use the appropriate argument type */
if (rettype == ANYELEMENTOID)
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID || rettype == ANYENUMOID)
return elem_typeid;
/* we don't return a generic type; send back the original return type */
@@ -1333,7 +1367,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
/*
* resolve_generic_type()
* Deduce an individual actual datatype on the assumption that
* the rules for ANYARRAY/ANYELEMENT are being followed.
* the rules for polymorphic types are being followed.
*
* declared_type is the declared datatype we want to resolve.
* context_actual_type is the actual input datatype to some argument
@@ -1362,7 +1396,8 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return context_actual_type;
}
else if (context_declared_type == ANYELEMENTOID)
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -1375,7 +1410,7 @@ resolve_generic_type(Oid declared_type,
return array_typeid;
}
}
else if (declared_type == ANYELEMENTOID)
else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -1389,7 +1424,8 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typelem;
}
else if (context_declared_type == ANYELEMENTOID)
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYENUMOID)
{
/* Use the actual type; it doesn't matter if array or not */
return context_actual_type;
@@ -1402,7 +1438,7 @@ resolve_generic_type(Oid declared_type,
}
/* If we get here, declared_type is polymorphic and context isn't */
/* NB: this is a calling-code logic error, not a user error */
elog(ERROR, "could not determine ANYARRAY/ANYELEMENT type because context isn't polymorphic");
elog(ERROR, "could not determine polymorphic type because context isn't polymorphic");
return InvalidOid; /* keep compiler quiet */
}
@@ -1502,6 +1538,7 @@ TypeCategory(Oid inType)
case (INTERNALOID):
case (OPAQUEOID):
case (ANYELEMENTOID):
case (ANYENUMOID):
result = GENERIC_TYPE;
break;
@@ -1647,6 +1684,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (get_element_type(srctype) != InvalidOid)
return true;
/* Also accept any enum type as coercible to ANYENUM */
if (targettype == ANYENUMOID)
if (type_is_enum(srctype))
return true;
/* Else look in pg_cast */
tuple = SearchSysCache(CASTSOURCETARGET,
ObjectIdGetDatum(srctype),
@@ -1777,6 +1819,22 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
result = true;
}
}
/*
* If we still haven't found a possibility, check for enums,
* and retry looking for a cast to or from ANYENUM. But don't
* mistakenly conclude that ANYENUM-to-some-enum-type is a
* trivial cast.
*/
if (!result)
{
if (type_is_enum(sourceTypeId))
result = find_coercion_pathway(targetTypeId, ANYENUMOID,
ccontext, funcid, arrayCoerce);
else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId))
result = find_coercion_pathway(ANYENUMOID, sourceTypeId,
ccontext, funcid, arrayCoerce);
}
}
return result;
@@ -1813,7 +1871,7 @@ find_typmod_coercion_function(Oid typeId,
/* Check for a varlena array type (and not a domain) */
if (typeForm->typelem != InvalidOid &&
typeForm->typlen == -1 &&
typeForm->typtype != 'd')
typeForm->typtype != TYPTYPE_DOMAIN)
{
/* Yes, switch our attention to the element type */
typeId = typeForm->typelem;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.215 2007/03/27 23:21:10 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.216 2007/04/02 03:49:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1627,7 +1627,7 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
break;
case RTE_FUNCTION:
toid = exprType(rte->funcexpr);
if (toid == RECORDOID || get_typtype(toid) == 'c')
if (type_is_rowtype(toid))
{
/* func returns composite; same as relation case */
result = (Node *) makeVar(vnum,

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.94 2007/02/01 19:10:27 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.95 2007/04/02 03:49:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -886,8 +886,8 @@ make_scalar_array_op(ParseState *pstate, List *opname,
declared_arg_types[1] = opform->oprright;
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument and return
* types, possibly adjusting return type or declared_arg_types (which will
* enforce consistency with polymorphic argument and return types,
* possibly adjusting return type or declared_arg_types (which will
* be used as the cast destination by make_fn_arguments)
*/
rettype = enforce_generic_type_consistency(actual_arg_types,
@@ -911,15 +911,25 @@ make_scalar_array_op(ParseState *pstate, List *opname,
/*
* Now switch back to the array type on the right, arranging for any
* needed cast to be applied.
* needed cast to be applied. Beware of polymorphic operators here;
* enforce_generic_type_consistency may or may not have replaced a
* polymorphic type with a real one.
*/
res_atypeId = get_array_type(declared_arg_types[1]);
if (!OidIsValid(res_atypeId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(declared_arg_types[1])),
parser_errposition(pstate, location)));
if (IsPolymorphicType(declared_arg_types[1]))
{
/* assume the actual array type is OK */
res_atypeId = atypeId;
}
else
{
res_atypeId = get_array_type(declared_arg_types[1]);
if (!OidIsValid(res_atypeId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(declared_arg_types[1])),
parser_errposition(pstate, location)));
}
actual_arg_types[1] = atypeId;
declared_arg_types[1] = res_atypeId;
@@ -986,8 +996,8 @@ make_op_expr(ParseState *pstate, Operator op,
}
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument and return
* types, possibly adjusting return type or declared_arg_types (which will
* enforce consistency with polymorphic argument and return types,
* possibly adjusting return type or declared_arg_types (which will
* be used as the cast destination by make_fn_arguments)
*/
rettype = enforce_generic_type_consistency(actual_arg_types,

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.87 2007/01/05 22:19:34 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.88 2007/04/02 03:49:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -398,16 +398,6 @@ typeByVal(Type t)
return typ->typbyval;
}
/* given type (as type struct), return the value of its 'typtype' attribute.*/
char
typeTypType(Type t)
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typtype;
}
/* given type (as type struct), return the name of type */
char *
typeTypeName(Type t)