diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2000-02-26 21:11:10 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2000-02-26 21:11:10 +0000 |
commit | 7173c485c817735012ceacd8662a0c2aa3068396 (patch) | |
tree | 46cf181e4970ba015fb580631f790334b8228775 /src/backend/parser/parse_expr.c | |
parent | cbf4c9671ed01c769c6d6abb29fe082437373c33 (diff) | |
download | postgresql-7173c485c817735012ceacd8662a0c2aa3068396.tar.gz postgresql-7173c485c817735012ceacd8662a0c2aa3068396.zip |
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.
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 104 |
1 files changed, 103 insertions, 1 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 2efdd136005..d0ac448732e 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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; @@ -711,6 +722,97 @@ exprTypmod(Node *expr) } /* + * 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. */ |