88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.70 2000/02/21 18:47:02 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.71 2000/02/26 21:11:10 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
1515
1616#include "postgres.h"
1717
1818#include "catalog/pg_operator.h"
19+ #include "catalog/pg_proc.h"
1920#include "nodes/makefuncs.h"
2021#include "nodes/params.h"
2122#include "nodes/relation.h"
2930#include "parser/parse_relation.h"
3031#include "parser/parse_target.h"
3132#include "utils/builtins.h"
33+ #include "utils/syscache.h"
3234
3335static Node * parser_typecast_constant (Value * expr ,TypeName * typename );
3436static Node * parser_typecast_expression (ParseState * pstate ,
@@ -701,6 +703,15 @@ exprTypmod(Node *expr)
701703}
702704}
703705break ;
706+ case T_Expr :
707+ {
708+ int32 coercedTypmod ;
709+
710+ /* Be smart about length-coercion functions... */
711+ if (exprIsLengthCoercion (expr ,& coercedTypmod ))
712+ return coercedTypmod ;
713+ }
714+ break ;
704715case T_RelabelType :
705716return ((RelabelType * )expr )-> resulttypmod ;
706717break ;
@@ -710,6 +721,97 @@ exprTypmod(Node *expr)
710721return -1 ;
711722}
712723
724+ /*
725+ * exprIsLengthCoercion
726+ *Detect whether an expression tree is an application of a datatype's
727+ *typmod-coercion function. Optionally extract the result's typmod.
728+ *
729+ * If coercedTypmod is not NULL, the typmod is stored there if the expression
730+ * is a length-coercion function, else -1 is stored there.
731+ *
732+ * We assume that a two-argument function named for a datatype, whose
733+ * output and first argument types are that datatype, and whose second
734+ * input is an int32 constant, represents a forced length coercion.
735+ * XXX It'd be better if the parsetree retained some explicit indication
736+ * of the coercion, so we didn't need these heuristics.
737+ */
738+ bool
739+ exprIsLengthCoercion (Node * expr ,int32 * coercedTypmod )
740+ {
741+ Func * func ;
742+ Const * second_arg ;
743+ HeapTuple tup ;
744+ Form_pg_proc procStruct ;
745+ Form_pg_type typeStruct ;
746+
747+ if (coercedTypmod != NULL )
748+ * coercedTypmod = -1 ;/* default result on failure */
749+
750+ /* Is it a function-call at all? */
751+ if (expr == NULL ||
752+ !IsA (expr ,Expr )||
753+ ((Expr * )expr )-> opType != FUNC_EXPR )
754+ return false;
755+ func = (Func * ) (((Expr * )expr )-> oper );
756+ Assert (IsA (func ,Func ));
757+
758+ /*
759+ * If it's not a two-argument function with the second argument being
760+ * an int4 constant, it can't have been created from a length coercion.
761+ */
762+ if (length (((Expr * )expr )-> args )!= 2 )
763+ return false;
764+ second_arg = (Const * )lsecond (((Expr * )expr )-> args );
765+ if (!IsA (second_arg ,Const )||
766+ second_arg -> consttype != INT4OID ||
767+ second_arg -> constisnull )
768+ return false;
769+
770+ /*
771+ * Lookup the function in pg_proc
772+ */
773+ tup = SearchSysCacheTuple (PROCOID ,
774+ ObjectIdGetDatum (func -> funcid ),
775+ 0 ,0 ,0 );
776+ if (!HeapTupleIsValid (tup ))
777+ elog (ERROR ,"cache lookup for proc %u failed" ,func -> funcid );
778+ procStruct = (Form_pg_proc )GETSTRUCT (tup );
779+
780+ /*
781+ * It must be a function with two arguments where the first is of
782+ * the same type as the return value and the second is an int4.
783+ * Also, just to be sure, check return type agrees with expr node.
784+ */
785+ if (procStruct -> pronargs != 2 ||
786+ procStruct -> prorettype != procStruct -> proargtypes [0 ]||
787+ procStruct -> proargtypes [1 ]!= INT4OID ||
788+ procStruct -> prorettype != ((Expr * )expr )-> typeOid )
789+ return false;
790+
791+ /*
792+ * Furthermore, the name of the function must be the same
793+ * as the argument/result type's name.
794+ */
795+ tup = SearchSysCacheTuple (TYPEOID ,
796+ ObjectIdGetDatum (procStruct -> prorettype ),
797+ 0 ,0 ,0 );
798+ if (!HeapTupleIsValid (tup ))
799+ elog (ERROR ,"cache lookup for type %u failed" ,
800+ procStruct -> prorettype );
801+ typeStruct = (Form_pg_type )GETSTRUCT (tup );
802+ if (strncmp (NameStr (procStruct -> proname ),
803+ NameStr (typeStruct -> typname ),
804+ NAMEDATALEN )!= 0 )
805+ return false;
806+
807+ /*
808+ * OK, it is indeed a length-coercion function.
809+ */
810+ if (coercedTypmod != NULL )
811+ * coercedTypmod = DatumGetInt32 (second_arg -> constvalue );
812+ return true;
813+ }
814+
713815/*
714816 * Produce an appropriate Const node from a constant value produced
715817 * by the parser and an explicit type name to cast to.