Fix exprTypmod to recognize length-coercion function expressions,
authorTom Lane
Sat, 26 Feb 2000 21:11:10 +0000 (21:11 +0000)
committerTom Lane
Sat, 26 Feb 2000 21:11:10 +0000 (21:11 +0000)
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.

src/backend/parser/parse_expr.c
src/include/parser/parse_expr.h

index 2efdd136005ce3a181bd6685f40ee7b49950eeda..d0ac448732e6d71ae384519f57ba28caffbadbda 100644 (file)
@@ -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;
@@ -710,6 +721,97 @@ exprTypmod(Node *expr)
    return -1;
 }
 
+/*
+ * 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.
index 5d20173745c2e548f2b9c55211c07194cf96b80e..50d7b284bb5508fc76a40e8a146dbc37b04830da 100644 (file)
@@ -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 */