diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index faa0c9f6bb..51795aa3b3 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.18 2001/10/04 17:52:24 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.19 2001/10/08 19:55:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -87,11 +87,11 @@ format_type(PG_FUNCTION_ARGS) PG_RETURN_DATUM(_textin(result)); } - - /* * This version is for use within the backend in error messages, etc. * One difference is that it will fail for an invalid type. + * + * The result is always a palloc'd string. */ char * format_type_be(Oid type_oid) @@ -99,6 +99,15 @@ format_type_be(Oid type_oid) return format_type_internal(type_oid, -1, false); } +/* + * This version allows a nondefault typemod to be specified. + */ +char * +format_type_with_typemod(Oid type_oid, int32 typemod) +{ + return format_type_internal(type_oid, typemod, false); +} + static char * diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c931d0a6ef..1d1969e68d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.84 2001/10/04 22:00:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.85 2001/10/08 19:55:07 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -130,6 +130,7 @@ static bool find_alias_in_namespace(Node *nsnode, Node *expr, static bool phony_equal(Node *expr1, Node *expr2, int levelsup); static void get_rule_expr(Node *node, deparse_context *context); static void get_func_expr(Expr *expr, deparse_context *context); +static Node *strip_type_coercion(Node *expr, Oid resultType); static void get_tle_expr(TargetEntry *tle, deparse_context *context); static void get_const_expr(Const *constval, deparse_context *context); static void get_sublink_expr(Node *node, deparse_context *context); @@ -2038,49 +2039,31 @@ get_func_expr(Expr *expr, deparse_context *context) if (exprIsLengthCoercion((Node *) expr, &coercedTypmod)) { Node *arg = lfirst(expr->args); + char *typdesc; /* - * Strip off any RelabelType on the input, so we don't print - * redundancies like x::bpchar::char(8). + * Strip off any type coercions on the input, so we don't print + * redundancies like x::bpchar::character(8). * * XXX Are there any cases where this is a bad idea? */ - if (IsA(arg, RelabelType)) - arg = ((RelabelType *) arg)->arg; + arg = strip_type_coercion(arg, procStruct->prorettype); + appendStringInfoChar(buf, '('); get_rule_expr(arg, context); - appendStringInfo(buf, ")::"); - /* * Show typename with appropriate length decoration. Note that - * since exprIsLengthCoercion succeeded, the function name is the - * same as its output type name. + * since exprIsLengthCoercion succeeded, the function's output + * type is the right thing to use. + * + * XXX In general it is incorrect to quote the result of + * format_type_with_typemod, but are there any special cases + * where we should do so? */ - if (strcmp(proname, "bpchar") == 0) - { - if (coercedTypmod > (int32) VARHDRSZ) - appendStringInfo(buf, "char(%d)", coercedTypmod - VARHDRSZ); - else - appendStringInfo(buf, "char"); - } - else if (strcmp(proname, "varchar") == 0) - { - if (coercedTypmod > (int32) VARHDRSZ) - appendStringInfo(buf, "varchar(%d)", coercedTypmod - VARHDRSZ); - else - appendStringInfo(buf, "varchar"); - } - else if (strcmp(proname, "numeric") == 0) - { - if (coercedTypmod >= (int32) VARHDRSZ) - appendStringInfo(buf, "numeric(%d,%d)", - ((coercedTypmod - VARHDRSZ) >> 16) & 0xffff, - (coercedTypmod - VARHDRSZ) & 0xffff); - else - appendStringInfo(buf, "numeric"); - } - else - appendStringInfo(buf, "%s", quote_identifier(proname)); + typdesc = format_type_with_typemod(procStruct->prorettype, + coercedTypmod); + appendStringInfo(buf, ")::%s", typdesc); + pfree(typdesc); ReleaseSysCache(proctup); return; @@ -2103,6 +2086,79 @@ get_func_expr(Expr *expr, deparse_context *context) } +/* + * strip_type_coercion + * Strip any type coercions at the top of the given expression tree, + * as long as they are coercions to the given datatype. + * + * A RelabelType node is always a type coercion. A function call is also + * considered a type coercion if it has one argument and the function name + * is the same as the (internal) name of its result type. + * + * XXX It'd be better if the parsetree retained some explicit indication + * of the coercion, so we didn't need these heuristics. + */ +static Node * +strip_type_coercion(Node *expr, Oid resultType) +{ + if (expr == NULL || exprType(expr) != resultType) + return expr; + + if (IsA(expr, RelabelType)) + return strip_type_coercion(((RelabelType *) expr)->arg, resultType); + + if (IsA(expr, Expr) && ((Expr *) expr)->opType == FUNC_EXPR) + { + Func *func; + HeapTuple procTuple; + HeapTuple typeTuple; + Form_pg_proc procStruct; + Form_pg_type typeStruct; + + func = (Func *) (((Expr *) expr)->oper); + Assert(IsA(func, Func)); + if (length(((Expr *) expr)->args) != 1) + return expr; + /* Lookup the function in pg_proc */ + procTuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(func->funcid), + 0, 0, 0); + if (!HeapTupleIsValid(procTuple)) + elog(ERROR, "cache lookup for proc %u failed", func->funcid); + procStruct = (Form_pg_proc) GETSTRUCT(procTuple); + /* Double-check func has one arg and correct result type */ + if (procStruct->pronargs != 1 || + procStruct->prorettype != resultType) + { + ReleaseSysCache(procTuple); + return expr; + } + /* See if function has same name as its result type */ + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(procStruct->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup for type %u failed", + procStruct->prorettype); + typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + if (strncmp(NameStr(procStruct->proname), + NameStr(typeStruct->typname), + NAMEDATALEN) != 0) + { + ReleaseSysCache(procTuple); + ReleaseSysCache(typeTuple); + return expr; + } + /* Okay, it is indeed a type-coercion function */ + ReleaseSysCache(procTuple); + ReleaseSysCache(typeTuple); + return strip_type_coercion(lfirst(((Expr *) expr)->args), resultType); + } + + return expr; +} + + /* ---------- * get_tle_expr * diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 0f0c9e15a8..e9b52e7cb4 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.165 2001/09/28 08:09:14 thomas Exp $ + * $Id: builtins.h,v 1.166 2001/10/08 19:55:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -598,6 +598,7 @@ extern Datum pg_convert2(PG_FUNCTION_ARGS); /* format_type.c */ extern Datum format_type(PG_FUNCTION_ARGS); extern char * format_type_be(Oid type_oid); +extern char * format_type_with_typemod(Oid type_oid, int32 typemod); extern Datum oidvectortypes(PG_FUNCTION_ARGS); extern int32 type_maximum_size(Oid type_oid, int32 typemod);