From: Tom Lane Date: Sat, 26 Feb 2000 21:11:10 +0000 (+0000) Subject: Fix exprTypmod to recognize length-coercion function expressions, X-Git-Tag: REL9_0_0~23867 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=7173c485c817735012ceacd8662a0c2aa3068396;p=pg-rex%2Fsyncrep.git Fix exprTypmod to recognize length-coercion function expressions, such as bpchar(char_expression, N), and pull out the attrtypmod that the function is coercing to. This allows correct deduction of the column type in examples such as CREATE VIEW v AS SELECT f1::char(8) FROM tbl; Formerly we labeled v's column as char-of-unknown-length not char(8). Also, this change causes the parser not to insert a redundant length coercion function if the user has explicitly casted an INSERT or UPDATE expression to the right length. --- diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 2efdd13600..d0ac448732 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.70 2000/02/21 18:47:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.71 2000/02/26 21:11:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "postgres.h" #include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" #include "nodes/makefuncs.h" #include "nodes/params.h" #include "nodes/relation.h" @@ -29,6 +30,7 @@ #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "utils/builtins.h" +#include "utils/syscache.h" static Node *parser_typecast_constant(Value *expr, TypeName *typename); static Node *parser_typecast_expression(ParseState *pstate, @@ -701,6 +703,15 @@ exprTypmod(Node *expr) } } break; + case T_Expr: + { + int32 coercedTypmod; + + /* Be smart about length-coercion functions... */ + if (exprIsLengthCoercion(expr, &coercedTypmod)) + return coercedTypmod; + } + break; case T_RelabelType: return ((RelabelType *) expr)->resulttypmod; break; @@ -711,6 +722,97 @@ exprTypmod(Node *expr) } /* + * exprIsLengthCoercion + * Detect whether an expression tree is an application of a datatype's + * typmod-coercion function. Optionally extract the result's typmod. + * + * If coercedTypmod is not NULL, the typmod is stored there if the expression + * is a length-coercion function, else -1 is stored there. + * + * We assume that a two-argument function named for a datatype, whose + * output and first argument types are that datatype, and whose second + * input is an int32 constant, represents a forced length coercion. + * XXX It'd be better if the parsetree retained some explicit indication + * of the coercion, so we didn't need these heuristics. + */ +bool +exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) +{ + Func *func; + Const *second_arg; + HeapTuple tup; + Form_pg_proc procStruct; + Form_pg_type typeStruct; + + if (coercedTypmod != NULL) + *coercedTypmod = -1; /* default result on failure */ + + /* Is it a function-call at all? */ + if (expr == NULL || + ! IsA(expr, Expr) || + ((Expr *) expr)->opType != FUNC_EXPR) + return false; + func = (Func *) (((Expr *) expr)->oper); + Assert(IsA(func, Func)); + + /* + * If it's not a two-argument function with the second argument being + * an int4 constant, it can't have been created from a length coercion. + */ + if (length(((Expr *) expr)->args) != 2) + return false; + second_arg = (Const *) lsecond(((Expr *) expr)->args); + if (! IsA(second_arg, Const) || + second_arg->consttype != INT4OID || + second_arg->constisnull) + return false; + + /* + * Lookup the function in pg_proc + */ + tup = SearchSysCacheTuple(PROCOID, + ObjectIdGetDatum(func->funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup for proc %u failed", func->funcid); + procStruct = (Form_pg_proc) GETSTRUCT(tup); + + /* + * It must be a function with two arguments where the first is of + * the same type as the return value and the second is an int4. + * Also, just to be sure, check return type agrees with expr node. + */ + if (procStruct->pronargs != 2 || + procStruct->prorettype != procStruct->proargtypes[0] || + procStruct->proargtypes[1] != INT4OID || + procStruct->prorettype != ((Expr *) expr)->typeOid) + return false; + + /* + * Furthermore, the name of the function must be the same + * as the argument/result type's name. + */ + tup = SearchSysCacheTuple(TYPEOID, + ObjectIdGetDatum(procStruct->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup for type %u failed", + procStruct->prorettype); + typeStruct = (Form_pg_type) GETSTRUCT(tup); + if (strncmp(NameStr(procStruct->proname), + NameStr(typeStruct->typname), + NAMEDATALEN) != 0) + return false; + + /* + * OK, it is indeed a length-coercion function. + */ + if (coercedTypmod != NULL) + *coercedTypmod = DatumGetInt32(second_arg->constvalue); + return true; +} + +/* * Produce an appropriate Const node from a constant value produced * by the parser and an explicit type name to cast to. */ diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h index 5d20173745..50d7b284bb 100644 --- a/src/include/parser/parse_expr.h +++ b/src/include/parser/parse_expr.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_expr.h,v 1.16 2000/01/26 05:58:27 momjian Exp $ + * $Id: parse_expr.h,v 1.17 2000/02/26 21:11:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,5 +23,6 @@ extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence); extern Oid exprType(Node *expr); extern int32 exprTypmod(Node *expr); +extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod); #endif /* PARSE_EXPR_H */