diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/gram.y | 55 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 278 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 7 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 12 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 12 |
5 files changed, 218 insertions, 146 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1994875bd1c..6951653ce8a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.293 2002/03/19 12:52:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.294 2002/03/20 19:44:21 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -135,8 +135,7 @@ static void doNegateFloat(Value *v); ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, CopyStmt, CreateAsStmt, CreateDomainStmt, CreateGroupStmt, CreatePLangStmt, CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt, - CreateUserStmt, CreatedbStmt, CursorStmt, - DefineStmt, DeleteStmt, + CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt, DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt, DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt, GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt, @@ -151,7 +150,7 @@ static void doNegateFloat(Value *v); simple_select %type <node> alter_column_default -%type <ival> drop_behavior +%type <ival> drop_behavior, opt_drop_behavior %type <list> createdb_opt_list, createdb_opt_item %type <boolean> opt_equal @@ -1181,6 +1180,10 @@ drop_behavior: CASCADE { $$ = CASCADE; } | RESTRICT { $$ = RESTRICT; } ; +opt_drop_behavior: CASCADE { $$ = CASCADE; } + | RESTRICT { $$ = RESTRICT; } + | /* EMPTY */ { $$ = RESTRICT; /* default */ } + ; /***************************************************************************** @@ -2030,22 +2033,13 @@ def_list: def_elem { $$ = makeList1($1); } | def_list ',' def_elem { $$ = lappend($1, $3); } ; -def_elem: DEFAULT '=' b_expr - { - $$ = makeNode(DefElem); - $$->defname = "default"; - if (exprIsNullConstant($3)) - $$->arg = (Node *)NULL; - else - $$->arg = $3; - } - | ColId '=' def_arg +def_elem: ColLabel '=' def_arg { $$ = makeNode(DefElem); $$->defname = $1; $$->arg = (Node *)$3; } - | ColId + | ColLabel { $$ = makeNode(DefElem); $$->defname = $1; @@ -2069,19 +2063,11 @@ def_arg: func_return { $$ = (Node *)$1; } * *****************************************************************************/ -DropStmt: DROP drop_type name_list +DropStmt: DROP drop_type name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = $2; n->names = $3; - n->behavior = RESTRICT; /* Restricted by default */ - $$ = (Node *)n; - } - | DROP DOMAIN_P name_list drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = DROP_DOMAIN_P; - n->names = $3; n->behavior = $4; $$ = (Node *)n; } @@ -2092,7 +2078,8 @@ drop_type: TABLE { $$ = DROP_TABLE; } | VIEW { $$ = DROP_VIEW; } | INDEX { $$ = DROP_INDEX; } | RULE { $$ = DROP_RULE; } - | TYPE_P { $$ = DROP_TYPE_P; } + | TYPE_P { $$ = DROP_TYPE; } + | DOMAIN_P { $$ = DROP_DOMAIN; } ; /***************************************************************************** @@ -3194,12 +3181,19 @@ createdb_opt_item: LOCATION opt_equal Sconst } ; +/* + * Though the equals sign doesn't match other WITH options, pg_dump uses + * equals for backward compability, and it doesn't seem worth remove it. + */ +opt_equal: '=' { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } + ; + /***************************************************************************** * * DROP DATABASE * - * *****************************************************************************/ DropdbStmt: DROP DATABASE database_name @@ -3210,20 +3204,11 @@ DropdbStmt: DROP DATABASE database_name } ; -/* - * Though the equals sign doesn't match other WITH options, pg_dump uses - * equals for backward compability, and it doesn't seem worth remove it. - */ -opt_equal: '=' { $$ = TRUE; } - | /*EMPTY*/ { $$ = FALSE; } - ; - /***************************************************************************** * * ALTER DATABASE * - * *****************************************************************************/ AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 7aaaa65c61b..690a047915a 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,25 +8,31 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.67 2002/03/19 02:18:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.68 2002/03/20 19:44:22 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/pg_proc.h" +#include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_type.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" + Oid DemoteType(Oid inType); Oid PromoteTypeToNext(Oid inType); static Oid PreferredType(CATEGORY category, Oid type); +static Node *build_func_call(Oid funcid, Oid rettype, List *args); +static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId, + Oid secondArgType); /* coerce_type() @@ -87,32 +93,28 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, result = (Node *) newcon; } - else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId)) + else if (IsBinaryCompatible(inputTypeId, targetTypeId)) { /* * We don't really need to do a conversion, but we do need to * attach a RelabelType node so that the expression will be seen * to have the intended type when inspected by higher-level code. - */ - RelabelType *relabel = makeNode(RelabelType); - - relabel->arg = node; - relabel->resulttype = targetTypeId; - - /* + * * XXX could we label result with exprTypmod(node) instead of * default -1 typmod, to save a possible length-coercion later? * Would work if both types have same interpretation of typmod, * which is likely but not certain. */ - relabel->resulttypmod = -1; - - result = (Node *) relabel; + result = (Node *) makeRelabelType(node, targetTypeId, -1); } else if (typeInheritsFrom(inputTypeId, targetTypeId)) { - /* Input class type is a subclass of target, so nothing to do */ - result = node; + /* + * Input class type is a subclass of target, so nothing to do + * --- except relabel the type. This is binary compatibility + * for complex types. + */ + result = (Node *) makeRelabelType(node, targetTypeId, -1); } else { @@ -121,21 +123,24 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * (caller should have determined that there is one), and generate * an expression tree representing run-time application of the * conversion function. + * + * For domains, we use the coercion function for the base type. */ - FuncCall *n = makeNode(FuncCall); + Oid baseTypeId = getBaseType(targetTypeId); + Oid funcId; - n->funcname = typeidTypeName(targetTypeId); - n->args = makeList1(node); - n->agg_star = false; - n->agg_distinct = false; + funcId = find_coercion_function(baseTypeId, + getBaseType(inputTypeId), + InvalidOid); + if (!OidIsValid(funcId)) + elog(ERROR, "coerce_type: no conversion function from %s to %s", + format_type_be(inputTypeId), format_type_be(targetTypeId)); - result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST); + result = build_func_call(funcId, baseTypeId, makeList1(node)); - /* safety check that we got the right thing */ - if (exprType(result) != targetTypeId) - elog(ERROR, "coerce_type: conversion function %s produced %s", - typeidTypeName(targetTypeId), - typeidTypeName(exprType(result))); + /* if domain, relabel with domain type ID */ + if (targetTypeId != baseTypeId) + result = (Node *) makeRelabelType(result, targetTypeId, -1); /* * If the input is a constant, apply the type conversion function @@ -152,7 +157,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * nodes that mustn't be collapsed. (It'd be a lot cleaner to * make a separate node type for that purpose...) */ - if (IsA(node, Const) &&!((Const *) node)->constisnull) + if (IsA(node, Const) && + !((Const *) node)->constisnull) result = eval_const_expressions(result); } @@ -169,23 +175,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * * Notes: * This uses the same mechanism as the CAST() SQL construct in gram.y. - * We should also check the function return type on candidate conversion - * routines just to be safe but we do not do that yet... - * - thomas 1998-03-31 */ bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids) { int i; - HeapTuple ftup; - Form_pg_proc pform; - Oid oid_array[FUNC_MAX_ARGS]; /* run through argument list... */ for (i = 0; i < nargs; i++) { Oid inputTypeId = input_typeids[i]; Oid targetTypeId = func_typeids[i]; + Oid funcId; /* no problem if same type */ if (inputTypeId == targetTypeId) @@ -195,7 +196,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids) * one of the known-good transparent conversions? then drop * through... */ - if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId)) + if (IsBinaryCompatible(inputTypeId, targetTypeId)) continue; /* don't know what to do for the output type? then quit... */ @@ -232,25 +233,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids) * Else, try for explicit conversion using functions: look for a * single-argument function named with the target type name and * accepting the source type. + * + * If either type is a domain, use its base type instead. */ - MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); - oid_array[0] = inputTypeId; - - ftup = SearchSysCache(PROCNAME, - PointerGetDatum(typeidTypeName(targetTypeId)), - Int32GetDatum(1), - PointerGetDatum(oid_array), - 0); - if (!HeapTupleIsValid(ftup)) - return false; - /* Make sure the function's result type is as expected, too */ - pform = (Form_pg_proc) GETSTRUCT(ftup); - if (pform->prorettype != targetTypeId) - { - ReleaseSysCache(ftup); + funcId = find_coercion_function(getBaseType(targetTypeId), + getBaseType(inputTypeId), + InvalidOid); + if (!OidIsValid(funcId)) return false; - } - ReleaseSysCache(ftup); } return true; @@ -277,8 +267,8 @@ Node * coerce_type_typmod(ParseState *pstate, Node *node, Oid targetTypeId, int32 atttypmod) { - char *funcname; - Oid oid_array[FUNC_MAX_ARGS]; + Oid baseTypeId; + Oid funcId; /* * A negative typmod is assumed to mean that no coercion is wanted. @@ -286,30 +276,28 @@ coerce_type_typmod(ParseState *pstate, Node *node, if (atttypmod < 0 || atttypmod == exprTypmod(node)) return node; - funcname = typeidTypeName(targetTypeId); - MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); - oid_array[0] = targetTypeId; - oid_array[1] = INT4OID; - - /* attempt to find with arguments exactly as specified... */ - if (SearchSysCacheExists(PROCNAME, - PointerGetDatum(funcname), - Int32GetDatum(2), - PointerGetDatum(oid_array), - 0)) + /* If given type is a domain, use base type instead */ + baseTypeId = getBaseType(targetTypeId); + + funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID); + + if (OidIsValid(funcId)) { - A_Const *cons = makeNode(A_Const); - FuncCall *func = makeNode(FuncCall); + Const *cons; - cons->val.type = T_Integer; - cons->val.val.ival = atttypmod; + cons = makeConst(INT4OID, + sizeof(int32), + Int32GetDatum(atttypmod), + false, + true, + false, + false); - func->funcname = funcname; - func->args = makeList2(node, cons); - func->agg_star = false; - func->agg_distinct = false; + node = build_func_call(funcId, baseTypeId, makeList2(node, cons)); - node = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST); + /* relabel if it's domain case */ + if (targetTypeId != baseTypeId) + node = (Node *) makeRelabelType(node, targetTypeId, atttypmod); } return node; @@ -532,6 +520,64 @@ TypeCategory(Oid inType) } /* TypeCategory() */ +/* IsBinaryCompatible() + * Check if two types are binary-compatible. + * + * This notion allows us to cheat and directly exchange values without + * going through the trouble of calling a conversion function. + * + * XXX This should be moved to system catalog lookups + * to allow for better type extensibility. + */ + +/* + * This macro describes hard-coded knowledge of binary compatibility + * for built-in types. + */ +#define IS_BINARY_COMPATIBLE(a,b) \ + (((a) == BPCHAROID && (b) == TEXTOID) \ + || ((a) == BPCHAROID && (b) == VARCHAROID) \ + || ((a) == VARCHAROID && (b) == TEXTOID) \ + || ((a) == VARCHAROID && (b) == BPCHAROID) \ + || ((a) == TEXTOID && (b) == BPCHAROID) \ + || ((a) == TEXTOID && (b) == VARCHAROID) \ + || ((a) == OIDOID && (b) == INT4OID) \ + || ((a) == OIDOID && (b) == REGPROCOID) \ + || ((a) == INT4OID && (b) == OIDOID) \ + || ((a) == INT4OID && (b) == REGPROCOID) \ + || ((a) == REGPROCOID && (b) == OIDOID) \ + || ((a) == REGPROCOID && (b) == INT4OID) \ + || ((a) == ABSTIMEOID && (b) == INT4OID) \ + || ((a) == INT4OID && (b) == ABSTIMEOID) \ + || ((a) == RELTIMEOID && (b) == INT4OID) \ + || ((a) == INT4OID && (b) == RELTIMEOID) \ + || ((a) == INETOID && (b) == CIDROID) \ + || ((a) == CIDROID && (b) == INETOID) \ + || ((a) == BITOID && (b) == VARBITOID) \ + || ((a) == VARBITOID && (b) == BITOID)) + +bool +IsBinaryCompatible(Oid type1, Oid type2) +{ + if (type1 == type2) + return true; + if (IS_BINARY_COMPATIBLE(type1, type2)) + return true; + /* + * Perhaps the types are domains; if so, look at their base types + */ + if (OidIsValid(type1)) + type1 = getBaseType(type1); + if (OidIsValid(type2)) + type2 = getBaseType(type2); + if (type1 == type2) + return true; + if (IS_BINARY_COMPATIBLE(type1, type2)) + return true; + return false; +} + + /* IsPreferredType() * Check if this type is a preferred type. * XXX This should be moved to system catalog lookups @@ -606,31 +652,81 @@ PreferredType(CATEGORY category, Oid type) return result; } /* PreferredType() */ - /* - * If the targetTypeId is a domain, we really want to coerce - * the tuple to the domain type -- not the domain itself + * find_coercion_function + * Look for a coercion function between two types. + * + * A coercion function must be named after (the internal name of) its + * result type, and must accept exactly the specified input type. + * + * This routine is also used to look for length-coercion functions, which + * are similar but accept a second argument. secondArgType is the type + * of the second argument (normally INT4OID), or InvalidOid if we are + * looking for a regular coercion function. + * + * If a function is found, return its pg_proc OID; else return InvalidOid. */ -Oid -getBaseType(Oid inType) +static Oid +find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType) { - HeapTuple tup; - Form_pg_type typTup; + char *funcname; + Oid oid_array[FUNC_MAX_ARGS]; + int nargs; + HeapTuple ftup; + Form_pg_proc pform; + Oid funcid; - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(inType), - 0, 0, 0); + funcname = typeidTypeName(targetTypeId); + MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); + oid_array[0] = inputTypeId; + if (OidIsValid(secondArgType)) + { + oid_array[1] = secondArgType; + nargs = 2; + } + else + nargs = 1; + + ftup = SearchSysCache(PROCNAME, + PointerGetDatum(funcname), + Int32GetDatum(nargs), + PointerGetDatum(oid_array), + 0); + if (!HeapTupleIsValid(ftup)) + return InvalidOid; + /* Make sure the function's result type is as expected, too */ + pform = (Form_pg_proc) GETSTRUCT(ftup); + if (pform->prorettype != targetTypeId) + { + ReleaseSysCache(ftup); + return InvalidOid; + } + funcid = ftup->t_data->t_oid; + ReleaseSysCache(ftup); + return funcid; +} - typTup = ((Form_pg_type) GETSTRUCT(tup)); +/* + * Build an expression tree representing a function call. + * + * The argument expressions must have been transformed already. + */ +static Node * +build_func_call(Oid funcid, Oid rettype, List *args) +{ + Func *funcnode; + Expr *expr; - /* - * Assume that typbasetype exists and is a base type, where inType - * was a domain - */ - if (typTup->typtype == 'd') - inType = typTup->typbasetype; + funcnode = makeNode(Func); + funcnode->funcid = funcid; + funcnode->functype = rettype; + funcnode->func_fcache = NULL; - ReleaseSysCache(tup); + expr = makeNode(Expr); + expr->typeOid = rettype; + expr->opType = FUNC_EXPR; + expr->oper = (Node *) funcnode; + expr->args = args; - return inType; + return (Node *) expr; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 5cbc26f5cbe..6409ef3226a 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.109 2002/03/19 02:18:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.110 2002/03/20 19:44:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1027,8 +1027,7 @@ parser_typecast_expression(ParseState *pstate, if (inputType != targetType) { expr = CoerceTargetExpr(pstate, expr, inputType, - getBaseType(targetType), - typename->typmod); + targetType, typename->typmod); if (expr == NULL) elog(ERROR, "Cannot cast type '%s' to '%s'", format_type_be(inputType), @@ -1040,7 +1039,7 @@ parser_typecast_expression(ParseState *pstate, * as well as a type coercion. */ expr = coerce_type_typmod(pstate, expr, - getBaseType(targetType), typename->typmod); + targetType, typename->typmod); return expr; } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index ed39d6c1036..6dc7b440fa8 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.118 2002/03/20 19:44:29 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -575,8 +575,7 @@ agg_select_candidate(Oid typeid, CandidateList candidates) { current_typeid = current_candidate->args[0]; - if (current_typeid == typeid - || IS_BINARY_COMPATIBLE(current_typeid, typeid)) + if (IsBinaryCompatible(current_typeid, typeid)) { last_candidate = current_candidate; ncandidates++; @@ -815,9 +814,7 @@ func_select_candidate(int nargs, { if (input_typeids[i] != UNKNOWNOID) { - if (current_typeids[i] == input_typeids[i] || - IS_BINARY_COMPATIBLE(current_typeids[i], - input_typeids[i])) + if (IsBinaryCompatible(current_typeids[i], input_typeids[i])) nmatch++; } } @@ -1115,8 +1112,7 @@ func_get_detail(char *funcname, Node *arg1 = lfirst(fargs); if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || - sourceType == targetType || - IS_BINARY_COMPATIBLE(sourceType, targetType)) + IsBinaryCompatible(sourceType, targetType)) { /* Yup, it's a type coercion */ *funcid = InvalidOid; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 318f1b9eb7e..8495f9f9e65 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.52 2002/02/19 20:11:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.53 2002/03/20 19:44:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -354,9 +354,7 @@ oper_select_candidate(int nargs, { if (input_typeids[i] != UNKNOWNOID) { - if (current_typeids[i] == input_typeids[i] || - IS_BINARY_COMPATIBLE(current_typeids[i], - input_typeids[i])) + if (IsBinaryCompatible(current_typeids[i], input_typeids[i])) nmatch++; } } @@ -736,10 +734,8 @@ compatible_oper(char *op, Oid arg1, Oid arg2, bool noError) /* but is it good enough? */ opform = (Form_pg_operator) GETSTRUCT(optup); - if ((opform->oprleft == arg1 || - IS_BINARY_COMPATIBLE(opform->oprleft, arg1)) && - (opform->oprright == arg2 || - IS_BINARY_COMPATIBLE(opform->oprright, arg2))) + if (IsBinaryCompatible(opform->oprleft, arg1) && + IsBinaryCompatible(opform->oprright, arg2)) return optup; /* nope... */ |