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.
This commit is contained in:
parent
cbf4c9671e
commit
7173c485c8
@ -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.
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user