Change rule dumper to produce reasonable output for casts that assign
authorTom Lane
Sat, 26 Feb 2000 21:13:18 +0000 (21:13 +0000)
committerTom Lane
Sat, 26 Feb 2000 21:13:18 +0000 (21:13 +0000)
a specific length or precision, such as foo::char(8).  Remove erroneous
removal of user-written casts at the top level of a SELECT target item.

src/backend/utils/adt/ruleutils.c

index 985c0c030930fe56a7ef50a96631b767cfadd5fd..bdeaf440829bad16c91600ab5bc01152c5eee6c7 100644 (file)
@@ -3,7 +3,7 @@
  *           out of its tuple
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.43 2000/02/21 20:18:10 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.44 2000/02/26 21:13:18 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -49,6 +49,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/tlist.h"
 #include "parser/keywords.h"
+#include "parser/parse_expr.h"
 #include "parser/parsetree.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -947,7 +948,8 @@ get_select_query_def(Query *query, deparse_context *context)
        appendStringInfo(buf, sep);
        sep = ", ";
 
-       get_tle_expr(tle, context);
+       /* Do NOT use get_tle_expr here; see its comments! */
+       get_rule_expr(tle->expr, context);
 
        /* Check if we must say AS ... */
        if (! IsA(tle->expr, Var))
@@ -1486,16 +1488,16 @@ static void
 get_func_expr(Expr *expr, deparse_context *context)
 {
    StringInfo  buf = context->buf;
+   Func       *func = (Func *) (expr->oper);
    HeapTuple   proctup;
    Form_pg_proc procStruct;
+   char       *proname;
+   int32       coercedTypmod;
    List       *l;
    char       *sep;
-   Func       *func = (Func *) (expr->oper);
-   char       *proname;
 
-   /* ----------
+   /*
     * Get the functions pg_proc tuple
-    * ----------
     */
    proctup = SearchSysCacheTuple(PROCOID,
                                  ObjectIdGetDatum(func->funcid),
@@ -1527,9 +1529,59 @@ get_func_expr(Expr *expr, deparse_context *context)
        }
    }
 
-   /* ----------
-    * Build a string of proname(args)
-    * ----------
+   /*
+    * Check to see if function is a length-coercion function for some
+    * datatype.  If so, display the operation as a type cast.
+    */
+   if (exprIsLengthCoercion((Node *) expr, &coercedTypmod))
+   {
+       Node       *arg = lfirst(expr->args);
+
+       /*
+        * Strip off any RelabelType on the input, so we don't print
+        * redundancies like x::bpchar::char(8).
+        * XXX Are there any cases where this is a bad idea?
+        */
+       if (IsA(arg, RelabelType))
+           arg = ((RelabelType *) arg)->arg;
+       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.
+        */
+       if (strcmp(proname, "bpchar") == 0)
+       {
+           if (coercedTypmod > VARHDRSZ)
+               appendStringInfo(buf, "char(%d)", coercedTypmod - VARHDRSZ);
+           else
+               appendStringInfo(buf, "char");
+       }
+       else if (strcmp(proname, "varchar") == 0)
+       {
+           if (coercedTypmod > VARHDRSZ)
+               appendStringInfo(buf, "varchar(%d)", coercedTypmod - VARHDRSZ);
+           else
+               appendStringInfo(buf, "varchar");
+       }
+       else if (strcmp(proname, "numeric") == 0)
+       {
+           if (coercedTypmod >= VARHDRSZ)
+               appendStringInfo(buf, "numeric(%d,%d)",
+                                ((coercedTypmod - VARHDRSZ) >> 16) & 0xffff,
+                                (coercedTypmod - VARHDRSZ) & 0xffff);
+           else
+               appendStringInfo(buf, "numeric");
+       }
+       else
+           appendStringInfo(buf, "%s", quote_identifier(proname));
+       return;
+   }
+
+   /*
+    * Normal function: display as proname(args)
     */
    appendStringInfo(buf, "%s(", quote_identifier(proname));
    sep = "";
@@ -1546,99 +1598,37 @@ get_func_expr(Expr *expr, deparse_context *context)
 /* ----------
  * get_tle_expr
  *
- *     A target list expression is a bit different from a normal expression.
- *     If the target column has an atttypmod, the parser usually puts a
- *     padding-/cut-function call around the expression itself.
- *     We must get rid of it, otherwise dump/reload/dump... would blow up
- *     the expressions.
+ *     In an INSERT or UPDATE targetlist item, the parser may have inserted
+ *     a length-coercion function call to coerce the value to the right
+ *     length for the target column.  We want to suppress the output of
+ *     that function call, otherwise dump/reload/dump... would blow up the
+ *     expression by adding more and more layers of length-coercion calls.
+ *
+ * As of 7.0, this hack is no longer absolutely essential, because the parser
+ * is now smart enough not to add a redundant length coercion function call.
+ * But we still suppress the function call just for neatness of displayed
+ * rules.
+ *
+ * Note that this hack must NOT be applied to SELECT targetlist items;
+ * any length coercion appearing there is something the user actually wrote.
  * ----------
  */
 static void
 get_tle_expr(TargetEntry *tle, deparse_context *context)
 {
    Expr       *expr = (Expr *) (tle->expr);
-   Func       *func;
-   HeapTuple   tup;
-   Form_pg_proc procStruct;
-   Form_pg_type typeStruct;
-   Const      *second_arg;
-
-   /* ----------
-    * Check if the result has an atttypmod and if the
-    * expression in the targetlist entry is a function call
-    * ----------
-    */
-   if (tle->resdom->restypmod < 0 ||
-       ! IsA(expr, Expr) ||
-       expr->opType != FUNC_EXPR)
-   {
-       get_rule_expr(tle->expr, context);
-       return;
-   }
-
-   func = (Func *) (expr->oper);
-
-   /* ----------
-    * Get the functions pg_proc tuple
-    * ----------
-    */
-   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.
-    * ----------
-    */
-   if (procStruct->pronargs != 2 ||
-       procStruct->prorettype != procStruct->proargtypes[0] ||
-       procStruct->proargtypes[1] != INT4OID)
-   {
-       get_rule_expr(tle->expr, context);
-       return;
-   }
+   int32       coercedTypmod;
 
    /*
-    * Furthermore, the name of the function must be the same
-    * as the argument/result type 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)
-   {
-       get_rule_expr(tle->expr, context);
-       return;
-   }
-
-   /* ----------
-    * Finally (to be totally safe) the second argument must be a
-    * const and match the value in the results atttypmod.
-    * ----------
+    * If top level is a length coercion to the correct length, suppress it;
+    * else dump the expression normally.
     */
-   second_arg = (Const *) lsecond(expr->args);
-   if (! IsA(second_arg, Const) ||
-       DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod)
-   {
+   if (tle->resdom->restypmod >= 0 &&
+       exprIsLengthCoercion((Node *) expr, &coercedTypmod) &&
+       coercedTypmod == tle->resdom->restypmod)
+       get_rule_expr((Node *) lfirst(expr->args), context);
+   else
        get_rule_expr(tle->expr, context);
-       return;
-   }
-
-   /* ----------
-    * Whow - got it. Now get rid of the padding function
-    * ----------
-    */
-   get_rule_expr((Node *) lfirst(expr->args), context);
 }