diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index a766b0a133e9..9456144151a2 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -6008,3 +6008,158 @@ SELECT ST_AsText(ST_AsMVTGeom( ST_GeomFromText('LINESTRING (0 0, 10.6 0.4, 10.6 5.4, 0 -5, 0 0)'), ST_MakeBox2D(ST_Point(0, 0), ST_Point(20, 20)), 20, -1, false)) + +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 geom 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 not 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 not 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 not 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 not 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..b5dffeb39978 100644 --- a/pkg/sql/opt/invertedidx/geo.go +++ b/pkg/sql/opt/invertedidx/geo.go @@ -334,6 +334,99 @@ 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 MakeSTDWithin, MakeSTDFullyWithin 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 op opt.Operator + var left, right opt.ScalarExpr + var function *memo.FunctionExpr + leftIsFunction := false + rightIsFunction := false + 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 g.factory.CustomFuncs().STDistanceUseSpheroid(function.Args) { + return expr, false + } + constant, rightIsConstant := right.(*memo.ConstExpr) + if !rightIsConstant { + return expr, false + } + value := constant.Value + if !g.factory.CustomFuncs().IsFloatDatum(value) { + return expr, false + } + if !g.factory.CustomFuncs().DatumsEqual(value, tree.NewDInt(0)) { + return expr, false + } + return g.factory.CustomFuncs().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 + } + op = t.Op() + // 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 makeSTDWithinFunc func(op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, fnIsLeftArg bool) opt.ScalarExpr + var boundExpr opt.ScalarExpr + if leftIsFunction { + boundExpr = right + } else { + boundExpr = left + } + if private.Name == "st_distance" { + makeSTDWithinFunc = g.factory.CustomFuncs().MakeSTDWithin + } else { + makeSTDWithinFunc = g.factory.CustomFuncs().MakeSTDFullyWithin + } + return makeSTDWithinFunc(op, args, boundExpr, leftIsFunction), true +} + // extractInvertedFilterConditionFromLeaf is part of the invertedFilterPlanner // interface. func (g *geoFilterPlanner) extractInvertedFilterConditionFromLeaf( @@ -344,6 +437,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 +478,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..35dc57dc2d2a 100644 --- a/pkg/sql/opt/norm/comp_funcs.go +++ b/pkg/sql/opt/norm/comp_funcs.go @@ -177,47 +177,24 @@ 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, +// MakeSTDWithin returns an ST_DWithin function that replaces an expression +// of the form: ST_Distance(a,b) <= x or x <= ST_Distance(a,b). fnIsLeftArg should +// be passed as true if the ST_Distance is on the left, otherwise false. +func (c *CustomFuncs) MakeSTDWithin( + op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, fnIsLeftArg bool, ) 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, +// MakeSTDFullyWithin returns an ST_DFullyWithin function that replaces an +// expression of the form: ST_MaxDistance(a,b) <= x or x <= ST_MaxDistance(a,b). +// fnIsLeftArg should be passed as true if the ST_MaxDistance is on the left, +// otherwise false. +func (c *CustomFuncs) MakeSTDFullyWithin( + op opt.Operator, args memo.ScalarListExpr, bound opt.ScalarExpr, fnIsLeftArg bool, ) opt.ScalarExpr { const fnName = "st_dfullywithin" - const fnIsLeftArg = false return c.makeSTDWithin(op, args, bound, fnName, fnIsLeftArg) } 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