mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
Change rule dumper to produce reasonable output for casts that assign
a specific length or precision, such as foo::char(8). Remove erroneous removal of user-written casts at the top level of a SELECT target item.
This commit is contained in:
@ -3,7 +3,7 @@
|
|||||||
* out of its tuple
|
* out of its tuple
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.43 2000/02/21 20:18:10 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.44 2000/02/26 21:13:18 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -49,6 +49,7 @@
|
|||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/tlist.h"
|
#include "optimizer/tlist.h"
|
||||||
#include "parser/keywords.h"
|
#include "parser/keywords.h"
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
@ -947,7 +948,8 @@ get_select_query_def(Query *query, deparse_context *context)
|
|||||||
appendStringInfo(buf, sep);
|
appendStringInfo(buf, sep);
|
||||||
sep = ", ";
|
sep = ", ";
|
||||||
|
|
||||||
get_tle_expr(tle, context);
|
/* Do NOT use get_tle_expr here; see its comments! */
|
||||||
|
get_rule_expr(tle->expr, context);
|
||||||
|
|
||||||
/* Check if we must say AS ... */
|
/* Check if we must say AS ... */
|
||||||
if (! IsA(tle->expr, Var))
|
if (! IsA(tle->expr, Var))
|
||||||
@ -1486,16 +1488,16 @@ static void
|
|||||||
get_func_expr(Expr *expr, deparse_context *context)
|
get_func_expr(Expr *expr, deparse_context *context)
|
||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
|
Func *func = (Func *) (expr->oper);
|
||||||
HeapTuple proctup;
|
HeapTuple proctup;
|
||||||
Form_pg_proc procStruct;
|
Form_pg_proc procStruct;
|
||||||
|
char *proname;
|
||||||
|
int32 coercedTypmod;
|
||||||
List *l;
|
List *l;
|
||||||
char *sep;
|
char *sep;
|
||||||
Func *func = (Func *) (expr->oper);
|
|
||||||
char *proname;
|
|
||||||
|
|
||||||
/* ----------
|
/*
|
||||||
* Get the functions pg_proc tuple
|
* Get the functions pg_proc tuple
|
||||||
* ----------
|
|
||||||
*/
|
*/
|
||||||
proctup = SearchSysCacheTuple(PROCOID,
|
proctup = SearchSysCacheTuple(PROCOID,
|
||||||
ObjectIdGetDatum(func->funcid),
|
ObjectIdGetDatum(func->funcid),
|
||||||
@ -1527,9 +1529,59 @@ get_func_expr(Expr *expr, deparse_context *context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------
|
/*
|
||||||
* Build a string of proname(args)
|
* Check to see if function is a length-coercion function for some
|
||||||
* ----------
|
* datatype. If so, display the operation as a type cast.
|
||||||
|
*/
|
||||||
|
if (exprIsLengthCoercion((Node *) expr, &coercedTypmod))
|
||||||
|
{
|
||||||
|
Node *arg = lfirst(expr->args);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strip off any RelabelType on the input, so we don't print
|
||||||
|
* redundancies like x::bpchar::char(8).
|
||||||
|
* XXX Are there any cases where this is a bad idea?
|
||||||
|
*/
|
||||||
|
if (IsA(arg, RelabelType))
|
||||||
|
arg = ((RelabelType *) arg)->arg;
|
||||||
|
appendStringInfoChar(buf, '(');
|
||||||
|
get_rule_expr(arg, context);
|
||||||
|
appendStringInfo(buf, ")::");
|
||||||
|
/*
|
||||||
|
* Show typename with appropriate length decoration.
|
||||||
|
* Note that since exprIsLengthCoercion succeeded, the function
|
||||||
|
* name is the same as its output type name.
|
||||||
|
*/
|
||||||
|
if (strcmp(proname, "bpchar") == 0)
|
||||||
|
{
|
||||||
|
if (coercedTypmod > VARHDRSZ)
|
||||||
|
appendStringInfo(buf, "char(%d)", coercedTypmod - VARHDRSZ);
|
||||||
|
else
|
||||||
|
appendStringInfo(buf, "char");
|
||||||
|
}
|
||||||
|
else if (strcmp(proname, "varchar") == 0)
|
||||||
|
{
|
||||||
|
if (coercedTypmod > VARHDRSZ)
|
||||||
|
appendStringInfo(buf, "varchar(%d)", coercedTypmod - VARHDRSZ);
|
||||||
|
else
|
||||||
|
appendStringInfo(buf, "varchar");
|
||||||
|
}
|
||||||
|
else if (strcmp(proname, "numeric") == 0)
|
||||||
|
{
|
||||||
|
if (coercedTypmod >= VARHDRSZ)
|
||||||
|
appendStringInfo(buf, "numeric(%d,%d)",
|
||||||
|
((coercedTypmod - VARHDRSZ) >> 16) & 0xffff,
|
||||||
|
(coercedTypmod - VARHDRSZ) & 0xffff);
|
||||||
|
else
|
||||||
|
appendStringInfo(buf, "numeric");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendStringInfo(buf, "%s", quote_identifier(proname));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normal function: display as proname(args)
|
||||||
*/
|
*/
|
||||||
appendStringInfo(buf, "%s(", quote_identifier(proname));
|
appendStringInfo(buf, "%s(", quote_identifier(proname));
|
||||||
sep = "";
|
sep = "";
|
||||||
@ -1546,99 +1598,37 @@ get_func_expr(Expr *expr, deparse_context *context)
|
|||||||
/* ----------
|
/* ----------
|
||||||
* get_tle_expr
|
* get_tle_expr
|
||||||
*
|
*
|
||||||
* A target list expression is a bit different from a normal expression.
|
* In an INSERT or UPDATE targetlist item, the parser may have inserted
|
||||||
* If the target column has an atttypmod, the parser usually puts a
|
* a length-coercion function call to coerce the value to the right
|
||||||
* padding-/cut-function call around the expression itself.
|
* length for the target column. We want to suppress the output of
|
||||||
* We must get rid of it, otherwise dump/reload/dump... would blow up
|
* that function call, otherwise dump/reload/dump... would blow up the
|
||||||
* the expressions.
|
* expression by adding more and more layers of length-coercion calls.
|
||||||
|
*
|
||||||
|
* As of 7.0, this hack is no longer absolutely essential, because the parser
|
||||||
|
* is now smart enough not to add a redundant length coercion function call.
|
||||||
|
* But we still suppress the function call just for neatness of displayed
|
||||||
|
* rules.
|
||||||
|
*
|
||||||
|
* Note that this hack must NOT be applied to SELECT targetlist items;
|
||||||
|
* any length coercion appearing there is something the user actually wrote.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
get_tle_expr(TargetEntry *tle, deparse_context *context)
|
get_tle_expr(TargetEntry *tle, deparse_context *context)
|
||||||
{
|
{
|
||||||
Expr *expr = (Expr *) (tle->expr);
|
Expr *expr = (Expr *) (tle->expr);
|
||||||
Func *func;
|
int32 coercedTypmod;
|
||||||
HeapTuple tup;
|
|
||||||
Form_pg_proc procStruct;
|
|
||||||
Form_pg_type typeStruct;
|
|
||||||
Const *second_arg;
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* Check if the result has an atttypmod and if the
|
|
||||||
* expression in the targetlist entry is a function call
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
if (tle->resdom->restypmod < 0 ||
|
|
||||||
! IsA(expr, Expr) ||
|
|
||||||
expr->opType != FUNC_EXPR)
|
|
||||||
{
|
|
||||||
get_rule_expr(tle->expr, context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
func = (Func *) (expr->oper);
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* Get the functions pg_proc tuple
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
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.
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
if (procStruct->pronargs != 2 ||
|
|
||||||
procStruct->prorettype != procStruct->proargtypes[0] ||
|
|
||||||
procStruct->proargtypes[1] != INT4OID)
|
|
||||||
{
|
|
||||||
get_rule_expr(tle->expr, context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Furthermore, the name of the function must be the same
|
* If top level is a length coercion to the correct length, suppress it;
|
||||||
* as the argument/result type name.
|
* else dump the expression normally.
|
||||||
*/
|
*/
|
||||||
tup = SearchSysCacheTuple(TYPEOID,
|
if (tle->resdom->restypmod >= 0 &&
|
||||||
ObjectIdGetDatum(procStruct->prorettype),
|
exprIsLengthCoercion((Node *) expr, &coercedTypmod) &&
|
||||||
0, 0, 0);
|
coercedTypmod == tle->resdom->restypmod)
|
||||||
if (!HeapTupleIsValid(tup))
|
get_rule_expr((Node *) lfirst(expr->args), context);
|
||||||
elog(ERROR, "cache lookup for type %u failed",
|
else
|
||||||
procStruct->prorettype);
|
|
||||||
typeStruct = (Form_pg_type) GETSTRUCT(tup);
|
|
||||||
if (strncmp(NameStr(procStruct->proname),
|
|
||||||
NameStr(typeStruct->typname),
|
|
||||||
NAMEDATALEN) != 0)
|
|
||||||
{
|
|
||||||
get_rule_expr(tle->expr, context);
|
get_rule_expr(tle->expr, context);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* Finally (to be totally safe) the second argument must be a
|
|
||||||
* const and match the value in the results atttypmod.
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
second_arg = (Const *) lsecond(expr->args);
|
|
||||||
if (! IsA(second_arg, Const) ||
|
|
||||||
DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod)
|
|
||||||
{
|
|
||||||
get_rule_expr(tle->expr, context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* Whow - got it. Now get rid of the padding function
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
get_rule_expr((Node *) lfirst(expr->args), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user