1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Code review for protransform patches.

Fix loss of previous expression-simplification work when a transform
function fires: we must not simply revert to untransformed input tree.
Instead build a dummy FuncExpr node to pass to the transform function.
This has the additional advantage of providing a simpler, more uniform
API for transform functions.

Move documentation to a somewhat less buried spot, relocate some
poorly-placed code, be more wary of null constants and invalid typmod
values, add an opr_sanity check on protransform function signatures,
and some other minor cosmetic adjustments.

Note: although this patch touches pg_proc.h, no need for catversion
bump, because the changes are cosmetic and don't actually change the
intended catalog contents.
This commit is contained in:
Tom Lane
2012-03-23 17:29:57 -04:00
parent e08b4101e1
commit 0339047bc9
15 changed files with 205 additions and 172 deletions

View File

@ -107,11 +107,11 @@ static List *simplify_and_arguments(List *args,
eval_const_expressions_context *context,
bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Expr *oldexpr, Oid funcid,
Oid result_type, int32 result_typmod, Oid result_collid,
Oid input_collid, List **args,
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
bool allow_non_const,
eval_const_expressions_context *context);
static List *reorder_function_arguments(List *args, Oid result_type,
HeapTuple func_tuple,
@ -2332,8 +2332,7 @@ eval_const_expressions_mutator(Node *node,
* length coercion; we want to preserve the typmod in the
* eventual Const if so.
*/
simple = simplify_function((Expr *) expr,
expr->funcid,
simple = simplify_function(expr->funcid,
expr->funcresulttype,
exprTypmod(node),
expr->funccollid,
@ -2389,8 +2388,7 @@ eval_const_expressions_mutator(Node *node,
* Code for op/func reduction is pretty bulky, so split it out
* as a separate function.
*/
simple = simplify_function((Expr *) expr,
expr->opfuncid,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
expr->opcollid,
expr->inputcollid,
@ -2491,8 +2489,7 @@ eval_const_expressions_mutator(Node *node,
* Code for op/func reduction is pretty bulky, so split it
* out as a separate function.
*/
simple = simplify_function((Expr *) expr,
expr->opfuncid,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
expr->opcollid,
expr->inputcollid,
@ -2698,8 +2695,7 @@ eval_const_expressions_mutator(Node *node,
getTypeInputInfo(expr->resulttype,
&infunc, &intypioparam);
simple = simplify_function(NULL,
outfunc,
simple = simplify_function(outfunc,
CSTRINGOID, -1,
InvalidOid,
InvalidOid,
@ -2728,8 +2724,7 @@ eval_const_expressions_mutator(Node *node,
false,
true));
simple = simplify_function(NULL,
infunc,
simple = simplify_function(infunc,
expr->resulttype, -1,
expr->resultcollid,
InvalidOid,
@ -3581,15 +3576,11 @@ simplify_boolean_equality(Oid opno, List *args)
* Subroutine for eval_const_expressions: try to simplify a function call
* (which might originally have been an operator; we don't care)
*
* Inputs are the original expression (can be NULL), function OID, actual
* result type OID (which is needed for polymorphic functions), result typmod,
* result collation, the input collation to use for the function, the
* pre-simplified argument list, and some flags; also the context data for
* eval_const_expressions. In common cases, several of the arguments could be
* derived from the original expression. Sending them separately avoids
* duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
* A NULL original expression disables use of transform functions while
* retaining all other behaviors.
* Inputs are the function OID, actual result type OID (which is needed for
* polymorphic functions), result typmod, result collation,
* the input collation to use for the function,
* the pre-simplified argument list, and some flags;
* also the context data for eval_const_expressions.
*
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
@ -3601,28 +3592,32 @@ simplify_boolean_equality(Oid opno, List *args)
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
simplify_function(Expr *oldexpr, Oid funcid,
Oid result_type, int32 result_typmod, Oid result_collid,
Oid input_collid, List **args,
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
bool allow_non_const,
eval_const_expressions_context *context)
{
HeapTuple func_tuple;
Form_pg_proc func_form;
Expr *newexpr;
Oid transform;
/*
* We have three strategies for simplification: execute the function to
* deliver a constant result, use a transform function to generate a
* substitute node tree, or expand in-line the body of the function
* definition (which only works for simple SQL-language functions, but
* that is a common case). Each needs access to the function's pg_proc
* tuple, so fetch it just once.
* that is a common case). Each case needs access to the function's
* pg_proc tuple, so fetch it just once.
*
* Note: the allow_non_const flag suppresses both the second and third
* strategies; so if !allow_non_const, simplify_function can only return
* a Const or NULL. Argument-list rewriting happens anyway, though.
*/
func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
func_form = (Form_pg_proc) GETSTRUCT(func_tuple);
/*
* While we have the tuple, reorder named arguments and add default
@ -3631,48 +3626,38 @@ simplify_function(Expr *oldexpr, Oid funcid,
if (has_named_args)
*args = reorder_function_arguments(*args, result_type, func_tuple,
context);
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
else if (func_form->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod,
result_collid, input_collid, *args,
func_tuple, context);
/*
* Some functions calls can be simplified at plan time based on properties
* specific to the function. For example, "varchar(s::varchar(4), 8,
* true)" simplifies to "s::varchar(4)", and "int4mul(n, 1)" could
* simplify to "n". To define such function-specific optimizations, write
* a "transform function" and store its OID in the pg_proc.protransform of
* the primary function. Give each transform function the signature
* "protransform(internal) RETURNS internal". The argument, internally an
* Expr *, is the node representing a call to the primary function. If
* the transform function's study of that node proves that a simplified
* Expr substitutes for all possible concrete calls represented thereby,
* return that simplified Expr. Otherwise, return the NULL pointer.
*
* Currently, the specific Expr nodetag can be FuncExpr, OpExpr or
* DistinctExpr. This list may change in the future. The function should
* check the nodetag and return the NULL pointer for unexpected inputs.
*
* We make no guarantee that PostgreSQL will never call the primary
* function in cases that the transform function would simplify. Ensure
* rigorous equivalence between the simplified expression and an actual
* call to the primary function.
*
* Currently, this facility is undocumented and not exposed to users at
* the SQL level. Core length coercion casts use it to avoid calls
* guaranteed to return their input unchanged. This in turn allows ALTER
* TABLE ALTER TYPE to avoid rewriting tables for some typmod changes. In
* the future, this facility may find other applications, like simplifying
* x*0, x*1, and x+0.
*/
transform = ((Form_pg_proc) GETSTRUCT(func_tuple))->protransform;
if (!newexpr && OidIsValid(transform) && oldexpr)
newexpr = (Expr *) DatumGetPointer(OidFunctionCall1(transform,
PointerGetDatum(oldexpr)));
if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
{
/*
* Build a dummy FuncExpr node containing the simplified arg list. We
* use this approach to present a uniform interface to the transform
* function regardless of how the function is actually being invoked.
*/
FuncExpr fexpr;
if (!newexpr && allow_inline)
fexpr.xpr.type = T_FuncExpr;
fexpr.funcid = funcid;
fexpr.funcresulttype = result_type;
fexpr.funcretset = func_form->proretset;
fexpr.funcformat = COERCE_DONTCARE;
fexpr.funccollid = result_collid;
fexpr.inputcollid = input_collid;
fexpr.args = *args;
fexpr.location = -1;
newexpr = (Expr *)
DatumGetPointer(OidFunctionCall1(func_form->protransform,
PointerGetDatum(&fexpr)));
}
if (!newexpr && allow_non_const)
newexpr = inline_function(funcid, result_type, result_collid,
input_collid, *args,
func_tuple, context);