Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit075df6b

Browse files
committed
Add planner support functions for range operators <@ and @>.
These support functions will transform expressions with constantrange values into direct comparisons on the range bound values,which are frequently better-optimizable. The transformation isskipped however if it would require double evaluation of avolatile or expensive element expression.Along the way, add the range opfamily OID to range typcache entries,since load_rangetype_info has to compute that anyway and it seemssilly to duplicate the work later.Kim Johan Andersson and Jian He, reviewed by Laurenz AlbeDiscussion:https://postgr.es/m/94f64d1f-b8c0-b0c5-98bc-0793a34e0851@kimmet.dk
1 parentabb0b4f commit075df6b

File tree

8 files changed

+469
-11
lines changed

8 files changed

+469
-11
lines changed

‎src/backend/utils/adt/rangetypes.c

Lines changed: 240 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,20 @@
3030
*/
3131
#include"postgres.h"
3232

33-
#include"access/tupmacs.h"
3433
#include"common/hashfn.h"
35-
#include"lib/stringinfo.h"
3634
#include"libpq/pqformat.h"
3735
#include"miscadmin.h"
36+
#include"nodes/makefuncs.h"
3837
#include"nodes/miscnodes.h"
39-
#include"port/pg_bitutils.h"
38+
#include"nodes/supportnodes.h"
39+
#include"optimizer/clauses.h"
40+
#include"optimizer/cost.h"
41+
#include"optimizer/optimizer.h"
4042
#include"utils/builtins.h"
4143
#include"utils/date.h"
4244
#include"utils/lsyscache.h"
4345
#include"utils/rangetypes.h"
4446
#include"utils/timestamp.h"
45-
#include"varatt.h"
4647

4748

4849
/* fn_extra cache entry for one of the range I/O functions */
@@ -69,6 +70,12 @@ static Size datum_compute_size(Size data_length, Datum val, bool typbyval,
6970
chartypalign,int16typlen,chartypstorage);
7071
staticPointerdatum_write(Pointerptr,Datumdatum,booltypbyval,
7172
chartypalign,int16typlen,chartypstorage);
73+
staticNode*find_simplified_clause(PlannerInfo*root,
74+
Expr*rangeExpr,Expr*elemExpr);
75+
staticExpr*build_bound_expr(Expr*elemExpr,Datumval,
76+
boolisLowerBound,boolisInclusive,
77+
TypeCacheEntry*typeCache,
78+
Oidopfamily,Oidrng_collation);
7279

7380

7481
/*
@@ -2173,6 +2180,58 @@ make_empty_range(TypeCacheEntry *typcache)
21732180
returnmake_range(typcache,&lower,&upper, true,NULL);
21742181
}
21752182

2183+
/*
2184+
* Planner support function for elem_contained_by_range (<@ operator).
2185+
*/
2186+
Datum
2187+
elem_contained_by_range_support(PG_FUNCTION_ARGS)
2188+
{
2189+
Node*rawreq= (Node*)PG_GETARG_POINTER(0);
2190+
Node*ret=NULL;
2191+
2192+
if (IsA(rawreq,SupportRequestSimplify))
2193+
{
2194+
SupportRequestSimplify*req= (SupportRequestSimplify*)rawreq;
2195+
FuncExpr*fexpr=req->fcall;
2196+
Expr*leftop,
2197+
*rightop;
2198+
2199+
Assert(list_length(fexpr->args)==2);
2200+
leftop=linitial(fexpr->args);
2201+
rightop=lsecond(fexpr->args);
2202+
2203+
ret=find_simplified_clause(req->root,rightop,leftop);
2204+
}
2205+
2206+
PG_RETURN_POINTER(ret);
2207+
}
2208+
2209+
/*
2210+
* Planner support function for range_contains_elem (@> operator).
2211+
*/
2212+
Datum
2213+
range_contains_elem_support(PG_FUNCTION_ARGS)
2214+
{
2215+
Node*rawreq= (Node*)PG_GETARG_POINTER(0);
2216+
Node*ret=NULL;
2217+
2218+
if (IsA(rawreq,SupportRequestSimplify))
2219+
{
2220+
SupportRequestSimplify*req= (SupportRequestSimplify*)rawreq;
2221+
FuncExpr*fexpr=req->fcall;
2222+
Expr*leftop,
2223+
*rightop;
2224+
2225+
Assert(list_length(fexpr->args)==2);
2226+
leftop=linitial(fexpr->args);
2227+
rightop=lsecond(fexpr->args);
2228+
2229+
ret=find_simplified_clause(req->root,leftop,rightop);
2230+
}
2231+
2232+
PG_RETURN_POINTER(ret);
2233+
}
2234+
21762235

21772236
/*
21782237
*----------------------------------------------------------
@@ -2715,3 +2774,180 @@ datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
27152774

27162775
returnptr;
27172776
}
2777+
2778+
/*
2779+
* Common code for the elem_contained_by_range and range_contains_elem
2780+
* support functions. The caller has extracted the function argument
2781+
* expressions, and swapped them if necessary to pass the range first.
2782+
*
2783+
* Returns a simplified replacement expression, or NULL if we can't simplify.
2784+
*/
2785+
staticNode*
2786+
find_simplified_clause(PlannerInfo*root,Expr*rangeExpr,Expr*elemExpr)
2787+
{
2788+
RangeType*range;
2789+
TypeCacheEntry*rangetypcache;
2790+
RangeBoundlower;
2791+
RangeBoundupper;
2792+
boolempty;
2793+
2794+
/* can't do anything unless the range is a non-null constant */
2795+
if (!IsA(rangeExpr,Const)|| ((Const*)rangeExpr)->constisnull)
2796+
returnNULL;
2797+
range=DatumGetRangeTypeP(((Const*)rangeExpr)->constvalue);
2798+
2799+
rangetypcache=lookup_type_cache(RangeTypeGetOid(range),
2800+
TYPECACHE_RANGE_INFO);
2801+
if (rangetypcache->rngelemtype==NULL)
2802+
elog(ERROR,"type %u is not a range type",RangeTypeGetOid(range));
2803+
2804+
range_deserialize(rangetypcache,range,&lower,&upper,&empty);
2805+
2806+
if (empty)
2807+
{
2808+
/* if the range is empty, then there can be no matches */
2809+
returnmakeBoolConst(false, false);
2810+
}
2811+
elseif (lower.infinite&&upper.infinite)
2812+
{
2813+
/* the range has infinite bounds, so it matches everything */
2814+
returnmakeBoolConst(true, false);
2815+
}
2816+
else
2817+
{
2818+
/* at least one bound is available, we have something to work with */
2819+
TypeCacheEntry*elemTypcache=rangetypcache->rngelemtype;
2820+
Oidopfamily=rangetypcache->rng_opfamily;
2821+
Oidrng_collation=rangetypcache->rng_collation;
2822+
Expr*lowerExpr=NULL;
2823+
Expr*upperExpr=NULL;
2824+
2825+
if (!lower.infinite&& !upper.infinite)
2826+
{
2827+
/*
2828+
* When both bounds are present, we have a problem: the
2829+
* "simplified" clause would need to evaluate the elemExpr twice.
2830+
* That's definitely not okay if the elemExpr is volatile, and
2831+
* it's also unattractive if the elemExpr is expensive.
2832+
*/
2833+
QualCosteval_cost;
2834+
2835+
if (contain_volatile_functions((Node*)elemExpr))
2836+
returnNULL;
2837+
2838+
/*
2839+
* We define "expensive" as "contains any subplan or more than 10
2840+
* operators". Note that the subplan search has to be done
2841+
* explicitly, since cost_qual_eval() will barf on unplanned
2842+
* subselects.
2843+
*/
2844+
if (contain_subplans((Node*)elemExpr))
2845+
returnNULL;
2846+
cost_qual_eval_node(&eval_cost, (Node*)elemExpr,root);
2847+
if (eval_cost.startup+eval_cost.per_tuple>
2848+
10*cpu_operator_cost)
2849+
returnNULL;
2850+
}
2851+
2852+
/* Okay, try to build boundary comparison expressions */
2853+
if (!lower.infinite)
2854+
{
2855+
lowerExpr=build_bound_expr(elemExpr,
2856+
lower.val,
2857+
true,
2858+
lower.inclusive,
2859+
elemTypcache,
2860+
opfamily,
2861+
rng_collation);
2862+
if (lowerExpr==NULL)
2863+
returnNULL;
2864+
}
2865+
2866+
if (!upper.infinite)
2867+
{
2868+
/* Copy the elemExpr if we need two copies */
2869+
if (!lower.infinite)
2870+
elemExpr=copyObject(elemExpr);
2871+
upperExpr=build_bound_expr(elemExpr,
2872+
upper.val,
2873+
false,
2874+
upper.inclusive,
2875+
elemTypcache,
2876+
opfamily,
2877+
rng_collation);
2878+
if (upperExpr==NULL)
2879+
returnNULL;
2880+
}
2881+
2882+
if (lowerExpr!=NULL&&upperExpr!=NULL)
2883+
return (Node*)make_andclause(list_make2(lowerExpr,upperExpr));
2884+
elseif (lowerExpr!=NULL)
2885+
return (Node*)lowerExpr;
2886+
elseif (upperExpr!=NULL)
2887+
return (Node*)upperExpr;
2888+
else
2889+
{
2890+
Assert(false);
2891+
returnNULL;
2892+
}
2893+
}
2894+
}
2895+
2896+
/*
2897+
* Helper function for find_simplified_clause().
2898+
*
2899+
* Build the expression (elemExpr Operator val), where the operator is
2900+
* the appropriate member of the given opfamily depending on
2901+
* isLowerBound and isInclusive. typeCache is the typcache entry for
2902+
* the "val" value (presently, this will be the same type as elemExpr).
2903+
* rng_collation is the collation to use in the comparison.
2904+
*
2905+
* Return NULL on failure (if, for some reason, we can't find the operator).
2906+
*/
2907+
staticExpr*
2908+
build_bound_expr(Expr*elemExpr,Datumval,
2909+
boolisLowerBound,boolisInclusive,
2910+
TypeCacheEntry*typeCache,
2911+
Oidopfamily,Oidrng_collation)
2912+
{
2913+
OidelemType=typeCache->type_id;
2914+
int16elemTypeLen=typeCache->typlen;
2915+
boolelemByValue=typeCache->typbyval;
2916+
OidelemCollation=typeCache->typcollation;
2917+
int16strategy;
2918+
Oidoproid;
2919+
Expr*constExpr;
2920+
2921+
/* Identify the comparison operator to use */
2922+
if (isLowerBound)
2923+
strategy=isInclusive ?BTGreaterEqualStrategyNumber :BTGreaterStrategyNumber;
2924+
else
2925+
strategy=isInclusive ?BTLessEqualStrategyNumber :BTLessStrategyNumber;
2926+
2927+
/*
2928+
* We could use exprType(elemExpr) here, if it ever becomes possible that
2929+
* elemExpr is not the exact same type as the range elements.
2930+
*/
2931+
oproid=get_opfamily_member(opfamily,elemType,elemType,strategy);
2932+
2933+
/* We don't really expect failure here, but just in case ... */
2934+
if (!OidIsValid(oproid))
2935+
returnNULL;
2936+
2937+
/* OK, convert "val" to a full-fledged Const node, and make the OpExpr */
2938+
constExpr= (Expr*)makeConst(elemType,
2939+
-1,
2940+
elemCollation,
2941+
elemTypeLen,
2942+
val,
2943+
false,
2944+
elemByValue);
2945+
2946+
returnmake_opclause(oproid,
2947+
BOOLOID,
2948+
false,
2949+
elemExpr,
2950+
constExpr,
2951+
InvalidOid,
2952+
rng_collation);
2953+
}

‎src/backend/utils/adt/rangetypes_selfuncs.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,10 @@ rangesel(PG_FUNCTION_ARGS)
196196
elseif (operator==OID_RANGE_ELEM_CONTAINED_OP)
197197
{
198198
/*
199-
* Here, the Var is the elem, not the range. For now we just punt and
200-
* return the default estimate. In future we could disassemble the
201-
* range constant and apply scalarineqsel ...
199+
* Here, the Var is the elem, not the range. In typical cases
200+
* elem_contained_by_range_support will have simplified this case, so
201+
* that we won't get here. If we do get here we'll fall back on a
202+
* default estimate.
202203
*/
203204
}
204205
elseif (((Const*)other)->consttype==vardata.vartype)

‎src/backend/utils/cache/typcache.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,7 @@ load_rangetype_info(TypeCacheEntry *typentry)
940940
/* get opclass properties and look up the comparison function */
941941
opfamilyOid=get_opclass_family(opclassOid);
942942
opcintype=get_opclass_input_type(opclassOid);
943+
typentry->rng_opfamily=opfamilyOid;
943944

944945
cmpFnOid=get_opfamily_proc(opfamilyOid,opcintype,opcintype,
945946
BTORDER_PROC);

‎src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/*yyyymmddN */
60-
#defineCATALOG_VERSION_NO202401191
60+
#defineCATALOG_VERSION_NO202401201
6161

6262
#endif

‎src/include/catalog/pg_proc.dat

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10503,13 +10503,15 @@
1050310503
proname => 'range_overlaps', prorettype => 'bool',
1050410504
proargtypes => 'anyrange anyrange', prosrc => 'range_overlaps' },
1050510505
{ oid => '3858',
10506-
proname => 'range_contains_elem', prorettype => 'bool',
10507-
proargtypes => 'anyrange anyelement', prosrc => 'range_contains_elem' },
10506+
proname => 'range_contains_elem', prosupport => 'range_contains_elem_support',
10507+
prorettype => 'bool', proargtypes => 'anyrange anyelement',
10508+
prosrc => 'range_contains_elem' },
1050810509
{ oid => '3859',
1050910510
proname => 'range_contains', prorettype => 'bool',
1051010511
proargtypes => 'anyrange anyrange', prosrc => 'range_contains' },
1051110512
{ oid => '3860',
10512-
proname => 'elem_contained_by_range', prorettype => 'bool',
10513+
proname => 'elem_contained_by_range',
10514+
prosupport => 'elem_contained_by_range_support', prorettype => 'bool',
1051310515
proargtypes => 'anyelement anyrange', prosrc => 'elem_contained_by_range' },
1051410516
{ oid => '3861',
1051510517
proname => 'range_contained_by', prorettype => 'bool',
@@ -10532,6 +10534,12 @@
1053210534
{ oid => '3867',
1053310535
proname => 'range_union', prorettype => 'anyrange',
1053410536
proargtypes => 'anyrange anyrange', prosrc => 'range_union' },
10537+
{ oid => '9998', descr => 'planner support for range_contains_elem',
10538+
proname => 'range_contains_elem_support', prorettype => 'internal',
10539+
proargtypes => 'internal', prosrc => 'range_contains_elem_support' },
10540+
{ oid => '9999', descr => 'planner support for elem_contained_by_range',
10541+
proname => 'elem_contained_by_range_support', prorettype => 'internal',
10542+
proargtypes => 'internal', prosrc => 'elem_contained_by_range_support' },
1053510543
{ oid => '4057',
1053610544
descr => 'the smallest range which includes both of the given ranges',
1053710545
proname => 'range_merge', prorettype => 'anyrange',

‎src/include/utils/typcache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ typedef struct TypeCacheEntry
9696
* btree comparison function.
9797
*/
9898
structTypeCacheEntry*rngelemtype;/* range's element type */
99+
Oidrng_opfamily;/* opfamily to use for range comparisons */
99100
Oidrng_collation;/* collation for comparisons, if any */
100101
FmgrInforng_cmp_proc_finfo;/* comparison function */
101102
FmgrInforng_canonical_finfo;/* canonicalization function, if any */

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp