Skip to content
Snippets Groups Projects
Commit 7173c485 authored by Tom Lane's avatar Tom Lane
Browse files

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.
parent cbf4c967
No related branches found
No related tags found
No related merge requests found
......@@ -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 */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment