Skip to content

Commit 7173c48

Browse files
committed
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.
1 parent cbf4c96 commit 7173c48

File tree

2 files changed

+105
-2
lines changed

2 files changed

+105
-2
lines changed

src/backend/parser/parse_expr.c

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
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"
@@ -29,6 +30,7 @@
2930
#include "parser/parse_relation.h"
3031
#include "parser/parse_target.h"
3132
#include "utils/builtins.h"
33+
#include "utils/syscache.h"
3234

3335
static Node *parser_typecast_constant(Value *expr, TypeName *typename);
3436
static Node *parser_typecast_expression(ParseState *pstate,
@@ -701,6 +703,15 @@ exprTypmod(Node *expr)
701703
}
702704
}
703705
break;
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;
704715
case T_RelabelType:
705716
return ((RelabelType *) expr)->resulttypmod;
706717
break;
@@ -710,6 +721,97 @@ exprTypmod(Node *expr)
710721
return -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.

src/include/parser/parse_expr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: parse_expr.h,v 1.16 2000/01/26 05:58:27 momjian Exp $
10+
* $Id: parse_expr.h,v 1.17 2000/02/26 21:11:09 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -23,5 +23,6 @@
2323
extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
2424
extern Oid exprType(Node *expr);
2525
extern int32 exprTypmod(Node *expr);
26+
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
2627

2728
#endif /* PARSE_EXPR_H */

0 commit comments

Comments
 (0)