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:
@ -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);
|
||||
|
Reference in New Issue
Block a user