From 09183003a22752df75bd15bca270ce0e599f74dd Mon Sep 17 00:00:00 2001 From: Mark Sirek Date: Thu, 17 Aug 2023 14:39:37 -0700 Subject: [PATCH 1/3] invertedidx: derive geog/geom filters which enable inverted index scan The query normalization rules FoldEqZeroSTDistance, FoldCmpSTDistanceLeft, FoldCmpSTDistanceRight, FoldCmpSTMaxDistanceLeft and FoldCmpSTMaxDistanceRight replace binary comparison operations, =, <, >, <=, >= involving the `st_distance` or `st_maxdistance` functions with equivalent function calls which may enable inverted index scan, for example st_dwithin, and st_dfullywithin. For non-null and non-empty geographies and geometries, this rewrite is semantically equivalent, but for null or empty inputs, `st_distance` uses three-valued boolean logic and returns a null. `st_dwithin` always returns true or false, so may return a different value when the expression is negated. For example, the following test returns `true`, but should return `null`: ```sql CREATE TABLE g1 (geog GEOGRAPHY); INSERT INTO g1 VALUES (ST_GeogFromText('MULTILINESTRING EMPTY')); SELECT ST_Distance('POINT(0 0)'::GEOGRAPHY, geog) > 1 AS predicate_result FROM g1; predicate_result -------------------- t ``` The solution is to move all of the logic in the above normalization rules into `geoFilterPlanner.extractInvertedFilterConditionFromLeaf`, and use the derived predicate only for the purposes of finding inverted index scan spans. The spans will always include all rows qualified for the original predicate, but may include additional rows. The original filter is then applied to the results of the inverted index scan to filter out any results with nulls or empty geographies and geometries. Epic: none Fixes: #103616 Release note (bug fix): This patch fixes a bug in geospatial queries, where a query filter of the form `ST_Distance(geog1, geog2) > constant`, or `ST_MaxDistance(geom1, geom2) > constant`, where the operator is one of >, <, >=, <=, or a filter of the form `ST_Distance(geog1, geog2, false) = 0` may mistakenly evaluate to `true` when one or both of the inputs is null or an empty geography/geometry. More rows could be returned by the query than expected. --- .../logictest/testdata/logic_test/geospatial | 156 +++++ pkg/sql/opt/invertedidx/geo.go | 98 ++- pkg/sql/opt/norm/comp_funcs.go | 91 +-- pkg/sql/opt/norm/rules/comp.opt | 62 -- pkg/sql/opt/norm/testdata/rules/comp | 634 +++++++++++++++--- .../opt/xform/testdata/coster/spatial-filters | 28 +- 6 files changed, 825 insertions(+), 244 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index e861f27c636e..57ffa6568170 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -6021,3 +6021,159 @@ SELECT st_asmvtgeom( NULL subtest end + +subtest regression_103616 + +# Regression test for #103616 +statement ok +CREATE TABLE t103616 ( + tag string, + geog GEOGRAPHY, + geom GEOMETRY, + INVERTED INDEX geog_idx (geog), + INVERTED INDEX geom_idx (geom) +); + +statement ok +insert into t103616 values ('null', null, null); + +statement ok +insert into t103616 values ('point (0 0)', 'POINT(0 0)'::GEOGRAPHY, 'POINT(0 0)'::GEOMETRY); + +statement ok +insert into t103616 values ('point (10 10)', 'POINT(10 10)'::GEOGRAPHY, 'POINT(10 10)'::GEOMETRY); + +statement ok +insert into t103616 values ('empty 1', ST_GeogFromText('POLYGON EMPTY'), ST_GeomFromText('POLYGON EMPTY')); + +statement ok +insert into t103616 values ('empty 2', ST_GeogFromText('GEOMETRYCOLLECTION EMPTY'), ST_GeomFromText('GEOMETRYCOLLECTION EMPTY')); + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616@geog_idx +WHERE + st_distance(geog, 'POINT(0 0)'::GEOGRAPHY, false) = 0; +---- +point (0 0) + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616@geog_idx +WHERE + st_distance(geog, 'POINT(0 0)'::GEOGRAPHY, false) = 0; +---- +point (0 0) + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616@geog_idx +WHERE + NOT 1.2345678901234566e-43 < st_distance(geog, 'POINT(0 0)'::GEOGRAPHY); +---- +point (0 0) + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + NOT 1.2345678901234566e-43 > st_distance(geog, 'POINT(0 0)'::GEOGRAPHY); +---- +point (10 10) + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616@geog_idx +WHERE + 1.2345678901234566e-43 > st_distance(geog, 'POINT(0 0)'::GEOGRAPHY); +---- +point (0 0) + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + 1.2345678901234566e-43 < st_distance(geog, 'POINT(0 0)'::GEOGRAPHY); +---- +point (10 10) + +# Null and empty geog values should not appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + 0 <= st_distance(geog, 'POINT(0 0)'::GEOGRAPHY); +---- +point (0 0) +point (10 10) + +# Only null and empty geog values should appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + (st_distance(geog, 'POINT(0 0)'::GEOGRAPHY, false) = 0) IS NULL; +---- +null +empty 1 +empty 2 + +# Only null and empty geog values should appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + (0 <= st_distance(geog, 'POINT(0 0)'::GEOGRAPHY)) IS NULL; +---- +null +empty 1 +empty 2 + +# Null and empty geom values should appear in the output. +query T rowsort +SELECT tag FROM t103616@geom_idx +WHERE + NOT 1.2345678901234566e-43 < st_maxdistance(geom, 'POINT(0 0)'::GEOMETRY); +---- +point (0 0) + +# Null and empty geom values should not appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + NOT 1.2345678901234566e-43 > st_maxdistance(geom, 'POINT(0 0)'::GEOMETRY); +---- +point (10 10) + +# Null and empty geom values should not appear in the output. +query T rowsort +SELECT tag FROM t103616@geom_idx +WHERE + 1.2345678901234566e-43 > st_maxdistance(geom, 'POINT(0 0)'::GEOMETRY); +---- +point (0 0) + +# Null and empty geom values should not appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + 1.2345678901234566e-43 < st_maxdistance(geom, 'POINT(0 0)'::GEOMETRY); +---- +point (10 10) + +# Null and empty geom values should not appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + 0 <= st_maxdistance(geom, 'POINT(0 0)'::GEOMETRY); +---- +point (0 0) +point (10 10) + +# Only null and empty geog values should appear in the output. +query T rowsort +SELECT tag FROM t103616 +WHERE + (0 <= st_maxdistance(geom, 'POINT(0 0)'::GEOMETRY)) IS NULL; +---- +null +empty 1 +empty 2 + diff --git a/pkg/sql/opt/invertedidx/geo.go b/pkg/sql/opt/invertedidx/geo.go index c2bbcdec60f6..58e624a10929 100644 --- a/pkg/sql/opt/invertedidx/geo.go +++ b/pkg/sql/opt/invertedidx/geo.go @@ -334,6 +334,94 @@ type geoFilterPlanner struct { var _ invertedFilterPlanner = &geoFilterPlanner{} +// maybeDeriveUsefulInvertedFilterCondition identifies an expression of the +// form: 'st_distance(a, b, bool) = 0', 'st_distance(...) <= x' or +// 'st_maxdistance(...) <= x', and returns a function call to st_dwithin, +// st_dwithinexclusive, st_dfullywithin, st_dfullywithinexclusive or +// st_intersects which is almost equivalent to the original expression, except +// for empty or null geography/geometry inputs (it may evaluate to true in more +// cases). The derived expression may enable use of an inverted index scan. See +// the MaybeMakeSTDWithin or MakeIntersectionFunction method for the specific +// function that is used to replace expressions with different comparison +// operators (e.g. '<' vs '<='). Note that the `st_distance` or `st_maxdistance` +// may be on the left or right of the comparison operation (LT, GT, LE, GE). +func (g *geoFilterPlanner) maybeDeriveUsefulInvertedFilterCondition( + expr opt.ScalarExpr, +) (opt.ScalarExpr, bool) { + var left, right opt.ScalarExpr + var function *memo.FunctionExpr + leftIsFunction := false + rightIsFunction := false + c := g.factory.CustomFuncs() + switch t := expr.(type) { + case *memo.EqExpr: + left = t.Left + right = t.Right + function, leftIsFunction = left.(*memo.FunctionExpr) + if !leftIsFunction { + return expr, false + } + private := &function.FunctionPrivate + if private.Name != "st_distance" { + return expr, false + } + if c.STDistanceUseSpheroid(function.Args) { + return expr, false + } + constant, rightIsConstant := right.(*memo.ConstExpr) + if !rightIsConstant { + return expr, false + } + value := constant.Value + if !c.IsFloatDatum(value) { + return expr, false + } + if !c.DatumsEqual(value, tree.NewDInt(0)) { + return expr, false + } + return c.MakeIntersectionFunction(function.Args), true + case *memo.LtExpr, *memo.GtExpr, *memo.LeExpr, *memo.GeExpr: + left = t.Child(0).(opt.ScalarExpr) + right = t.Child(1).(opt.ScalarExpr) + function, leftIsFunction = left.(*memo.FunctionExpr) + if !leftIsFunction { + function, rightIsFunction = right.(*memo.FunctionExpr) + if !rightIsFunction { + return expr, false + } + } + // Combinations which result in a `NOT st_d*` function would not enable + // inverted index scan, so no need to derive filters for these cases. + if leftIsFunction && (t.Op() == opt.GtOp || t.Op() == opt.GeOp) { + return expr, false + } else if rightIsFunction && (t.Op() == opt.LtOp || t.Op() == opt.LeOp) { + return expr, false + } + // Main logic below to eliminate a code nesting level. + default: + return expr, false + } + + if function == nil { + return expr, false + } + args := function.Args + private := &function.FunctionPrivate + if private.Name != "st_distance" && private.Name != "st_maxdistance" { + return expr, false + } + var boundExpr opt.ScalarExpr + if leftIsFunction { + boundExpr = right + } else { + boundExpr = left + } + if private.Name == "st_distance" { + return c.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, false /* fullyWithin */) + } + return c.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, true /* fullyWithin */) +} + // extractInvertedFilterConditionFromLeaf is part of the invertedFilterPlanner // interface. func (g *geoFilterPlanner) extractInvertedFilterConditionFromLeaf( @@ -344,6 +432,9 @@ func (g *geoFilterPlanner) extractInvertedFilterConditionFromLeaf( _ *invertedexpr.PreFiltererStateForInvertedFilterer, ) { var args memo.ScalarListExpr + filterIsDerived := false + originalExpr := expr + expr, filterIsDerived = g.maybeDeriveUsefulInvertedFilterCondition(expr) switch t := expr.(type) { case *memo.FunctionExpr: args = t.Args @@ -382,8 +473,11 @@ func (g *geoFilterPlanner) extractInvertedFilterConditionFromLeaf( ctx, g.factory, expr, args, true /* commuteArgs */, g.tabID, g.index, g.getSpanExpr, ) } - if !invertedExpr.IsTight() { - remainingFilters = expr + // A derived filter may not be semantically equivalent to the original, so we + // need to apply the original filter in that case, the same as when the + // inverted expression is not tight. + if !invertedExpr.IsTight() || filterIsDerived { + remainingFilters = originalExpr } return invertedExpr, remainingFilters, pfState } diff --git a/pkg/sql/opt/norm/comp_funcs.go b/pkg/sql/opt/norm/comp_funcs.go index a654e61eaabe..fae909833306 100644 --- a/pkg/sql/opt/norm/comp_funcs.go +++ b/pkg/sql/opt/norm/comp_funcs.go @@ -177,61 +177,27 @@ func (c *CustomFuncs) MakeIntersectionFunction(args memo.ScalarListExpr) opt.Sca ) } -// MakeSTDWithinLeft returns an ST_DWithin function that replaces an expression -// of the following form: ST_Distance(a,b) <= x. Note that the ST_Distance -// function is on the left side of the inequality. -func (c *CustomFuncs) MakeSTDWithinLeft( - op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, -) opt.ScalarExpr { - const fnName = "st_dwithin" - const fnIsLeftArg = true - return c.makeSTDWithin(op, args, bound, fnName, fnIsLeftArg) -} - -// MakeSTDWithinRight returns an ST_DWithin function that replaces an expression -// of the following form: x <= ST_Distance(a,b). Note that the ST_Distance -// function is on the right side of the inequality. -func (c *CustomFuncs) MakeSTDWithinRight( - op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, -) opt.ScalarExpr { - const fnName = "st_dwithin" - const fnIsLeftArg = false - return c.makeSTDWithin(op, args, bound, fnName, fnIsLeftArg) -} - -// MakeSTDFullyWithinLeft returns an ST_DFullyWithin function that replaces an -// expression of the following form: ST_MaxDistance(a,b) <= x. Note that the -// ST_MaxDistance function is on the left side of the inequality. -func (c *CustomFuncs) MakeSTDFullyWithinLeft( - op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, -) opt.ScalarExpr { - const fnName = "st_dfullywithin" - const fnIsLeftArg = true - return c.makeSTDWithin(op, args, bound, fnName, fnIsLeftArg) -} - -// MakeSTDFullyWithinRight returns an ST_DFullyWithin function that replaces an -// expression of the following form: x <= ST_MaxDistance(a,b). Note that the -// ST_MaxDistance function is on the right side of the inequality. -func (c *CustomFuncs) MakeSTDFullyWithinRight( - op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, -) opt.ScalarExpr { - const fnName = "st_dfullywithin" - const fnIsLeftArg = false - return c.makeSTDWithin(op, args, bound, fnName, fnIsLeftArg) -} - -// makeSTDWithin returns an ST_DWithin (or ST_DFullyWithin) function that -// replaces an expression of the following form: ST_Distance(a,b) <= x. The -// ST_Distance function can be on either side of the inequality, and the -// inequality can be one of the following: '<', '<=', '>', '>='. This -// replacement allows early-exit behavior, and may enable use of an inverted -// index scan. -func (c *CustomFuncs) makeSTDWithin( - op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, fnName string, fnIsLeftArg bool, -) opt.ScalarExpr { +// MaybeMakeSTDWithin attempts to derive an ST_DWithin (or ST_DFullyWithin) +// function that is similar to an expression of the following form: +// ST_Distance(a,b) <= x. The ST_Distance function can be on either side of the +// inequality, and the inequality can be one of the following: '<', '<=', '>', +// '>='. This replacement allows early-exit behavior, and may enable use of an +// inverted index scan. If the derived expression would need to be negated with +// a NotExpr, so the derivation fails, and expr, ok=false is returned. +func (c *CustomFuncs) MaybeMakeSTDWithin( + expr opt.ScalarExpr, + args memo.ScalarListExpr, + bound opt.ScalarExpr, + fnIsLeftArg bool, + fullyWithin bool, +) (derivedExpr opt.ScalarExpr, ok bool) { + op := expr.Op() var not bool var name string + fnName := "st_dwithin" + if fullyWithin { + fnName = "st_dfullywithin" + } incName := fnName exName := fnName + "exclusive" switch op { @@ -279,7 +245,15 @@ func (c *CustomFuncs) makeSTDWithin( name = incName } } - + if not { + // ST_DWithin and ST_DWithinExclusive are equivalent to ST_Distance <= x and + // ST_Distance < x respectively. The comparison operator in the matched + // expression (if ST_Distance is normalized to be on the left) is either '>' + // or '>='. Therefore, we would have to take the opposite of within. This + // would not result in a useful expression for inverted index scan, so return + // ok=false. + return expr, false + } newArgs := make(memo.ScalarListExpr, len(args)+1) const distanceIdx, useSpheroidIdx = 2, 3 copy(newArgs, args[:distanceIdx]) @@ -302,12 +276,5 @@ func (c *CustomFuncs) makeSTDWithin( Properties: props, Overload: overload, }) - if not { - // ST_DWithin and ST_DWithinExclusive are equivalent to ST_Distance <= x and - // ST_Distance < x respectively. The comparison operator in the matched - // expression (if ST_Distance is normalized to be on the left) is either '>' - // or '>='. Therefore, we have to take the opposite of within. - within = c.f.ConstructNot(within) - } - return within + return within, true } diff --git a/pkg/sql/opt/norm/rules/comp.opt b/pkg/sql/opt/norm/rules/comp.opt index d123f9379003..257f2eb6b0a8 100644 --- a/pkg/sql/opt/norm/rules/comp.opt +++ b/pkg/sql/opt/norm/rules/comp.opt @@ -258,68 +258,6 @@ (MakeTimeZoneFunction (FirstScalarListExpr $args) $right) ) -# FoldEqZeroSTDistance matches an expression of the form: 'ST_Distance(a,b) = 0' -# and replaces it with 'ST_Intersects(a,b)'. This replacement allows for -# early-exit behavior, and may allow an inverted index scan to be generated. -[FoldEqZeroSTDistance, Normalize] -(Eq - (Function $args:* $private:(FunctionPrivate "st_distance")) & - ^(STDistanceUseSpheroid $args) - $right:(Const - $value:* & (IsFloatDatum $value) & (DatumsEqual $value 0) - ) -) -=> -(MakeIntersectionFunction $args) - -# FoldCmpSTDistanceLeft replaces an expression of the form: -# 'ST_Distance(...) <= x' with a call to ST_DWithin or ST_DWithinExclusive. This -# replacement allows early-exit behavior, and may enable use of an inverted -# index scan. See the MakeSTDWithin method for the specific variation on -# ST_DWithin that is used to replace expressions with different comparison -# operators (e.g. '<' vs '<='). -[FoldCmpSTDistanceLeft, Normalize] -(Ge | Gt | Le | Lt - (Function $args:* $private:(FunctionPrivate "st_distance")) - $right:* -) -=> -(MakeSTDWithinLeft (OpName) $args $right) - -# FoldCmpSTDistanceRight mirrors FoldCmpSTDistanceLeft. -[FoldCmpSTDistanceRight, Normalize] -(Ge | Gt | Le | Lt - $left:* - (Function $args:* $private:(FunctionPrivate "st_distance")) -) -=> -(MakeSTDWithinRight (OpName) $args $left) - -# FoldCmpSTMaxDistanceLeft is a variant of FoldCmpSTDistanceLeft that matches -# ST_MaxDistance instead of ST_Distance. -[FoldCmpSTMaxDistanceLeft, Normalize] -(Ge | Gt | Le | Lt - (Function - $args:* - $private:(FunctionPrivate "st_maxdistance") - ) - $right:* -) -=> -(MakeSTDFullyWithinLeft (OpName) $args $right) - -# FoldCmpSTMaxDistanceRight mirrors FoldCmpSTMaxDistanceLeft. -[FoldCmpSTMaxDistanceRight, Normalize] -(Ge | Gt | Le | Lt - $left:* - (Function - $args:* - $private:(FunctionPrivate "st_maxdistance") - ) -) -=> -(MakeSTDFullyWithinRight (OpName) $args $left) - # FoldEqTrue replaces x = True with x. [FoldEqTrue, Normalize] (Eq $left:* (True)) diff --git a/pkg/sql/opt/norm/testdata/rules/comp b/pkg/sql/opt/norm/testdata/rules/comp index c78fc082dd1f..bf1145d2ec4a 100644 --- a/pkg/sql/opt/norm/testdata/rules/comp +++ b/pkg/sql/opt/norm/testdata/rules/comp @@ -6,7 +6,9 @@ exec-ddl CREATE TABLE geom_geog ( geom GEOMETRY, geog GEOGRAPHY, - val FLOAT + val FLOAT, + INVERTED INDEX(geom), + INVERTED INDEX(geog) ) ---- @@ -883,36 +885,70 @@ project └── projections └── ts:1 <= '2020-06-01 13:35:55' [as="?column?":6, outer=(1)] +# Tests for FoldEqZeroSTDistance, FoldCmpSTDistanceLeft, FoldCmpSTDistanceRight, +# FoldCmpSTMaxDistanceLeft and FoldCmpSTMaxDistanceRight will be moved to an +# xform rules test in a later commit, but are kept in this commit for now to +# show the diff in filters. A derived predicate is only built for inverted +# index scan processing, so GenerateInvertedIndexScans is the targeted rule. + # -------------------------------------------------- # FoldEqZeroSTDistance # -------------------------------------------------- # Geometry case. -norm expect=FoldEqZeroSTDistance +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 0 ---- select - ├── columns: geom:1!null geog:2 val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_intersects('010100000000000000000000000000000000000000', geom:1) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(7) └── filters - └── st_intersects(geom:1, '010100000000000000000000000000000000000000') [outer=(1), immutable, constraints=(/1: (/NULL - ])] + └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 0.0 [outer=(1), immutable] # Geography case with use_spheroid=false. -norm expect=FoldEqZeroSTDistance +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', false) = 0 ---- select - ├── columns: geom:1 geog:2!null val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_intersects('0101000020E610000000000000000000000000000000000000', geog:2) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) └── filters - └── st_intersects(geog:2, '0101000020E610000000000000000000000000000000000000') [outer=(2), immutable, constraints=(/2: (/NULL - ])] + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) = 0.0 [outer=(2), immutable] # No-op case because the constant is nonzero. -norm expect-not=FoldEqZeroSTDistance +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 1 ---- select @@ -924,7 +960,7 @@ select └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 1.0 [outer=(1), immutable] # No-op case because use_spheroid=true implicitly. -norm expect-not=FoldEqZeroSTDistance +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)') = 0 ---- select @@ -936,7 +972,7 @@ select └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') = 0.0 [outer=(2), immutable] # No-op case because use_spheroid=true. -norm expect-not=FoldEqZeroSTDistance +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', true) = 0 ---- select @@ -952,31 +988,69 @@ select # -------------------------------------------------- # Geometry case with '<=' operator. -norm expect=FoldCmpSTDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') <= 5 ---- select - ├── columns: geom:1!null geog:2 val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dwithin('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) └── filters - └── st_dwithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] + └── st_distance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] # Geometry case with '<' operator. -norm expect=FoldCmpSTDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance('point(0.0 0.0)', geom) < 5 ---- select - ├── columns: geom:1!null geog:2 val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dwithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) └── filters - └── st_dwithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] + └── st_distance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] # Geometry case with '>=' operator. -norm expect=FoldCmpSTDistanceLeft +# Inverted index scan not expected because derived expression is +# NOT st_dwithinexclusive. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') >= 5 ---- select @@ -985,10 +1059,11 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dwithinexclusive(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable] + └── st_distance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] # Geometry case with '>' operator. -norm expect=FoldCmpSTDistanceLeft +# Inverted index scan not expected because derived expression is NOT st_dwithin. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') > 5 ---- select @@ -997,169 +1072,511 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dwithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable] + └── st_distance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] # Geography case with '<=' operator. -norm expect=FoldCmpSTDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') <= 5 ---- select - ├── columns: geom:1 geog:2!null val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) └── filters - └── st_dwithin(geog:2, '0101000020E610000000000000000000000000000000000000', 5.0) [outer=(2), immutable, constraints=(/2: (/NULL - ])] + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') <= 5.0 [outer=(2), immutable] # Geography case with '<' operator. -norm expect=FoldCmpSTDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5 ---- select - ├── columns: geom:1 geog:2!null val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) └── filters - └── st_dwithinexclusive(geog:2, '0101000020E610000000000000000000000000000000000000', 5.0) [outer=(2), immutable, constraints=(/2: (/NULL - ])] + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') < 5.0 [outer=(2), immutable] # Regression test for #54326. Ensure the distance param is cast to a float. -norm expect=FoldCmpSTDistanceLeft format=(show-scalars,show-types) +opt expect=GenerateInvertedIndexScans format=(show-scalars,show-types) SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5::int ---- select - ├── columns: geom:1(geometry) geog:2(geography!null) val:3(float) + ├── columns: geom:1(geometry) geog:2(geography) val:3(float) ├── immutable - ├── scan geom_geog - │ └── columns: geom:1(geometry) geog:2(geography) val:3(float) + ├── index-join geom_geog + │ ├── columns: geom:1(geometry) geog:2(geography) val:3(float) + │ └── inverted-filter + │ ├── columns: rowid:4(int!null) + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── function: st_dwithin [type=bool] + │ │ ├── const: '0101000020E610000000000000000000000000000000000000' [type=geography] + │ │ ├── variable: geog:2 [type=geography] + │ │ └── const: 5.0 [type=float] + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4(int!null) geog_inverted_key:8(encodedkey!null) + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) └── filters - └── function: st_dwithinexclusive [type=bool, outer=(2), immutable, constraints=(/2: (/NULL - ])] - ├── variable: geog:2 [type=geography] - ├── const: '0101000020E610000000000000000000000000000000000000' [type=geography] - └── const: 5.0 [type=float] - -norm expect=FoldCmpSTDistanceLeft format=(show-scalars,show-types) + └── lt [type=bool, outer=(2), immutable] + ├── function: st_distance [type=float] + │ ├── variable: geog:2 [type=geography] + │ └── const: '0101000020E610000000000000000000000000000000000000' [type=geography] + └── const: 5 [type=int] + +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans format=(show-scalars,show-types) SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < val::int ---- select - ├── columns: geom:1(geometry) geog:2(geography!null) val:3(float) + ├── columns: geom:1(geometry) geog:2(geography) val:3(float) ├── immutable ├── scan geom_geog │ └── columns: geom:1(geometry) geog:2(geography) val:3(float) └── filters - └── function: st_dwithinexclusive [type=bool, outer=(2,3), immutable, constraints=(/2: (/NULL - ])] - ├── variable: geog:2 [type=geography] - ├── const: '0101000020E610000000000000000000000000000000000000' [type=geography] - └── cast: FLOAT8 [type=float] - └── cast: INT8 [type=int] - └── variable: val:3 [type=float] + └── lt [type=bool, outer=(2,3), immutable] + ├── function: st_distance [type=float] + │ ├── variable: geog:2 [type=geography] + │ └── const: '0101000020E610000000000000000000000000000000000000' [type=geography] + └── cast: INT8 [type=int] + └── variable: val:3 [type=float] # Regression test for #55675. Handle use_spheroid param. -norm expect=FoldCmpSTDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)', true) <= 5 ---- select - ├── columns: geom:1 geog:2!null val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0, true) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) └── filters - └── st_dwithin(geog:2, '0101000020E610000000000000000000000000000000000000', 5.0, true) [outer=(2), immutable, constraints=(/2: (/NULL - ])] + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', true) <= 5.0 [outer=(2), immutable] # -------------------------------------------------- # FoldCmpSTDistanceRight # -------------------------------------------------- # Case with '<=' operator. -norm expect=FoldCmpSTDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val <= st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dwithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] + └── val:3 <= st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Case with '<' operator. -norm expect=FoldCmpSTDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val < st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dwithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] + └── val:3 < st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Case with '>=' operator. -norm expect=FoldCmpSTDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val >= st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1!null geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_dwithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] + └── val:3 >= st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Case with '>' operator. -norm expect=FoldCmpSTDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val > st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1!null geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_dwithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] + └── val:3 > st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Regression test for #55675. Handle use_spheroid param. -norm expect=FoldCmpSTDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val > st_distance(geog, 'point(0.0 0.0)', false) ---- select - ├── columns: geom:1 geog:2!null val:3!null + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_dwithinexclusive(geog:2, '0101000020E610000000000000000000000000000000000000', val:3, false) [outer=(2,3), immutable, constraints=(/2: (/NULL - ]; /3: (/NULL - ])] + └── val:3 > st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) [outer=(2,3), immutable, constraints=(/3: (/NULL - ])] # -------------------------------------------------- # FoldCmpSTMaxDistanceLeft # -------------------------------------------------- # Case with '<=' operator. -norm expect=FoldCmpSTMaxDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') <= 5 ---- select - ├── columns: geom:1!null geog:2 val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dfullywithin('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) └── filters - └── st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] + └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] # Case with '<' operator. -norm expect=FoldCmpSTMaxDistanceLeft +opt expect=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_maxdistance('point(0.0 0.0)', geom) < 5 ---- select - ├── columns: geom:1!null geog:2 val:3 + ├── columns: geom:1 geog:2 val:3 ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dfullywithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) └── filters - └── st_dfullywithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] + └── st_maxdistance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] # Case with '>=' operator. -norm expect=FoldCmpSTMaxDistanceLeft +# Inverted index scan not expected because derived expression is +# NOT st_dwithinexclusive. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') >= 5 ---- select @@ -1168,10 +1585,11 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dfullywithinexclusive(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable] + └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] # Case with '>' operator. -norm expect=FoldCmpSTMaxDistanceLeft +# Inverted index scan not expected because derived expression is NOT st_within. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') > 5 ---- select @@ -1180,59 +1598,67 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable] + └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] # -------------------------------------------------- # FoldCmpSTMaxDistanceRight # -------------------------------------------------- # Case with '<=' operator. -norm expect=FoldCmpSTMaxDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val <= st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dfullywithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] + └── val:3 <= st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Case with '<' operator. -norm expect=FoldCmpSTMaxDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val < st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── NOT st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] + └── val:3 < st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Case with '>=' operator. -norm expect=FoldCmpSTMaxDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val >= st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1!null geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] + └── val:3 >= st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # Case with '>' operator. -norm expect=FoldCmpSTMaxDistanceRight +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans SELECT * FROM geom_geog WHERE val > st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1!null geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3!null ├── immutable ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_dfullywithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] + └── val:3 > st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] # -------------------------------------------------- # FoldEqTrue + FoldEqFalse @@ -1300,11 +1726,11 @@ opt expect=FoldEqTrue SELECT count(*) FROM geom_geog WHERE (geom && st_geomfromewkt('SRID=4326;POLYGON((0 0,0 100,100 100,100 0,0 0))'))=TRUE; ---- scalar-group-by - ├── columns: count:8!null + ├── columns: count:10!null ├── cardinality: [1 - 1] ├── immutable ├── key: () - ├── fd: ()-->(8) + ├── fd: ()-->(10) ├── select │ ├── columns: geom:1!null │ ├── immutable @@ -1331,7 +1757,7 @@ scalar-group-by │ └── filters │ └── geom:1 && '0103000020E610000001000000050000000000000000000000000000000000000000000000000000000000000000005940000000000000594000000000000059400000000000005940000000000000000000000000000000000000000000000000' [outer=(1), immutable, constraints=(/1: (/NULL - ])] └── aggregations - └── count-rows [as=count_rows:8] + └── count-rows [as=count_rows:10] # -------------------------------------------------- # FoldNeTrue + FoldNeFalse diff --git a/pkg/sql/opt/xform/testdata/coster/spatial-filters b/pkg/sql/opt/xform/testdata/coster/spatial-filters index b845c8e919e9..32aeb5c72f31 100644 --- a/pkg/sql/opt/xform/testdata/coster/spatial-filters +++ b/pkg/sql/opt/xform/testdata/coster/spatial-filters @@ -132,24 +132,24 @@ WHERE st_distance(geog, st_makepoint(1.0, 1.0)::geography) < 200; project ├── columns: id:1!null ├── immutable - ├── stats: [rows=55000] - ├── cost: 535578.67 + ├── stats: [rows=166666.7] + ├── cost: 1036695.34 ├── key: (1) └── select - ├── columns: id:1!null geog:4!null + ├── columns: id:1!null geog:4 ├── immutable - ├── stats: [rows=55000, distinct(4)=50000, null(4)=0] - ├── cost: 535028.65 + ├── stats: [rows=166666.7] + ├── cost: 1035028.65 ├── key: (1) ├── fd: (1)-->(4) ├── scan g │ ├── columns: id:1!null geog:4 - │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0, distinct(4)=50000, null(4)=5000] + │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0] │ ├── cost: 530028.62 │ ├── key: (1) │ └── fd: (1)-->(4) └── filters - └── st_dwithinexclusive(geog:4, '0101000020E6100000000000000000F03F000000000000F03F', 200.0) [outer=(4), immutable, constraints=(/4: (/NULL - ])] + └── st_distance(geog:4, '0101000020E6100000000000000000F03F000000000000F03F') < 200.0 [outer=(4), immutable] opt SELECT id FROM g @@ -158,24 +158,24 @@ WHERE st_distance(geog, st_makepoint(a, 1.0)::geography) < 200; project ├── columns: id:1!null ├── immutable - ├── stats: [rows=55000] - ├── cost: 1040578.77 + ├── stats: [rows=166666.7] + ├── cost: 1541695.44 ├── key: (1) └── select - ├── columns: id:1!null a:2 geog:4!null + ├── columns: id:1!null a:2 geog:4 ├── immutable - ├── stats: [rows=55000, distinct(4)=50000, null(4)=0] - ├── cost: 1040028.75 + ├── stats: [rows=166666.7] + ├── cost: 1540028.75 ├── key: (1) ├── fd: (1)-->(2,4) ├── scan g │ ├── columns: id:1!null a:2 geog:4 - │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0, distinct(4)=50000, null(4)=5000] + │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0] │ ├── cost: 535028.72 │ ├── key: (1) │ └── fd: (1)-->(2,4) └── filters - └── st_dwithinexclusive(geog:4, st_makepoint(a:2, 1.0)::GEOGRAPHY, 200.0) [outer=(2,4), immutable, constraints=(/4: (/NULL - ])] + └── st_distance(geog:4, st_makepoint(a:2, 1.0)::GEOGRAPHY) < 200.0 [outer=(2,4), immutable] opt SELECT id FROM g From 3e84b5aeea687a144695ea76a9961b5eb2fe12ba Mon Sep 17 00:00:00 2001 From: Mark Sirek Date: Thu, 17 Aug 2023 15:37:11 -0700 Subject: [PATCH 2/3] xform: move st_distance rules tests from norm to xform This commit moves the tests for the old normalization rules FoldEqZeroSTDistance, FoldCmpSTDistanceLeft, FoldCmpSTDistanceRight, FoldCmpSTMaxDistanceLeft and FoldCmpSTMaxDistanceRight from norm into xform. The tests now check whether the derived filter triggers the GenerateInvertedIndexScans rule. Epic: none Informs: #103616 Release note: None --- pkg/sql/opt/norm/testdata/rules/comp | 785 +----------------------- pkg/sql/opt/xform/testdata/rules/select | 777 +++++++++++++++++++++++ 2 files changed, 781 insertions(+), 781 deletions(-) diff --git a/pkg/sql/opt/norm/testdata/rules/comp b/pkg/sql/opt/norm/testdata/rules/comp index bf1145d2ec4a..e203d140db1d 100644 --- a/pkg/sql/opt/norm/testdata/rules/comp +++ b/pkg/sql/opt/norm/testdata/rules/comp @@ -6,9 +6,7 @@ exec-ddl CREATE TABLE geom_geog ( geom GEOMETRY, geog GEOGRAPHY, - val FLOAT, - INVERTED INDEX(geom), - INVERTED INDEX(geog) + val FLOAT ) ---- @@ -885,781 +883,6 @@ project └── projections └── ts:1 <= '2020-06-01 13:35:55' [as="?column?":6, outer=(1)] -# Tests for FoldEqZeroSTDistance, FoldCmpSTDistanceLeft, FoldCmpSTDistanceRight, -# FoldCmpSTMaxDistanceLeft and FoldCmpSTMaxDistanceRight will be moved to an -# xform rules test in a later commit, but are kept in this commit for now to -# show the diff in filters. A derived predicate is only built for inverted -# index scan processing, so GenerateInvertedIndexScans is the targeted rule. - -# -------------------------------------------------- -# FoldEqZeroSTDistance -# -------------------------------------------------- - -# Geometry case. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 0 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /7 - │ │ ├── tight: false, unique: false - │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ ├── pre-filterer expression - │ │ └── st_intersects('010100000000000000000000000000000000000000', geom:1) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geom_idx - │ ├── columns: rowid:4!null geom_inverted_key:7!null - │ ├── inverted constraint: /7/4 - │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ ├── key: (4) - │ └── fd: (4)-->(7) - └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 0.0 [outer=(1), immutable] - -# Geography case with use_spheroid=false. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', false) = 0 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /8 - │ │ ├── tight: false, unique: false - │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ ├── pre-filterer expression - │ │ └── st_intersects('0101000020E610000000000000000000000000000000000000', geog:2) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geog_idx - │ ├── columns: rowid:4!null geog_inverted_key:8!null - │ ├── inverted constraint: /8/4 - │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ ├── key: (4) - │ └── fd: (4)-->(8) - └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) = 0.0 [outer=(2), immutable] - -# No-op case because the constant is nonzero. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 1 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 1.0 [outer=(1), immutable] - -# No-op case because use_spheroid=true implicitly. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)') = 0 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') = 0.0 [outer=(2), immutable] - -# No-op case because use_spheroid=true. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', true) = 0 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', true) = 0.0 [outer=(2), immutable] - -# -------------------------------------------------- -# FoldCmpSTDistanceLeft -# -------------------------------------------------- - -# Geometry case with '<=' operator. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') <= 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /7 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── pre-filterer expression - │ │ └── st_dwithin('010100000000000000000000000000000000000000', geom:1, 5.0) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geom_idx - │ ├── columns: rowid:4!null geom_inverted_key:7!null - │ ├── inverted constraint: /7/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── key: (4) - │ └── fd: (4)-->(7) - └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] - -# Geometry case with '<' operator. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance('point(0.0 0.0)', geom) < 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /7 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── pre-filterer expression - │ │ └── st_dwithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geom_idx - │ ├── columns: rowid:4!null geom_inverted_key:7!null - │ ├── inverted constraint: /7/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── key: (4) - │ └── fd: (4)-->(7) - └── filters - └── st_distance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] - -# Geometry case with '>=' operator. -# Inverted index scan not expected because derived expression is -# NOT st_dwithinexclusive. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') >= 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] - -# Geometry case with '>' operator. -# Inverted index scan not expected because derived expression is NOT st_dwithin. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') > 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] - -# Geography case with '<=' operator. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') <= 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /8 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── pre-filterer expression - │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geog_idx - │ ├── columns: rowid:4!null geog_inverted_key:8!null - │ ├── inverted constraint: /8/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── key: (4) - │ └── fd: (4)-->(8) - └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') <= 5.0 [outer=(2), immutable] - -# Geography case with '<' operator. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /8 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── pre-filterer expression - │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geog_idx - │ ├── columns: rowid:4!null geog_inverted_key:8!null - │ ├── inverted constraint: /8/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── key: (4) - │ └── fd: (4)-->(8) - └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') < 5.0 [outer=(2), immutable] - -# Regression test for #54326. Ensure the distance param is cast to a float. -opt expect=GenerateInvertedIndexScans format=(show-scalars,show-types) -SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5::int ----- -select - ├── columns: geom:1(geometry) geog:2(geography) val:3(float) - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1(geometry) geog:2(geography) val:3(float) - │ └── inverted-filter - │ ├── columns: rowid:4(int!null) - │ ├── inverted expression: /8 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── pre-filterer expression - │ │ └── function: st_dwithin [type=bool] - │ │ ├── const: '0101000020E610000000000000000000000000000000000000' [type=geography] - │ │ ├── variable: geog:2 [type=geography] - │ │ └── const: 5.0 [type=float] - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geog_idx - │ ├── columns: rowid:4(int!null) geog_inverted_key:8(encodedkey!null) - │ ├── inverted constraint: /8/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── key: (4) - │ └── fd: (4)-->(8) - └── filters - └── lt [type=bool, outer=(2), immutable] - ├── function: st_distance [type=float] - │ ├── variable: geog:2 [type=geography] - │ └── const: '0101000020E610000000000000000000000000000000000000' [type=geography] - └── const: 5 [type=int] - -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans format=(show-scalars,show-types) -SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < val::int ----- -select - ├── columns: geom:1(geometry) geog:2(geography) val:3(float) - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1(geometry) geog:2(geography) val:3(float) - └── filters - └── lt [type=bool, outer=(2,3), immutable] - ├── function: st_distance [type=float] - │ ├── variable: geog:2 [type=geography] - │ └── const: '0101000020E610000000000000000000000000000000000000' [type=geography] - └── cast: INT8 [type=int] - └── variable: val:3 [type=float] - -# Regression test for #55675. Handle use_spheroid param. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)', true) <= 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /8 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── pre-filterer expression - │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0, true) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geog_idx - │ ├── columns: rowid:4!null geog_inverted_key:8!null - │ ├── inverted constraint: /8/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") - │ ├── key: (4) - │ └── fd: (4)-->(8) - └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', true) <= 5.0 [outer=(2), immutable] - -# -------------------------------------------------- -# FoldCmpSTDistanceRight -# -------------------------------------------------- - -# Case with '<=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val <= st_distance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 <= st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Case with '<' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val < st_distance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 < st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Case with '>=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val >= st_distance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 >= st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Case with '>' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val > st_distance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 > st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Regression test for #55675. Handle use_spheroid param. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val > st_distance(geog, 'point(0.0 0.0)', false) ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 > st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) [outer=(2,3), immutable, constraints=(/3: (/NULL - ])] - -# -------------------------------------------------- -# FoldCmpSTMaxDistanceLeft -# -------------------------------------------------- - -# Case with '<=' operator. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') <= 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /7 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── pre-filterer expression - │ │ └── st_dfullywithin('010100000000000000000000000000000000000000', geom:1, 5.0) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geom_idx - │ ├── columns: rowid:4!null geom_inverted_key:7!null - │ ├── inverted constraint: /7/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── key: (4) - │ └── fd: (4)-->(7) - └── filters - └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] - -# Case with '<' operator. -opt expect=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_maxdistance('point(0.0 0.0)', geom) < 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── index-join geom_geog - │ ├── columns: geom:1 geog:2 val:3 - │ └── inverted-filter - │ ├── columns: rowid:4!null - │ ├── inverted expression: /7 - │ │ ├── tight: false, unique: false - │ │ └── union spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── pre-filterer expression - │ │ └── st_dfullywithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) - │ ├── key: (4) - │ └── scan geom_geog@geom_geog_geom_idx - │ ├── columns: rowid:4!null geom_inverted_key:7!null - │ ├── inverted constraint: /7/4 - │ │ └── spans - │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] - │ ├── key: (4) - │ └── fd: (4)-->(7) - └── filters - └── st_maxdistance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] - -# Case with '>=' operator. -# Inverted index scan not expected because derived expression is -# NOT st_dwithinexclusive. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') >= 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] - -# Case with '>' operator. -# Inverted index scan not expected because derived expression is NOT st_within. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') > 5 ----- -select - ├── columns: geom:1 geog:2 val:3 - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] - -# -------------------------------------------------- -# FoldCmpSTMaxDistanceRight -# -------------------------------------------------- - -# Case with '<=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val <= st_maxdistance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 <= st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Case with '<' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val < st_maxdistance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 < st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Case with '>=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val >= st_maxdistance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 >= st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - -# Case with '>' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans -SELECT * FROM geom_geog WHERE val > st_maxdistance(geom, 'point(0.0 0.0)') ----- -select - ├── columns: geom:1 geog:2 val:3!null - ├── immutable - ├── scan geom_geog - │ └── columns: geom:1 geog:2 val:3 - └── filters - └── val:3 > st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] - # -------------------------------------------------- # FoldEqTrue + FoldEqFalse # -------------------------------------------------- @@ -1726,11 +949,11 @@ opt expect=FoldEqTrue SELECT count(*) FROM geom_geog WHERE (geom && st_geomfromewkt('SRID=4326;POLYGON((0 0,0 100,100 100,100 0,0 0))'))=TRUE; ---- scalar-group-by - ├── columns: count:10!null + ├── columns: count:8!null ├── cardinality: [1 - 1] ├── immutable ├── key: () - ├── fd: ()-->(10) + ├── fd: ()-->(8) ├── select │ ├── columns: geom:1!null │ ├── immutable @@ -1757,7 +980,7 @@ scalar-group-by │ └── filters │ └── geom:1 && '0103000020E610000001000000050000000000000000000000000000000000000000000000000000000000000000005940000000000000594000000000000059400000000000005940000000000000000000000000000000000000000000000000' [outer=(1), immutable, constraints=(/1: (/NULL - ])] └── aggregations - └── count-rows [as=count_rows:10] + └── count-rows [as=count_rows:8] # -------------------------------------------------- # FoldNeTrue + FoldNeFalse diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index f040e9e09ddc..0d5aaf128d3e 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -7421,6 +7421,783 @@ index-join t63180 ├── key: (2) └── fd: (2)-->(5) +exec-ddl +CREATE TABLE geom_geog ( + geom GEOMETRY, + geog GEOGRAPHY, + val FLOAT, + INVERTED INDEX(geom), + INVERTED INDEX(geog) +) +---- + +# Tests for pre-existing normalization rules FoldEqZeroSTDistance, +# FoldCmpSTDistanceLeft, FoldCmpSTDistanceRight, FoldCmpSTMaxDistanceLeft and +# FoldCmpSTMaxDistanceRight have been moved here, and converted into tests +# detecting whether GenerateInvertedIndexScans was fired due to derivation +# of a new filter condition, used only internally when determining the +# inverted index scan spans to use. + + +# Tests related to old normalization rule FoldEqZeroSTDistance + +# Geometry case. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 0 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_intersects('010100000000000000000000000000000000000000', geom:1) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(7) + └── filters + └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 0.0 [outer=(1), immutable] + +# Geography case with use_spheroid=false. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', false) = 0 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_intersects('0101000020E610000000000000000000000000000000000000', geog:2) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) + └── filters + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) = 0.0 [outer=(2), immutable] + +# No-op case because the constant is nonzero. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 1 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 1.0 [outer=(1), immutable] + +# No-op case because use_spheroid=true implicitly. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)') = 0 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') = 0.0 [outer=(2), immutable] + +# No-op case because use_spheroid=true. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', true) = 0 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', true) = 0.0 [outer=(2), immutable] + +# Tests related to old normalization rule FoldCmpSTDistanceLeft + +# Geometry case with '<=' operator. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') <= 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dwithin('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) + └── filters + └── st_distance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] + +# Geometry case with '<' operator. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance('point(0.0 0.0)', geom) < 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dwithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) + └── filters + └── st_distance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] + +# Geometry case with '>=' operator. +# Inverted index scan not expected because derived expression is +# NOT st_dwithinexclusive. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') >= 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_distance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] + +# Geometry case with '>' operator. +# Inverted index scan not expected because derived expression is NOT st_dwithin. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') > 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_distance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] + +# Geography case with '<=' operator. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') <= 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) + └── filters + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') <= 5.0 [outer=(2), immutable] + +# Geography case with '<' operator. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) + └── filters + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') < 5.0 [outer=(2), immutable] + +# Regression test for #54326. Ensure the distance param is cast to a float. +opt expect=GenerateInvertedIndexScans format=(show-scalars,show-types) +SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5::int +---- +select + ├── columns: geom:1(geometry) geog:2(geography) val:3(float) + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1(geometry) geog:2(geography) val:3(float) + │ └── inverted-filter + │ ├── columns: rowid:4(int!null) + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── function: st_dwithin [type=bool] + │ │ ├── const: '0101000020E610000000000000000000000000000000000000' [type=geography] + │ │ ├── variable: geog:2 [type=geography] + │ │ └── const: 5.0 [type=float] + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4(int!null) geog_inverted_key:8(encodedkey!null) + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) + └── filters + └── lt [type=bool, outer=(2), immutable] + ├── function: st_distance [type=float] + │ ├── variable: geog:2 [type=geography] + │ └── const: '0101000020E610000000000000000000000000000000000000' [type=geography] + └── const: 5 [type=int] + +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans format=(show-scalars,show-types) +SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < val::int +---- +select + ├── columns: geom:1(geometry) geog:2(geography) val:3(float) + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1(geometry) geog:2(geography) val:3(float) + └── filters + └── lt [type=bool, outer=(2,3), immutable] + ├── function: st_distance [type=float] + │ ├── variable: geog:2 [type=geography] + │ └── const: '0101000020E610000000000000000000000000000000000000' [type=geography] + └── cast: INT8 [type=int] + └── variable: val:3 [type=float] + +# Regression test for #55675. Handle use_spheroid param. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)', true) <= 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /8 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── pre-filterer expression + │ │ └── st_dwithin('0101000020E610000000000000000000000000000000000000', geog:2, 5.0, true) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geog_idx + │ ├── columns: rowid:4!null geog_inverted_key:8!null + │ ├── inverted constraint: /8/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd \x00\x00\x00\x00\x00\x00\x01", "B\xfd\"\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\"\x00\x00\x00\x00\x00\x00\x01", "B\xfd$\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd$\x00\x00\x00\x00\x00\x00\x00", "B\xfd$\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd0\x00\x00\x00\x00\x00\x00\x00", "B\xfd0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x00", "B\xfd<\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd<\x00\x00\x00\x00\x00\x00\x01", "B\xfd>\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd>\x00\x00\x00\x00\x00\x00\x01", "B\xfd@\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd@\x00\x00\x00\x00\x00\x00\x01", "B\xfdB\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdD\x00\x00\x00\x00\x00\x00\x00", "B\xfdD\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdF\x00\x00\x00\x00\x00\x00\x01", "B\xfdH\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdH\x00\x00\x00\x00\x00\x00\x01", "B\xfdJ\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdJ\x00\x00\x00\x00\x00\x00\x01", "B\xfdL\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdL\x00\x00\x00\x00\x00\x00\x00", "B\xfdL\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdP\x00\x00\x00\x00\x00\x00\x00", "B\xfdP\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8a\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x8c\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x8c\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x8e\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x92\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x94\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x94\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x96\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb0\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xb4\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\xb4\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb6\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb6\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xb8\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xb8\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xba\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00", "B\xfd\xbc\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\xbe\x00\x00\x00\x00\x00\x00\x01", "B\xfd\xc0\x00\x00\x00\x00\x00\x00\x00") + │ ├── key: (4) + │ └── fd: (4)-->(8) + └── filters + └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', true) <= 5.0 [outer=(2), immutable] + +# Tests related to old normalization rule FoldCmpSTDistanceRight + +# Case with '<=' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val <= st_distance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 <= st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Case with '<' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val < st_distance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 < st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Case with '>=' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val >= st_distance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 >= st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Case with '>' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val > st_distance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 > st_distance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Regression test for #55675. Handle use_spheroid param. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val > st_distance(geog, 'point(0.0 0.0)', false) +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 > st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) [outer=(2,3), immutable, constraints=(/3: (/NULL - ])] + +# Tests related to old normalization rule FoldCmpSTMaxDistanceLeft + +# Case with '<=' operator. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') <= 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dfullywithin('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) + └── filters + └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] + +# Case with '<' operator. +opt expect=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_maxdistance('point(0.0 0.0)', geom) < 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── index-join geom_geog + │ ├── columns: geom:1 geog:2 val:3 + │ └── inverted-filter + │ ├── columns: rowid:4!null + │ ├── inverted expression: /7 + │ │ ├── tight: false, unique: false + │ │ └── union spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── pre-filterer expression + │ │ └── st_dfullywithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) + │ ├── key: (4) + │ └── scan geom_geog@geom_geog_geom_idx + │ ├── columns: rowid:4!null geom_inverted_key:7!null + │ ├── inverted constraint: /7/4 + │ │ └── spans + │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ ├── key: (4) + │ └── fd: (4)-->(7) + └── filters + └── st_maxdistance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] + +# Case with '>=' operator. +# Inverted index scan not expected because derived expression is +# NOT st_dwithinexclusive. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') >= 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] + +# Case with '>' operator. +# Inverted index scan not expected because derived expression is NOT st_within. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') > 5 +---- +select + ├── columns: geom:1 geog:2 val:3 + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] + +# Tests related to old normalization rule FoldCmpSTMaxDistanceRight + +# Case with '<=' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val <= st_maxdistance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 <= st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Case with '<' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val < st_maxdistance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 < st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Case with '>=' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val >= st_maxdistance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 >= st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + +# Case with '>' operator. +# Inverted index scan not expected because `st_distance` result compared +# with column. +opt expect-not=GenerateInvertedIndexScans +SELECT * FROM geom_geog WHERE val > st_maxdistance(geom, 'point(0.0 0.0)') +---- +select + ├── columns: geom:1 geog:2 val:3!null + ├── immutable + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 + └── filters + └── val:3 > st_maxdistance(geom:1, '010100000000000000000000000000000000000000') [outer=(1,3), immutable, constraints=(/3: (/NULL - ])] + # -------------------------------------------------- # GenerateZigzagJoins # -------------------------------------------------- From 0edeac4ee118145b588d55ae4ba8c97a6529bbd1 Mon Sep 17 00:00:00 2001 From: Mark Sirek Date: Thu, 17 Aug 2023 15:58:16 -0700 Subject: [PATCH 3/3] invertedidx: move geospatial filter derivation functions out of norm This commit moves functions previously associated with normalization rules, but now associated with exploration-time geospatial filter derivation out of the norm package. Epic: none Informs: #103616 Release note: None --- pkg/sql/opt/invertedidx/geo.go | 130 ++++++++++++++++++++++++++++++++- pkg/sql/opt/norm/comp_funcs.go | 124 ------------------------------- 2 files changed, 127 insertions(+), 127 deletions(-) diff --git a/pkg/sql/opt/invertedidx/geo.go b/pkg/sql/opt/invertedidx/geo.go index 58e624a10929..345050126d2a 100644 --- a/pkg/sql/opt/invertedidx/geo.go +++ b/pkg/sql/opt/invertedidx/geo.go @@ -334,6 +334,130 @@ type geoFilterPlanner struct { var _ invertedFilterPlanner = &geoFilterPlanner{} +// STDistanceUseSpheroid returns true if the use_spheroid argument of +// st_distance is not explicitly false. use_spheroid is the third argument of +// st_distance for the geography overload and it is true by default. The +// geometry overload does not have a use_spheroid argument, so if either of the +// first two arguments are geometries, it returns false. +func (g *geoFilterPlanner) STDistanceUseSpheroid(args memo.ScalarListExpr) bool { + if len(args) < 2 { + panic(errors.AssertionFailedf("expected st_distance to have at least two arguments")) + } + if args[0].DataType().Family() == types.GeometryFamily || + args[1].DataType().Family() == types.GeometryFamily { + return false + } + const useSpheroidIdx = 2 + if len(args) <= useSpheroidIdx { + // The use_spheroid argument is true by default, so return true if it + // was not provided. + return true + } + return args[useSpheroidIdx].Op() != opt.FalseOp +} + +// MaybeMakeSTDWithin attempts to derive an ST_DWithin (or ST_DFullyWithin) +// function that is similar to an expression of the following form: +// ST_Distance(a,b) <= x. The ST_Distance function can be on either side of the +// inequality, and the inequality can be one of the following: '<', '<=', '>', +// '>='. This replacement allows early-exit behavior, and may enable use of an +// inverted index scan. If the derived expression would need to be negated with +// a NotExpr, so the derivation fails, and expr, ok=false is returned. +func (g *geoFilterPlanner) MaybeMakeSTDWithin( + expr opt.ScalarExpr, + args memo.ScalarListExpr, + bound opt.ScalarExpr, + fnIsLeftArg bool, + fullyWithin bool, +) (derivedExpr opt.ScalarExpr, ok bool) { + op := expr.Op() + var not bool + var name string + fnName := "st_dwithin" + if fullyWithin { + fnName = "st_dfullywithin" + } + incName := fnName + exName := fnName + "exclusive" + switch op { + case opt.GeOp: + if fnIsLeftArg { + // Matched expression: ST_Distance(a,b) >= x. + not = true + name = exName + } else { + // Matched expression: x >= ST_Distance(a,b). + not = false + name = incName + } + + case opt.GtOp: + if fnIsLeftArg { + // Matched expression: ST_Distance(a,b) > x. + not = true + name = incName + } else { + // Matched expression: x > ST_Distance(a,b). + not = false + name = exName + } + + case opt.LeOp: + if fnIsLeftArg { + // Matched expression: ST_Distance(a,b) <= x. + not = false + name = incName + } else { + // Matched expression: x <= ST_Distance(a,b). + not = true + name = exName + } + + case opt.LtOp: + if fnIsLeftArg { + // Matched expression: ST_Distance(a,b) < x. + not = false + name = exName + } else { + // Matched expression: x < ST_Distance(a,b). + not = true + name = incName + } + } + if not { + // ST_DWithin and ST_DWithinExclusive are equivalent to ST_Distance <= x and + // ST_Distance < x respectively. The comparison operator in the matched + // expression (if ST_Distance is normalized to be on the left) is either '>' + // or '>='. Therefore, we would have to take the opposite of within. This + // would not result in a useful expression for inverted index scan, so return + // ok=false. + return expr, false + } + newArgs := make(memo.ScalarListExpr, len(args)+1) + const distanceIdx, useSpheroidIdx = 2, 3 + copy(newArgs, args[:distanceIdx]) + + // The distance parameter must be type float. + newArgs[distanceIdx] = g.factory.ConstructCast(bound, types.Float) + + // Add the use_spheroid parameter if it exists. + if len(newArgs) > useSpheroidIdx { + newArgs[useSpheroidIdx] = args[useSpheroidIdx-1] + } + + props, overload, ok := memo.FindFunction(&newArgs, name) + if !ok { + panic(errors.AssertionFailedf("could not find overload for %s", name)) + } + within := g.factory.ConstructFunction(newArgs, &memo.FunctionPrivate{ + Name: name, + Typ: types.Bool, + Properties: props, + Overload: overload, + }) + return within, true +} + // maybeDeriveUsefulInvertedFilterCondition identifies an expression of the // form: 'st_distance(a, b, bool) = 0', 'st_distance(...) <= x' or // 'st_maxdistance(...) <= x', and returns a function call to st_dwithin, @@ -365,7 +489,7 @@ func (g *geoFilterPlanner) maybeDeriveUsefulInvertedFilterCondition( if private.Name != "st_distance" { return expr, false } - if c.STDistanceUseSpheroid(function.Args) { + if g.STDistanceUseSpheroid(function.Args) { return expr, false } constant, rightIsConstant := right.(*memo.ConstExpr) @@ -417,9 +541,9 @@ func (g *geoFilterPlanner) maybeDeriveUsefulInvertedFilterCondition( boundExpr = left } if private.Name == "st_distance" { - return c.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, false /* fullyWithin */) + return g.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, false /* fullyWithin */) } - return c.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, true /* fullyWithin */) + return g.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, true /* fullyWithin */) } // extractInvertedFilterConditionFromLeaf is part of the invertedFilterPlanner diff --git a/pkg/sql/opt/norm/comp_funcs.go b/pkg/sql/opt/norm/comp_funcs.go index fae909833306..0d14881ba260 100644 --- a/pkg/sql/opt/norm/comp_funcs.go +++ b/pkg/sql/opt/norm/comp_funcs.go @@ -126,28 +126,6 @@ func findTimeZoneFunction(typ *types.T) (*tree.FunctionProperties, *tree.Overloa panic(errors.AssertionFailedf("could not find overload for timezone")) } -// STDistanceUseSpheroid returns true if the use_spheroid argument of -// st_distance is not explicitly false. use_spheroid is the third argument of -// st_distance for the geography overload and it is true by default. The -// geometry overload does not have a use_spheroid argument, so if either of the -// first two arguments are geometries, it returns false. -func (c *CustomFuncs) STDistanceUseSpheroid(args memo.ScalarListExpr) bool { - if len(args) < 2 { - panic(errors.AssertionFailedf("expected st_distance to have at least two arguments")) - } - if args[0].DataType().Family() == types.GeometryFamily || - args[1].DataType().Family() == types.GeometryFamily { - return false - } - const useSpheroidIdx = 2 - if len(args) <= useSpheroidIdx { - // The use_spheroid argument is true by default, so return true if it - // was not provided. - return true - } - return args[useSpheroidIdx].Op() != opt.FalseOp -} - // MakeIntersectionFunction returns an ST_Intersects function for the given // arguments. func (c *CustomFuncs) MakeIntersectionFunction(args memo.ScalarListExpr) opt.ScalarExpr { @@ -176,105 +154,3 @@ func (c *CustomFuncs) MakeIntersectionFunction(args memo.ScalarListExpr) opt.Sca }, ) } - -// MaybeMakeSTDWithin attempts to derive an ST_DWithin (or ST_DFullyWithin) -// function that is similar to an expression of the following form: -// ST_Distance(a,b) <= x. The ST_Distance function can be on either side of the -// inequality, and the inequality can be one of the following: '<', '<=', '>', -// '>='. This replacement allows early-exit behavior, and may enable use of an -// inverted index scan. If the derived expression would need to be negated with -// a NotExpr, so the derivation fails, and expr, ok=false is returned. -func (c *CustomFuncs) MaybeMakeSTDWithin( - expr opt.ScalarExpr, - args memo.ScalarListExpr, - bound opt.ScalarExpr, - fnIsLeftArg bool, - fullyWithin bool, -) (derivedExpr opt.ScalarExpr, ok bool) { - op := expr.Op() - var not bool - var name string - fnName := "st_dwithin" - if fullyWithin { - fnName = "st_dfullywithin" - } - incName := fnName - exName := fnName + "exclusive" - switch op { - case opt.GeOp: - if fnIsLeftArg { - // Matched expression: ST_Distance(a,b) >= x. - not = true - name = exName - } else { - // Matched expression: x >= ST_Distance(a,b). - not = false - name = incName - } - - case opt.GtOp: - if fnIsLeftArg { - // Matched expression: ST_Distance(a,b) > x. - not = true - name = incName - } else { - // Matched expression: x > ST_Distance(a,b). - not = false - name = exName - } - - case opt.LeOp: - if fnIsLeftArg { - // Matched expression: ST_Distance(a,b) <= x. - not = false - name = incName - } else { - // Matched expression: x <= ST_Distance(a,b). - not = true - name = exName - } - - case opt.LtOp: - if fnIsLeftArg { - // Matched expression: ST_Distance(a,b) < x. - not = false - name = exName - } else { - // Matched expression: x < ST_Distance(a,b). - not = true - name = incName - } - } - if not { - // ST_DWithin and ST_DWithinExclusive are equivalent to ST_Distance <= x and - // ST_Distance < x respectively. The comparison operator in the matched - // expression (if ST_Distance is normalized to be on the left) is either '>' - // or '>='. Therefore, we would have to take the opposite of within. This - // would not result in a useful expression for inverted index scan, so return - // ok=false. - return expr, false - } - newArgs := make(memo.ScalarListExpr, len(args)+1) - const distanceIdx, useSpheroidIdx = 2, 3 - copy(newArgs, args[:distanceIdx]) - - // The distance parameter must be type float. - newArgs[distanceIdx] = c.f.ConstructCast(bound, types.Float) - - // Add the use_spheroid parameter if it exists. - if len(newArgs) > useSpheroidIdx { - newArgs[useSpheroidIdx] = args[useSpheroidIdx-1] - } - - props, overload, ok := memo.FindFunction(&newArgs, name) - if !ok { - panic(errors.AssertionFailedf("could not find overload for %s", name)) - } - within := c.f.ConstructFunction(newArgs, &memo.FunctionPrivate{ - Name: name, - Typ: types.Bool, - Properties: props, - Overload: overload, - }) - return within, true -}