30
30
*/
31
31
#include "postgres.h"
32
32
33
- #include "access/tupmacs.h"
34
33
#include "common/hashfn.h"
35
- #include "lib/stringinfo.h"
36
34
#include "libpq/pqformat.h"
37
35
#include "miscadmin.h"
36
+ #include "nodes/makefuncs.h"
38
37
#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"
40
42
#include "utils/builtins.h"
41
43
#include "utils/date.h"
42
44
#include "utils/lsyscache.h"
43
45
#include "utils/rangetypes.h"
44
46
#include "utils/timestamp.h"
45
- #include "varatt.h"
46
47
47
48
48
49
/* 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,
69
70
char typalign , int16 typlen , char typstorage );
70
71
static Pointer datum_write (Pointer ptr , Datum datum , bool typbyval ,
71
72
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 );
72
79
73
80
74
81
/*
@@ -2173,6 +2180,58 @@ make_empty_range(TypeCacheEntry *typcache)
2173
2180
return make_range (typcache , & lower , & upper , true, NULL );
2174
2181
}
2175
2182
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
+
2176
2235
2177
2236
/*
2178
2237
*----------------------------------------------------------
@@ -2715,3 +2774,180 @@ datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
2715
2774
2716
2775
return ptr ;
2717
2776
}
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