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 char typalign , int16 typlen , char typstorage );
7071static Pointer datum_write (Pointer ptr , Datum datum , bool typbyval ,
7172 char typalign , int16 typlen , char typstorage );
73+ static Node * find_simplified_clause (PlannerInfo * root ,
74+ Expr * rangeExpr , Expr * elemExpr );
75+ static Expr * build_bound_expr (Expr * elemExpr , Datum val ,
76+ bool isLowerBound , bool isInclusive ,
77+ TypeCacheEntry * typeCache ,
78+ Oid opfamily , Oid rng_collation );
7279
7380
7481/*
@@ -2173,6 +2180,58 @@ make_empty_range(TypeCacheEntry *typcache)
21732180 return make_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 return ptr ;
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+ static Node *
2786+ find_simplified_clause (PlannerInfo * root , Expr * rangeExpr , Expr * elemExpr )
2787+ {
2788+ RangeType * range ;
2789+ TypeCacheEntry * rangetypcache ;
2790+ RangeBound lower ;
2791+ RangeBound upper ;
2792+ bool empty ;
2793+
2794+ /* can't do anything unless the range is a non-null constant */
2795+ if (!IsA (rangeExpr , Const ) || ((Const * ) rangeExpr )-> constisnull )
2796+ return NULL ;
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+ return makeBoolConst (false, false);
2810+ }
2811+ else if (lower .infinite && upper .infinite )
2812+ {
2813+ /* the range has infinite bounds, so it matches everything */
2814+ return makeBoolConst (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+ Oid opfamily = rangetypcache -> rng_opfamily ;
2821+ Oid rng_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+ QualCost eval_cost ;
2834+
2835+ if (contain_volatile_functions ((Node * ) elemExpr ))
2836+ return NULL ;
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+ return NULL ;
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+ return NULL ;
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+ return NULL ;
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+ return NULL ;
2880+ }
2881+
2882+ if (lowerExpr != NULL && upperExpr != NULL )
2883+ return (Node * ) make_andclause (list_make2 (lowerExpr , upperExpr ));
2884+ else if (lowerExpr != NULL )
2885+ return (Node * ) lowerExpr ;
2886+ else if (upperExpr != NULL )
2887+ return (Node * ) upperExpr ;
2888+ else
2889+ {
2890+ Assert (false);
2891+ return NULL ;
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+ static Expr *
2908+ build_bound_expr (Expr * elemExpr , Datum val ,
2909+ bool isLowerBound , bool isInclusive ,
2910+ TypeCacheEntry * typeCache ,
2911+ Oid opfamily , Oid rng_collation )
2912+ {
2913+ Oid elemType = typeCache -> type_id ;
2914+ int16 elemTypeLen = typeCache -> typlen ;
2915+ bool elemByValue = typeCache -> typbyval ;
2916+ Oid elemCollation = typeCache -> typcollation ;
2917+ int16 strategy ;
2918+ Oid oproid ;
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+ return NULL ;
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+ return make_opclause (oproid ,
2947+ BOOLOID ,
2948+ false,
2949+ elemExpr ,
2950+ constExpr ,
2951+ InvalidOid ,
2952+ rng_collation );
2953+ }
0 commit comments