Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0339047

Browse files
committed
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.
1 parent e08b410 commit 0339047

File tree

15 files changed

+205
-172
lines changed

15 files changed

+205
-172
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4398,7 +4398,8 @@
43984398
<entry><structfield>protransform</structfield></entry>
43994399
<entry><type>regproc</type></entry>
44004400
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
4401-
<entry>Calls to function can be simplified by this other function</entry>
4401+
<entry>Calls to this function can be simplified by this other function
4402+
(see <xref linkend="xfunc-transform-functions">)</entry>
44024403
</row>
44034404

44044405
<row>

doc/src/sgml/xfunc.sgml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3177,6 +3177,40 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
31773177
</para>
31783178
</sect2>
31793179

3180+
<sect2 id="xfunc-transform-functions">
3181+
<title>Transform Functions</title>
3182+
3183+
<para>
3184+
Some function calls can be simplified during planning based on
3185+
properties specific to the function. For example,
3186+
<literal>int4mul(n, 1)</> could be simplified to just <literal>n</>.
3187+
To define such function-specific optimizations, write a
3188+
<firstterm>transform function</> and place its OID in the
3189+
<structfield>protransform</> field of the primary function's
3190+
<structname>pg_proc</> entry. The transform function must have the SQL
3191+
signature <literal>protransform(internal) RETURNS internal</>. The
3192+
argument, actually <type>FuncExpr *</>, is a dummy node representing a
3193+
call to the primary function. If the transform function's study of the
3194+
expression tree proves that a simplified expression tree can substitute
3195+
for all possible concrete calls represented thereby, build and return
3196+
that simplified expression. Otherwise, return a <literal>NULL</>
3197+
pointer (<emphasis>not</> a SQL null).
3198+
</para>
3199+
3200+
<para>
3201+
We make no guarantee that <productname>PostgreSQL</> will never call the
3202+
primary function in cases that the transform function could simplify.
3203+
Ensure rigorous equivalence between the simplified expression and an
3204+
actual call to the primary function.
3205+
</para>
3206+
3207+
<para>
3208+
Currently, this facility is not exposed to users at the SQL level
3209+
because of security concerns, so it is only practical to use for
3210+
optimizing built-in functions.
3211+
</para>
3212+
</sect2>
3213+
31803214
<sect2>
31813215
<title>Shared Memory and LWLocks</title>
31823216

src/backend/nodes/nodeFuncs.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "catalog/pg_collation.h"
1818
#include "catalog/pg_type.h"
1919
#include "miscadmin.h"
20+
#include "nodes/makefuncs.h"
2021
#include "nodes/nodeFuncs.h"
2122
#include "nodes/relation.h"
2223
#include "utils/builtins.h"
@@ -547,6 +548,30 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
547548
return false;
548549
}
549550

551+
/*
552+
* relabel_to_typmod
553+
* Add a RelabelType node that changes just the typmod of the expression.
554+
*
555+
* This is primarily intended to be used during planning. Therefore, it
556+
* strips any existing RelabelType nodes to maintain the planner's invariant
557+
* that there are not adjacent RelabelTypes, and it uses COERCE_DONTCARE
558+
* which would typically be inappropriate earlier.
559+
*/
560+
Node *
561+
relabel_to_typmod(Node *expr, int32 typmod)
562+
{
563+
Oid type = exprType(expr);
564+
Oid coll = exprCollation(expr);
565+
566+
/* Strip any existing RelabelType node(s) */
567+
while (expr && IsA(expr, RelabelType))
568+
expr = (Node *) ((RelabelType *) expr)->arg;
569+
570+
/* Apply new typmod, preserving the previous exposed type and collation */
571+
return (Node *) makeRelabelType((Expr *) expr, type, typmod, coll,
572+
COERCE_DONTCARE);
573+
}
574+
550575
/*
551576
* expression_returns_set
552577
* Test whether an expression returns a set result.
@@ -2694,7 +2719,9 @@ query_or_expression_tree_mutator(Node *node,
26942719
* that could appear under it, but not other statement types.
26952720
*/
26962721
bool
2697-
raw_expression_tree_walker(Node *node, bool (*walker) (), void *context)
2722+
raw_expression_tree_walker(Node *node,
2723+
bool (*walker) (),
2724+
void *context)
26982725
{
26992726
ListCell *temp;
27002727

src/backend/optimizer/util/clauses.c

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,11 @@ static List *simplify_and_arguments(List *args,
107107
eval_const_expressions_context *context,
108108
bool *haveNull, bool *forceFalse);
109109
static Node *simplify_boolean_equality(Oid opno, List *args);
110-
static Expr *simplify_function(Expr *oldexpr, Oid funcid,
111-
Oid result_type, int32 result_typmod, Oid result_collid,
112-
Oid input_collid, List **args,
110+
static Expr *simplify_function(Oid funcid,
111+
Oid result_type, int32 result_typmod,
112+
Oid result_collid, Oid input_collid, List **args,
113113
bool has_named_args,
114-
bool allow_inline,
114+
bool allow_non_const,
115115
eval_const_expressions_context *context);
116116
static List *reorder_function_arguments(List *args, Oid result_type,
117117
HeapTuple func_tuple,
@@ -2332,8 +2332,7 @@ eval_const_expressions_mutator(Node *node,
23322332
* length coercion; we want to preserve the typmod in the
23332333
* eventual Const if so.
23342334
*/
2335-
simple = simplify_function((Expr *) expr,
2336-
expr->funcid,
2335+
simple = simplify_function(expr->funcid,
23372336
expr->funcresulttype,
23382337
exprTypmod(node),
23392338
expr->funccollid,
@@ -2389,8 +2388,7 @@ eval_const_expressions_mutator(Node *node,
23892388
* Code for op/func reduction is pretty bulky, so split it out
23902389
* as a separate function.
23912390
*/
2392-
simple = simplify_function((Expr *) expr,
2393-
expr->opfuncid,
2391+
simple = simplify_function(expr->opfuncid,
23942392
expr->opresulttype, -1,
23952393
expr->opcollid,
23962394
expr->inputcollid,
@@ -2491,8 +2489,7 @@ eval_const_expressions_mutator(Node *node,
24912489
* Code for op/func reduction is pretty bulky, so split it
24922490
* out as a separate function.
24932491
*/
2494-
simple = simplify_function((Expr *) expr,
2495-
expr->opfuncid,
2492+
simple = simplify_function(expr->opfuncid,
24962493
expr->opresulttype, -1,
24972494
expr->opcollid,
24982495
expr->inputcollid,
@@ -2698,8 +2695,7 @@ eval_const_expressions_mutator(Node *node,
26982695
getTypeInputInfo(expr->resulttype,
26992696
&infunc, &intypioparam);
27002697

2701-
simple = simplify_function(NULL,
2702-
outfunc,
2698+
simple = simplify_function(outfunc,
27032699
CSTRINGOID, -1,
27042700
InvalidOid,
27052701
InvalidOid,
@@ -2728,8 +2724,7 @@ eval_const_expressions_mutator(Node *node,
27282724
false,
27292725
true));
27302726

2731-
simple = simplify_function(NULL,
2732-
infunc,
2727+
simple = simplify_function(infunc,
27332728
expr->resulttype, -1,
27342729
expr->resultcollid,
27352730
InvalidOid,
@@ -3581,15 +3576,11 @@ simplify_boolean_equality(Oid opno, List *args)
35813576
* Subroutine for eval_const_expressions: try to simplify a function call
35823577
* (which might originally have been an operator; we don't care)
35833578
*
3584-
* Inputs are the original expression (can be NULL), function OID, actual
3585-
* result type OID (which is needed for polymorphic functions), result typmod,
3586-
* result collation, the input collation to use for the function, the
3587-
* pre-simplified argument list, and some flags; also the context data for
3588-
* eval_const_expressions. In common cases, several of the arguments could be
3589-
* derived from the original expression. Sending them separately avoids
3590-
* duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
3591-
* A NULL original expression disables use of transform functions while
3592-
* retaining all other behaviors.
3579+
* Inputs are the function OID, actual result type OID (which is needed for
3580+
* polymorphic functions), result typmod, result collation,
3581+
* the input collation to use for the function,
3582+
* the pre-simplified argument list, and some flags;
3583+
* also the context data for eval_const_expressions.
35933584
*
35943585
* Returns a simplified expression if successful, or NULL if cannot
35953586
* simplify the function call.
@@ -3601,28 +3592,32 @@ simplify_boolean_equality(Oid opno, List *args)
36013592
* pass-by-reference, and it may get modified even if simplification fails.
36023593
*/
36033594
static Expr *
3604-
simplify_function(Expr *oldexpr, Oid funcid,
3605-
Oid result_type, int32 result_typmod, Oid result_collid,
3606-
Oid input_collid, List **args,
3595+
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
3596+
Oid result_collid, Oid input_collid, List **args,
36073597
bool has_named_args,
3608-
bool allow_inline,
3598+
bool allow_non_const,
36093599
eval_const_expressions_context *context)
36103600
{
36113601
HeapTuple func_tuple;
3602+
Form_pg_proc func_form;
36123603
Expr *newexpr;
3613-
Oid transform;
36143604

36153605
/*
36163606
* We have three strategies for simplification: execute the function to
36173607
* deliver a constant result, use a transform function to generate a
36183608
* substitute node tree, or expand in-line the body of the function
36193609
* definition (which only works for simple SQL-language functions, but
3620-
* that is a common case). Each needs access to the function's pg_proc
3621-
* tuple, so fetch it just once.
3610+
* that is a common case). Each case needs access to the function's
3611+
* pg_proc tuple, so fetch it just once.
3612+
*
3613+
* Note: the allow_non_const flag suppresses both the second and third
3614+
* strategies; so if !allow_non_const, simplify_function can only return
3615+
* a Const or NULL. Argument-list rewriting happens anyway, though.
36223616
*/
36233617
func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
36243618
if (!HeapTupleIsValid(func_tuple))
36253619
elog(ERROR, "cache lookup failed for function %u", funcid);
3620+
func_form = (Form_pg_proc) GETSTRUCT(func_tuple);
36263621

36273622
/*
36283623
* While we have the tuple, reorder named arguments and add default
@@ -3631,48 +3626,38 @@ simplify_function(Expr *oldexpr, Oid funcid,
36313626
if (has_named_args)
36323627
*args = reorder_function_arguments(*args, result_type, func_tuple,
36333628
context);
3634-
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
3629+
else if (func_form->pronargs > list_length(*args))
36353630
*args = add_function_defaults(*args, result_type, func_tuple, context);
36363631

36373632
newexpr = evaluate_function(funcid, result_type, result_typmod,
36383633
result_collid, input_collid, *args,
36393634
func_tuple, context);
36403635

3641-
/*
3642-
* Some functions calls can be simplified at plan time based on properties
3643-
* specific to the function. For example, "varchar(s::varchar(4), 8,
3644-
* true)" simplifies to "s::varchar(4)", and "int4mul(n, 1)" could
3645-
* simplify to "n". To define such function-specific optimizations, write
3646-
* a "transform function" and store its OID in the pg_proc.protransform of
3647-
* the primary function. Give each transform function the signature
3648-
* "protransform(internal) RETURNS internal". The argument, internally an
3649-
* Expr *, is the node representing a call to the primary function. If
3650-
* the transform function's study of that node proves that a simplified
3651-
* Expr substitutes for all possible concrete calls represented thereby,
3652-
* return that simplified Expr. Otherwise, return the NULL pointer.
3653-
*
3654-
* Currently, the specific Expr nodetag can be FuncExpr, OpExpr or
3655-
* DistinctExpr. This list may change in the future. The function should
3656-
* check the nodetag and return the NULL pointer for unexpected inputs.
3657-
*
3658-
* We make no guarantee that PostgreSQL will never call the primary
3659-
* function in cases that the transform function would simplify. Ensure
3660-
* rigorous equivalence between the simplified expression and an actual
3661-
* call to the primary function.
3662-
*
3663-
* Currently, this facility is undocumented and not exposed to users at
3664-
* the SQL level. Core length coercion casts use it to avoid calls
3665-
* guaranteed to return their input unchanged. This in turn allows ALTER
3666-
* TABLE ALTER TYPE to avoid rewriting tables for some typmod changes. In
3667-
* the future, this facility may find other applications, like simplifying
3668-
* x*0, x*1, and x+0.
3669-
*/
3670-
transform = ((Form_pg_proc) GETSTRUCT(func_tuple))->protransform;
3671-
if (!newexpr && OidIsValid(transform) && oldexpr)
3672-
newexpr = (Expr *) DatumGetPointer(OidFunctionCall1(transform,
3673-
PointerGetDatum(oldexpr)));
3636+
if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
3637+
{
3638+
/*
3639+
* Build a dummy FuncExpr node containing the simplified arg list. We
3640+
* use this approach to present a uniform interface to the transform
3641+
* function regardless of how the function is actually being invoked.
3642+
*/
3643+
FuncExpr fexpr;
3644+
3645+
fexpr.xpr.type = T_FuncExpr;
3646+
fexpr.funcid = funcid;
3647+
fexpr.funcresulttype = result_type;
3648+
fexpr.funcretset = func_form->proretset;
3649+
fexpr.funcformat = COERCE_DONTCARE;
3650+
fexpr.funccollid = result_collid;
3651+
fexpr.inputcollid = input_collid;
3652+
fexpr.args = *args;
3653+
fexpr.location = -1;
3654+
3655+
newexpr = (Expr *)
3656+
DatumGetPointer(OidFunctionCall1(func_form->protransform,
3657+
PointerGetDatum(&fexpr)));
3658+
}
36743659

3675-
if (!newexpr && allow_inline)
3660+
if (!newexpr && allow_non_const)
36763661
newexpr = inline_function(funcid, result_type, result_collid,
36773662
input_collid, *args,
36783663
func_tuple, context);

src/backend/parser/parse_clause.c

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,25 +2271,3 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause)
22712271

22722272
return node;
22732273
}
2274-
2275-
/*
2276-
* relabel_to_typmod
2277-
* Add a RelabelType node that changes just the typmod, and remove all
2278-
* now-superfluous RelabelType nodes beneath it.
2279-
*/
2280-
Node *
2281-
relabel_to_typmod(Node *expr, int32 typmod)
2282-
{
2283-
Oid type = exprType(expr);
2284-
Oid coll = exprCollation(expr);
2285-
2286-
/*
2287-
* Strip any existing RelabelType, then add one. This is to preserve the
2288-
* invariant of no redundant RelabelTypes.
2289-
*/
2290-
while (IsA(expr, RelabelType))
2291-
expr = (Node *) ((RelabelType *) expr)->arg;
2292-
2293-
return (Node *) makeRelabelType((Expr *) expr, type, typmod, coll,
2294-
COERCE_DONTCARE);
2295-
}

src/backend/utils/adt/datetime.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include "funcapi.h"
2525
#include "miscadmin.h"
2626
#include "nodes/nodeFuncs.h"
27-
#include "parser/parse_clause.h"
2827
#include "utils/builtins.h"
2928
#include "utils/date.h"
3029
#include "utils/datetime.h"
@@ -4153,32 +4152,34 @@ CheckDateTokenTables(void)
41534152
}
41544153

41554154
/*
4156-
* Helper for temporal protransform functions. Types time, timetz, timestamp
4157-
* and timestamptz each have a range of allowed precisions. An unspecified
4158-
* precision is rigorously equivalent to the highest specifiable precision.
4155+
* Common code for temporal protransform functions. Types time, timetz,
4156+
* timestamp and timestamptz each have a range of allowed precisions. An
4157+
* unspecified precision is rigorously equivalent to the highest specifiable
4158+
* precision.
4159+
*
4160+
* Note: timestamp_scale throws an error when the typmod is out of range, but
4161+
* we can't get there from a cast: our typmodin will have caught it already.
41594162
*/
41604163
Node *
41614164
TemporalTransform(int32 max_precis, Node *node)
41624165
{
41634166
FuncExpr *expr = (FuncExpr *) node;
4164-
Node *typmod;
41654167
Node *ret = NULL;
4168+
Node *typmod;
41664169

4167-
if (!IsA(expr, FuncExpr))
4168-
return ret;
4170+
Assert(IsA(expr, FuncExpr));
4171+
Assert(list_length(expr->args) >= 2);
41694172

4170-
Assert(list_length(expr->args) == 2);
4171-
typmod = lsecond(expr->args);
4173+
typmod = (Node *) lsecond(expr->args);
41724174

4173-
if (IsA(typmod, Const))
4175+
if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
41744176
{
4175-
Node *source = linitial(expr->args);
4177+
Node *source = (Node *) linitial(expr->args);
41764178
int32 old_precis = exprTypmod(source);
41774179
int32 new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
41784180

4179-
if (new_precis == -1 ||
4180-
new_precis == max_precis ||
4181-
(old_precis != -1 && new_precis >= old_precis))
4181+
if (new_precis < 0 || new_precis == max_precis ||
4182+
(old_precis >= 0 && new_precis >= old_precis))
41824183
ret = relabel_to_typmod(source, new_precis);
41834184
}
41844185

0 commit comments

Comments
 (0)