Commit 3d660d33 authored by Tom Lane's avatar Tom Lane

Fix assorted oversights in range selectivity estimation.

calc_rangesel() failed outright when comparing range variables to empty
constant ranges with < or >=, as a result of missing cases in a switch.
It also produced a bogus estimate for > comparison to an empty range.

On top of that, the >= and > cases were mislabeled throughout.  For
nonempty constant ranges, they managed to produce the right answers
anyway as a result of counterbalancing typos.

Also, default_range_selectivity() omitted cases for elem <@ range,
range &< range, and range &> range, so that rather dubious defaults
were applied for these operators.

In passing, rearrange the code in rangesel() so that the elem <@ range
case is handled in a less opaque fashion.

Report and patch by Emre Hasegeli, some additional work by me
parent 68fa75f3
...@@ -73,6 +73,7 @@ default_range_selectivity(Oid operator) ...@@ -73,6 +73,7 @@ default_range_selectivity(Oid operator)
return 0.005; return 0.005;
case OID_RANGE_CONTAINS_ELEM_OP: case OID_RANGE_CONTAINS_ELEM_OP:
case OID_RANGE_ELEM_CONTAINED_OP:
/* /*
* "range @> elem" is more or less identical to a scalar * "range @> elem" is more or less identical to a scalar
...@@ -86,6 +87,8 @@ default_range_selectivity(Oid operator) ...@@ -86,6 +87,8 @@ default_range_selectivity(Oid operator)
case OID_RANGE_GREATER_EQUAL_OP: case OID_RANGE_GREATER_EQUAL_OP:
case OID_RANGE_LEFT_OP: case OID_RANGE_LEFT_OP:
case OID_RANGE_RIGHT_OP: case OID_RANGE_RIGHT_OP:
case OID_RANGE_OVERLAPS_LEFT_OP:
case OID_RANGE_OVERLAPS_RIGHT_OP:
/* these are similar to regular scalar inequalities */ /* these are similar to regular scalar inequalities */
return DEFAULT_INEQ_SEL; return DEFAULT_INEQ_SEL;
...@@ -109,7 +112,7 @@ rangesel(PG_FUNCTION_ARGS) ...@@ -109,7 +112,7 @@ rangesel(PG_FUNCTION_ARGS)
Node *other; Node *other;
bool varonleft; bool varonleft;
Selectivity selec; Selectivity selec;
TypeCacheEntry *typcache; TypeCacheEntry *typcache = NULL;
RangeType *constrange = NULL; RangeType *constrange = NULL;
/* /*
...@@ -186,18 +189,27 @@ rangesel(PG_FUNCTION_ARGS) ...@@ -186,18 +189,27 @@ rangesel(PG_FUNCTION_ARGS)
constrange = range_serialize(typcache, &lower, &upper, false); constrange = range_serialize(typcache, &lower, &upper, false);
} }
} }
else else if (operator == OID_RANGE_ELEM_CONTAINED_OP)
{
/*
* Here, the Var is the elem, not the range. For now we just punt and
* return the default estimate. In future we could disassemble the
* range constant and apply scalarineqsel ...
*/
}
else if (((Const *) other)->consttype == vardata.vartype)
{ {
typcache = range_get_typcache(fcinfo, ((Const *) other)->consttype); /* Both sides are the same range type */
typcache = range_get_typcache(fcinfo, vardata.vartype);
if (((Const *) other)->consttype == vardata.vartype) constrange = DatumGetRangeType(((Const *) other)->constvalue);
constrange = DatumGetRangeType(((Const *) other)->constvalue);
} }
/* /*
* If we got a valid constant on one side of the operator, proceed to * If we got a valid constant on one side of the operator, proceed to
* estimate using statistics. Otherwise punt and return a default constant * estimate using statistics. Otherwise punt and return a default constant
* estimate. * estimate. Note that calc_rangesel need not handle
* OID_RANGE_ELEM_CONTAINED_OP.
*/ */
if (constrange) if (constrange)
selec = calc_rangesel(typcache, &vardata, constrange, operator); selec = calc_rangesel(typcache, &vardata, constrange, operator);
...@@ -270,31 +282,37 @@ calc_rangesel(TypeCacheEntry *typcache, VariableStatData *vardata, ...@@ -270,31 +282,37 @@ calc_rangesel(TypeCacheEntry *typcache, VariableStatData *vardata,
*/ */
switch (operator) switch (operator)
{ {
/* these return false if either argument is empty */
case OID_RANGE_OVERLAP_OP: case OID_RANGE_OVERLAP_OP:
case OID_RANGE_OVERLAPS_LEFT_OP: case OID_RANGE_OVERLAPS_LEFT_OP:
case OID_RANGE_OVERLAPS_RIGHT_OP: case OID_RANGE_OVERLAPS_RIGHT_OP:
case OID_RANGE_LEFT_OP: case OID_RANGE_LEFT_OP:
case OID_RANGE_RIGHT_OP: case OID_RANGE_RIGHT_OP:
/* these return false if either argument is empty */ /* nothing is less than an empty range */
case OID_RANGE_LESS_OP:
selec = 0.0; selec = 0.0;
break; break;
/* only empty ranges can be contained by an empty range */
case OID_RANGE_CONTAINED_OP: case OID_RANGE_CONTAINED_OP:
/* only empty ranges are <= an empty range */
case OID_RANGE_LESS_EQUAL_OP: case OID_RANGE_LESS_EQUAL_OP:
case OID_RANGE_GREATER_EQUAL_OP:
/*
* these return true when both args are empty, false if only
* one is empty
*/
selec = empty_frac; selec = empty_frac;
break; break;
case OID_RANGE_CONTAINS_OP:
/* everything contains an empty range */ /* everything contains an empty range */
case OID_RANGE_CONTAINS_OP:
/* everything is >= an empty range */
case OID_RANGE_GREATER_EQUAL_OP:
selec = 1.0; selec = 1.0;
break; break;
/* all non-empty ranges are > an empty range */
case OID_RANGE_GREATER_OP:
selec = 1.0 - empty_frac;
break;
/* an element cannot be empty */
case OID_RANGE_CONTAINS_ELEM_OP: case OID_RANGE_CONTAINS_ELEM_OP:
default: default:
elog(ERROR, "unexpected operator %u", operator); elog(ERROR, "unexpected operator %u", operator);
...@@ -443,13 +461,13 @@ calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata, ...@@ -443,13 +461,13 @@ calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
case OID_RANGE_GREATER_OP: case OID_RANGE_GREATER_OP:
hist_selec = hist_selec =
1 - calc_hist_selectivity_scalar(typcache, &const_lower, 1 - calc_hist_selectivity_scalar(typcache, &const_lower,
hist_lower, nhist, true); hist_lower, nhist, false);
break; break;
case OID_RANGE_GREATER_EQUAL_OP: case OID_RANGE_GREATER_EQUAL_OP:
hist_selec = hist_selec =
1 - calc_hist_selectivity_scalar(typcache, &const_lower, 1 - calc_hist_selectivity_scalar(typcache, &const_lower,
hist_lower, nhist, false); hist_lower, nhist, true);
break; break;
case OID_RANGE_LEFT_OP: case OID_RANGE_LEFT_OP:
......
...@@ -1723,10 +1723,10 @@ DESCR("less than or equal"); ...@@ -1723,10 +1723,10 @@ DESCR("less than or equal");
#define OID_RANGE_LESS_EQUAL_OP 3885 #define OID_RANGE_LESS_EQUAL_OP 3885
DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge rangesel scalargtjoinsel )); DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge rangesel scalargtjoinsel ));
DESCR("greater than or equal"); DESCR("greater than or equal");
#define OID_RANGE_GREATER_OP 3886 #define OID_RANGE_GREATER_EQUAL_OP 3886
DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt rangesel scalargtjoinsel )); DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt rangesel scalargtjoinsel ));
DESCR("greater than"); DESCR("greater than");
#define OID_RANGE_GREATER_EQUAL_OP 3887 #define OID_RANGE_GREATER_OP 3887
DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 range_overlaps rangesel areajoinsel )); DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 range_overlaps rangesel areajoinsel ));
DESCR("overlaps"); DESCR("overlaps");
#define OID_RANGE_OVERLAP_OP 3888 #define OID_RANGE_OVERLAP_OP 3888
......
...@@ -260,6 +260,11 @@ select * from numrange_test where nr = '[1.1, 2.2)'; ...@@ -260,6 +260,11 @@ select * from numrange_test where nr = '[1.1, 2.2)';
[1.1,2.2) [1.1,2.2)
(1 row) (1 row)
select * from numrange_test where nr < 'empty';
nr
----
(0 rows)
select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]'); select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
nr nr
------- -------
...@@ -287,6 +292,33 @@ select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]'); ...@@ -287,6 +292,33 @@ select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
[1.7,1.7] [1.7,1.7]
(6 rows) (6 rows)
select * from numrange_test where nr <= 'empty';
nr
-------
empty
(1 row)
select * from numrange_test where nr >= 'empty';
nr
-----------
(,)
[3,)
(,5)
[1.1,2.2)
empty
[1.7,1.7]
(6 rows)
select * from numrange_test where nr > 'empty';
nr
-----------
(,)
[3,)
(,5)
[1.1,2.2)
[1.7,1.7]
(5 rows)
select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]'); select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
nr nr
----------- -----------
......
...@@ -67,9 +67,13 @@ SELECT * FROM numrange_test WHERE 1.9 <@ nr; ...@@ -67,9 +67,13 @@ SELECT * FROM numrange_test WHERE 1.9 <@ nr;
select * from numrange_test where nr = 'empty'; select * from numrange_test where nr = 'empty';
select * from numrange_test where nr = '(1.1, 2.2)'; select * from numrange_test where nr = '(1.1, 2.2)';
select * from numrange_test where nr = '[1.1, 2.2)'; select * from numrange_test where nr = '[1.1, 2.2)';
select * from numrange_test where nr < 'empty';
select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]'); select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
select * from numrange_test where nr < numrange(0.0, 1.0,'[]'); select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]'); select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
select * from numrange_test where nr <= 'empty';
select * from numrange_test where nr >= 'empty';
select * from numrange_test where nr > 'empty';
select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]'); select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
select * from numrange_test where nr > numrange(0.0, 1.0,'[]'); select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]'); select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment