From ea3cb2b2cdaa4adc246f7095e6ef6fe4140236e5 Mon Sep 17 00:00:00 2001 From: Michael Erickson Date: Thu, 21 Sep 2023 11:30:06 -0700 Subject: [PATCH 1/6] Revert "sql: do not allow subqueries to be cast to enums in views and UDFs" This reverts commit 3062dc8a8ce512e9b21dd40fe04a65f827e3e256. Release note: None --- pkg/sql/create_function.go | 4 +-- pkg/sql/create_view.go | 34 +++++++++---------- pkg/sql/logictest/testdata/logic_test/udf | 24 +++++++------ pkg/sql/logictest/testdata/logic_test/views | 26 ++++++++------ .../schemachanger/scbuild/builder_state.go | 21 ++++++++---- 5 files changed, 61 insertions(+), 48 deletions(-) diff --git a/pkg/sql/create_function.go b/pkg/sql/create_function.go index 9670325f0d91..a80b194db6aa 100644 --- a/pkg/sql/create_function.go +++ b/pkg/sql/create_function.go @@ -432,9 +432,7 @@ func setFuncOption(params runParams, udfDesc *funcdesc.Mutable, option tree.Func if err != nil { return err } - typeReplacedFuncBody, err := serializeUserDefinedTypes( - params.ctx, params.p.SemaCtx(), seqReplacedFuncBody, true /* multiStmt */, "UDFs", - ) + typeReplacedFuncBody, err := serializeUserDefinedTypes(params.ctx, params.p.SemaCtx(), seqReplacedFuncBody, true /* multiStmt */) if err != nil { return err } diff --git a/pkg/sql/create_view.go b/pkg/sql/create_view.go index c0f39816b3bc..5973a98a68af 100644 --- a/pkg/sql/create_view.go +++ b/pkg/sql/create_view.go @@ -411,8 +411,7 @@ func makeViewTableDesc( desc.ViewQuery = sequenceReplacedQuery } - typeReplacedQuery, err := serializeUserDefinedTypes(ctx, semaCtx, desc.ViewQuery, - false /* multiStmt */, "view queries") + typeReplacedQuery, err := serializeUserDefinedTypes(ctx, semaCtx, desc.ViewQuery, false /* multiStmt */) if err != nil { return tabledesc.Mutable{}, err } @@ -491,7 +490,7 @@ func replaceSeqNamesWithIDs( // and serialize any user defined types, so that renaming the type // does not corrupt the view. func serializeUserDefinedTypes( - ctx context.Context, semaCtx *tree.SemaContext, queries string, multiStmt bool, parentType string, + ctx context.Context, semaCtx *tree.SemaContext, queries string, multiStmt bool, ) (string, error) { replaceFunc := func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) { var innerExpr tree.Expr @@ -506,6 +505,20 @@ func serializeUserDefinedTypes( default: return true, expr, nil } + // We cannot type-check subqueries without using optbuilder, and there + // is no need to because we only need to rewrite string values that are + // directly cast to enums. For example, we must rewrite the 'foo' in: + // + // SELECT 'foo'::myenum + // + // We don't need to rewrite the 'foo' in the query below, which can be + // corrupted by renaming the 'foo' value in the myenum type. + // + // SELECT (SELECT 'foo')::myenum + // + if _, ok := innerExpr.(*tree.Subquery); ok { + return true, expr, nil + } // semaCtx may be nil if this is a virtual view being created at // init time. var typeResolver tree.TypeReferenceResolver @@ -520,14 +533,6 @@ func serializeUserDefinedTypes( if !typ.UserDefined() { return true, expr, nil } - { - // We cannot type-check subqueries without using optbuilder, so we - // currently do not support casting expressions with subqueries to - // UDTs. - context := "casts to enums within " + parentType - defer semaCtx.Properties.Restore(semaCtx.Properties) - semaCtx.Properties.Require(context, tree.RejectSubqueries) - } texpr, err := innerExpr.TypeCheck(ctx, semaCtx, typ) if err != nil { return false, expr, err @@ -598,13 +603,6 @@ func (p *planner) replaceViewDesc( toReplace.ViewQuery = updatedQuery } - typeReplacedQuery, err := serializeUserDefinedTypes(ctx, p.SemaCtx(), toReplace.ViewQuery, - false /* multiStmt */, "view queries") - if err != nil { - return nil, err - } - toReplace.ViewQuery = typeReplacedQuery - // Reset the columns to add the new result columns onto. toReplace.Columns = make([]descpb.ColumnDescriptor, 0, len(n.columns)) toReplace.NextColumnID = 0 diff --git a/pkg/sql/logictest/testdata/logic_test/udf b/pkg/sql/logictest/testdata/logic_test/udf index 78a6e6496acd..99f6ce527bba 100644 --- a/pkg/sql/logictest/testdata/logic_test/udf +++ b/pkg/sql/logictest/testdata/logic_test/udf @@ -3575,25 +3575,29 @@ SELECT public."LOWERCASE_HINT_ERROR_EXPLICIT_SCHEMA_FN"(); subtest end -# Regression tests for #105259 and #107654. Do not type-check subqueries in UDFs -# outside optbuilder. Doing so can cause internal errors. +# Regression test for #105259. Do not type-check subqueries in UDFs outside +# optbuilder. Doing so can cause internal errors. subtest regression_105259 statement ok CREATE TYPE e105259 AS ENUM ('foo'); -statement error pgcode 0A000 subqueries are not allowed in casts to enums within UDFs +statement ok CREATE FUNCTION f() RETURNS VOID LANGUAGE SQL AS $$ SELECT (SELECT 'foo')::e105259; SELECT NULL; $$ -statement error pgcode 0A000 subqueries are not allowed in casts to enums within UDFs -CREATE FUNCTION f() RETURNS VOID LANGUAGE SQL AS $$ - SELECT ( - CASE WHEN true THEN (SELECT 'foo') ELSE NULL END - )::e105259; - SELECT NULL; -$$ +query T +SELECT f() +---- +NULL + +statement ok +ALTER TYPE e105259 RENAME VALUE 'foo' TO 'bar' + +# Renaming the enum value corrupts the UDF. This is expected behavior. +statement error pgcode 22P02 invalid input value for enum e105259: "foo" +SELECT f() subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/views b/pkg/sql/logictest/testdata/logic_test/views index 3ff91b8b9d6c..9f8e531fd5db 100644 --- a/pkg/sql/logictest/testdata/logic_test/views +++ b/pkg/sql/logictest/testdata/logic_test/views @@ -1850,21 +1850,27 @@ DROP VIEW cd_v1 CASCADE; subtest end -# Regression tests for #105259 and #107654. Do not type-check subqueries in -# views outside optbuilder. Doing so can cause internal errors. -subtest regression_105259_107654 +# Regression test for #105259. Do not type-check subqueries in views outside +# optbuilder. Doing so can cause internal errors. +subtest regression_105259 statement ok CREATE TYPE e105259 AS ENUM ('foo'); -statement error pgcode 0A000 subqueries are not allowed in casts to enums within view queries -CREATE VIEW v AS +statement ok +CREATE VIEW v105259 AS SELECT (SELECT 'foo')::e105259 -statement error pgcode 0A000 subqueries are not allowed in casts to enums within view queries -CREATE VIEW v AS -SELECT ( - CASE WHEN true THEN (SELECT 'foo') ELSE NULL END -)::e105259 +query T +SELECT * FROM v105259 +---- +foo + +statement ok +ALTER TYPE e105259 RENAME VALUE 'foo' TO 'bar' + +# Renaming the enum value corrupts the view. This is expected behavior. +statement error pgcode 22P02 invalid input value for enum e105259: "foo" +SELECT * FROM v105259 subtest end diff --git a/pkg/sql/schemachanger/scbuild/builder_state.go b/pkg/sql/schemachanger/scbuild/builder_state.go index 2da9d5d8d67b..5f1ab117a885 100644 --- a/pkg/sql/schemachanger/scbuild/builder_state.go +++ b/pkg/sql/schemachanger/scbuild/builder_state.go @@ -1485,6 +1485,20 @@ func (b *builderState) serializeUserDefinedTypes(queryStr string) string { default: return true, expr, nil } + // We cannot type-check subqueries without using optbuilder, and there + // is no need to because we only need to rewrite string values that are + // directly cast to enums. For example, we must rewrite the 'foo' in: + // + // SELECT 'foo'::myenum + // + // We don't need to rewrite the 'foo' in the query below, which can be + // corrupted by renaming the 'foo' value in the myenum type. + // + // SELECT (SELECT 'foo')::myenum + // + if _, ok := innerExpr.(*tree.Subquery); ok { + return true, expr, nil + } var typ *types.T typ, err = tree.ResolveType(b.ctx, typRef, b.semaCtx.TypeResolver) if err != nil { @@ -1493,13 +1507,6 @@ func (b *builderState) serializeUserDefinedTypes(queryStr string) string { if !typ.UserDefined() { return true, expr, nil } - { - // We cannot type-check subqueries without using optbuilder, so we - // currently do not support casting expressions with subqueries to - // UDTs. - defer b.semaCtx.Properties.Restore(b.semaCtx.Properties) - b.semaCtx.Properties.Require("casts to enums within UDFs", tree.RejectSubqueries) - } texpr, err := innerExpr.TypeCheck(b.ctx, b.semaCtx, typ) if err != nil { return false, expr, err From 8253408268efb700109bc68cf9293ecd30ea39b6 Mon Sep 17 00:00:00 2001 From: Michael Erickson Date: Thu, 21 Sep 2023 11:30:20 -0700 Subject: [PATCH 2/6] Revert "sql/sem/tree: split derived SemaContext properties from contextual info" This reverts commit 9adfd4899f828ad30aaf3226e60db8745b772362. Release note: None --- pkg/sql/opt/optbuilder/scope.go | 13 +++- pkg/sql/opt/optbuilder/testdata/aggregate | 2 +- pkg/sql/sem/tree/type_check.go | 92 ++++++++--------------- 3 files changed, 42 insertions(+), 65 deletions(-) diff --git a/pkg/sql/opt/optbuilder/scope.go b/pkg/sql/opt/optbuilder/scope.go index 4583e5e18cde..f31453995a23 100644 --- a/pkg/sql/opt/optbuilder/scope.go +++ b/pkg/sql/opt/optbuilder/scope.go @@ -1357,10 +1357,15 @@ func (s *scope) replaceWindowFn(f *tree.FuncExpr, def *tree.ResolvedFunctionDefi // We will be performing type checking on expressions from PARTITION BY and // ORDER BY clauses below, and we need the semantic context to know that we // are in a window function. InWindowFunc is updated when type checking - // FuncExpr above, but it is reset upon returning from that, so we need to - // do this update manually. - defer s.builder.semaCtx.Properties.Ancestors.PopTo(s.builder.semaCtx.Properties.Ancestors) - s.builder.semaCtx.Properties.Ancestors.Push(tree.WindowFuncAncestor) + // FuncExpr above, but it is reset upon returning from that, so we need to do + // this update manually. + defer func(ctx *tree.SemaContext, prevWindow bool) { + ctx.Properties.Derived.InWindowFunc = prevWindow + }( + s.builder.semaCtx, + s.builder.semaCtx.Properties.Derived.InWindowFunc, + ) + s.builder.semaCtx.Properties.Derived.InWindowFunc = true oldPartitions := f.WindowDef.Partitions f.WindowDef.Partitions = make(tree.Exprs, len(oldPartitions)) diff --git a/pkg/sql/opt/optbuilder/testdata/aggregate b/pkg/sql/opt/optbuilder/testdata/aggregate index 23acc1373251..bf27a55a7789 100644 --- a/pkg/sql/opt/optbuilder/testdata/aggregate +++ b/pkg/sql/opt/optbuilder/testdata/aggregate @@ -2615,7 +2615,7 @@ sort # Grouping columns cannot be reused inside an aggregate input expression # because the aggregate input expressions and grouping expressions are -# built as part of the same projection. +# built as part of the same projection. build SELECT max((k+v)/(k-v)) AS r, (k+v)*(k-v) AS s FROM kv GROUP BY k+v, k-v ---- diff --git a/pkg/sql/sem/tree/type_check.go b/pkg/sql/sem/tree/type_check.go index aa46e6f5d1aa..03a10e1c2e1b 100644 --- a/pkg/sql/sem/tree/type_check.go +++ b/pkg/sql/sem/tree/type_check.go @@ -82,21 +82,17 @@ type SemaContext struct { // Restore() method, see below. type SemaProperties struct { // required constraints type checking to only accept certain kinds - // of expressions. See Require. + // of expressions. See SetConstraint required semaRequirements // Derived is populated during semantic analysis with properties // from the expression being analyzed. The caller is responsible // for re-initializing this when needed. Derived ScalarProperties - - // Ancestors is mutated during semantic analysis to provide contextual - // information for each descendent during traversal of sub-expressions. - Ancestors ScalarAncestors } type semaRequirements struct { - // context is the name of the semantic analysis context, for use in + // context is the name of the semantic anlysis context, for use in // error messages. context string @@ -106,14 +102,11 @@ type semaRequirements struct { rejectFlags SemaRejectFlags } -// Require resets the derived properties and the scalar ancestors, and sets -// required constraints. It must only be called before starting semantic -// analysis and during traversal by semantic analysis itself. +// Require resets the derived properties and sets required constraints. func (s *SemaProperties) Require(context string, rejectFlags SemaRejectFlags) { s.required.context = context s.required.rejectFlags = rejectFlags s.Derived.Clear() - s.Ancestors.clear() } // IsSet checks if the given rejectFlag is set as a required property. @@ -187,6 +180,16 @@ type ScalarProperties struct { // SeenGenerator is set to true if the expression originally // contained a SRF. SeenGenerator bool + + // inFuncExpr is temporarily set to true while type checking the + // parameters of a function. Used to process RejectNestedGenerators + // properly. + inFuncExpr bool + + // InWindowFunc is temporarily set to true while type checking the + // parameters of a window function in order to reject nested window + // functions. + InWindowFunc bool } // Clear resets the scalar properties to defaults. @@ -194,45 +197,6 @@ func (sp *ScalarProperties) Clear() { *sp = ScalarProperties{} } -// ScalarAncestors provides context for the current scalar expression during -// semantic analysis. Ancestors are temporarily modified by expressions so that -// their descendent expressions can be analyzed with respect to their ancestors. -type ScalarAncestors byte - -const ( - // FuncExprAncestor is temporarily added to ScalarAncestors while type - // checking the parameters of a function. Used to process - // RejectNestedGenerators properly. - FuncExprAncestor ScalarAncestors = 1 << iota - - // WindowFuncAncestor is temporarily added to ScalarAncestors while type - // checking the parameters of a window function in order to reject nested - // window functions. - WindowFuncAncestor -) - -// Push adds the given ancestor to s. -func (s *ScalarAncestors) Push(other ScalarAncestors) { - *s = *s | other -} - -// Has returns true if s has the given ancestor. -func (s ScalarAncestors) Has(other ScalarAncestors) bool { - return s&other != 0 -} - -// PopTo returns s to the given set of ancestors. Use with: -// -// defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) -func (s *ScalarAncestors) PopTo(orig ScalarAncestors) { - *s = orig -} - -// clear resets s to the default set of ancestors. -func (s *ScalarAncestors) clear() { - *s = 0 -} - // MakeSemaContext initializes a simple SemaContext suitable // for "lightweight" type checking such as the one performed for default // expressions. @@ -982,7 +946,7 @@ func (sc *SemaContext) checkFunctionUsage(expr *FuncExpr, def *ResolvedFunctionD return NewInvalidFunctionUsageError(WindowClass, sc.Properties.required.context) } - if sc.Properties.Ancestors.Has(WindowFuncAncestor) && + if sc.Properties.Derived.InWindowFunc && sc.Properties.required.rejectFlags&RejectNestedWindowFunctions != 0 { return pgerror.Newf(pgcode.Windowing, "window function calls cannot be nested") } @@ -991,7 +955,7 @@ func (sc *SemaContext) checkFunctionUsage(expr *FuncExpr, def *ResolvedFunctionD // If it is an aggregate function *not used OVER a window*, then // we have an aggregation. if fnCls == AggregateClass { - if sc.Properties.Ancestors.Has(FuncExprAncestor) && + if sc.Properties.Derived.inFuncExpr && sc.Properties.required.rejectFlags&RejectNestedAggregates != 0 { return NewAggInAggError() } @@ -1002,7 +966,7 @@ func (sc *SemaContext) checkFunctionUsage(expr *FuncExpr, def *ResolvedFunctionD } } if fnCls == GeneratorClass { - if sc.Properties.Ancestors.Has(FuncExprAncestor) && + if sc.Properties.Derived.inFuncExpr && sc.Properties.required.rejectFlags&RejectNestedGenerators != 0 { return NewInvalidNestedSRFError(sc.Properties.required.context) } @@ -1090,15 +1054,23 @@ func (expr *FuncExpr) TypeCheck( } if semaCtx != nil { - // We'll need to remember we are in a function application to generate - // suitable errors in checkFunctionUsage(). We cannot enter - // FuncExprAncestor earlier (in particular not before the call to - // checkFunctionUsage() above) because the top-level FuncExpr must be - // acceptable even if it is a SRF and RejectNestedGenerators is set. - defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) - semaCtx.Properties.Ancestors.Push(FuncExprAncestor) + // We'll need to remember we are in a function application to + // generate suitable errors in checkFunctionUsage(). We cannot + // set ctx.inFuncExpr earlier (in particular not before the call + // to checkFunctionUsage() above) because the top-level FuncExpr + // must be acceptable even if it is a SRF and + // RejectNestedGenerators is set. + defer func(semaCtx *SemaContext, prevFunc bool, prevWindow bool) { + semaCtx.Properties.Derived.inFuncExpr = prevFunc + semaCtx.Properties.Derived.InWindowFunc = prevWindow + }( + semaCtx, + semaCtx.Properties.Derived.inFuncExpr, + semaCtx.Properties.Derived.InWindowFunc, + ) + semaCtx.Properties.Derived.inFuncExpr = true if expr.WindowDef != nil { - semaCtx.Properties.Ancestors.Push(WindowFuncAncestor) + semaCtx.Properties.Derived.InWindowFunc = true } } From d7dde379f52898d523726b2e881f7ad0775f7854 Mon Sep 17 00:00:00 2001 From: Michael Erickson Date: Thu, 21 Sep 2023 11:28:28 -0700 Subject: [PATCH 3/6] Revert "invertedidx: move geospatial filter derivation functions out of norm" This reverts commit 9f47c3d7be5f1dc1181d8b9b50266a54cff7ade5. 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 345050126d2a..58e624a10929 100644 --- a/pkg/sql/opt/invertedidx/geo.go +++ b/pkg/sql/opt/invertedidx/geo.go @@ -334,130 +334,6 @@ 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, @@ -489,7 +365,7 @@ func (g *geoFilterPlanner) maybeDeriveUsefulInvertedFilterCondition( if private.Name != "st_distance" { return expr, false } - if g.STDistanceUseSpheroid(function.Args) { + if c.STDistanceUseSpheroid(function.Args) { return expr, false } constant, rightIsConstant := right.(*memo.ConstExpr) @@ -541,9 +417,9 @@ func (g *geoFilterPlanner) maybeDeriveUsefulInvertedFilterCondition( boundExpr = left } if private.Name == "st_distance" { - return g.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, false /* fullyWithin */) + return c.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, false /* fullyWithin */) } - return g.MaybeMakeSTDWithin(expr, args, boundExpr, leftIsFunction, true /* fullyWithin */) + return c.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 0d14881ba260..fae909833306 100644 --- a/pkg/sql/opt/norm/comp_funcs.go +++ b/pkg/sql/opt/norm/comp_funcs.go @@ -126,6 +126,28 @@ 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 { @@ -154,3 +176,105 @@ 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 +} From 4373d26073c8549c3ca22563473285d34e10585b Mon Sep 17 00:00:00 2001 From: Michael Erickson Date: Thu, 21 Sep 2023 11:28:51 -0700 Subject: [PATCH 4/6] Revert "xform: move st_distance rules tests from norm to xform" This reverts commit b8e5b6a3ac9b4192d91dff418f7068b82f1acc8c. 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 e203d140db1d..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,6 +885,781 @@ 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 # -------------------------------------------------- @@ -949,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 @@ -980,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/rules/select b/pkg/sql/opt/xform/testdata/rules/select index 0898129d6d42..852a993a21ec 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -6960,783 +6960,6 @@ 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 27c4c2d385e6dad1f085511bd7732855b2a6d8ce Mon Sep 17 00:00:00 2001 From: Michael Erickson Date: Thu, 21 Sep 2023 11:29:08 -0700 Subject: [PATCH 5/6] Revert "invertedidx: derive geog/geom filters which enable inverted index scan" This reverts commit de7a82555eafaebf947261d6ba53929657291e5c. Release note: None --- .../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, 244 insertions(+), 825 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index cd4c299204b0..3f41e25e4d92 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -5969,159 +5969,3 @@ POLYGON ((30.010000000000002 50.009999999999998, 30.010000000000002 52.009999999 statement error 'GeometryCollection' geometry type not supported SELECT st_asencodedpolyline(geomfromewkb(('01070000A0E61000000300000001060000A0E6100000040000000103000080010000000B0000006F9AF3F7D81863C057397A0B3ACD42C006ED79D13D2DF24130E1CBF62A4A45C01B6DC0D279F452C022F8C672D2C1F9C140DB9D23050141C0B53FBA6E396656C0C0A1842E2B53EC413A8DC94C568D61401BEF0352091651C05C681DE175D2FD41A048C859DF7C5440C00E327AC97FF63F68BBA8144D0EF8416E6042F340DD6040805791CE56602740DC6C3922676401C29C6D4009B1334E40625E855D7A7E5140D4241F7D86D2D5C14B1E4BCA077A58C07806BF9D4D084D4074E74BE4DCAA00C2C11D020045A051C0D81F8E3DD0AE3740B8315FF7F63AF4C1BAD97494BA9860C07411EFAF0A6C3D4038E26DE7A194F4C16F9AF3F7D81863C057397A0B3ACD42C006ED79D13D2DF2410103000080010000000B000000610D90AE22A55FC0B8265672F7562940CE3E91EB7A320042CC96C05617B649C085229B1A09E153C0D84FA7998764EB4190FEB5A7C71B2DC03EBBCB6B588F4DC02891E1ED4A7DE6417C0F7BBC14C75840E08D4D210E2F21C04180E453EDEDFFC112E57185B2435240F0D20FC2D7C11640F8BF2E3AB98DD2C1D0A9A94D6EA14140FCABD1E5BF074B400010C2F75AA16F4166BC301BE6FB55C002F9F182DE3D53400038B551BC0495C199463681FA6E5CC0184D3E0AE179554030323F0F2525F4415791FBE04FEA61C0361E8733C77A5240C84AEBEAD466EEC1D5012A75E04F63C0A8BF403F623D4240182CFEFA9071E0C1610D90AE22A55FC0B8265672F7562940CE3E91EB7A3200420103000080010000000C000000004CADA6B302E0BF3EE5FA281ACE3BC0E051ABD6580901429B65A22CCBDC53C003DEBC27260E4AC07E45691D3E7AF541408E6FAB9A1008C016E33E2631FD40C0EEF80F0FF3E8F341A8C5EAA8B0C836409E85B8193CE952C0F4448FFF9B31F24190F9A9FCD2AC604004F99C51ED4356C0903A1F652902D4C1C01B737BD389554080315427550738C05C51AE075475F64124F6F92382235C40148AB5FED33D38C048960D3AC6D7E8412C2E310EABA5514058EA10C427562EC0A82D4D1A69D7FE41621FC77070C26540FE84B78FE5BE51400962385E53FAFEC1483899F770B14DC020BD4BBBAB604D401C08306EA143E84140337962764325C0002F089A1DF3F83FAC9B66F32C2BE841004CADA6B302E0BF3EE5FA281ACE3BC0E051ABD65809014201030000800100000006000000D03FF366A2C95C40E59BFCECDD7553C0D028B038BB63D041A40AEF5CB0195840006FB928107F084020CA0A39A4AED34100F09D4126336140E8AC65DAC4954C4024406D4040D1EE41F04B3F3DA6C75740106594C0ADCE4440A498B6372039FD4155D08A1E777452C084CC004170E949409C87DF53EB9AFDC1D03FF366A2C95C40E59BFCECDD7553C0D028B038BB63D04101020000A0E61000000200000074F55D784E5F50408F7DE14351CD41C0FCCCE7B1B166F541583C392FCD7554409022EAF3EFCC39C03CDE1C0E29A7EB4101040000A0E6100000060000000101000080E8801DFC8A2130C04401B4AE182F50C0BC13340EB7E2E1C1010100008010DC3F808D824D406C9927CDAE5144409A464D93BBD7F94101010000806F0682A50FE253C030E1964D47F74BC00E752ECB60B0F441010100008014F5242FC2C444C01257DB8E51FA51404449E2FB7D2AFFC1010100008069945BA1FD2E50C0501CA74B7A661C40CB83CB9A4894F1C10101000080C0BBBC345A4B51C0E08F6EBB2F250E400681A78F4FFCF741'::GEOGRAPHY)::BYTEA)::GEOMETRY); - -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 58e624a10929..c2bbcdec60f6 100644 --- a/pkg/sql/opt/invertedidx/geo.go +++ b/pkg/sql/opt/invertedidx/geo.go @@ -334,94 +334,6 @@ 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( @@ -432,9 +344,6 @@ 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 @@ -473,11 +382,8 @@ func (g *geoFilterPlanner) extractInvertedFilterConditionFromLeaf( ctx, g.factory, expr, args, true /* commuteArgs */, g.tabID, g.index, g.getSpanExpr, ) } - // 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 + if !invertedExpr.IsTight() { + remainingFilters = expr } return invertedExpr, remainingFilters, pfState } diff --git a/pkg/sql/opt/norm/comp_funcs.go b/pkg/sql/opt/norm/comp_funcs.go index fae909833306..a654e61eaabe 100644 --- a/pkg/sql/opt/norm/comp_funcs.go +++ b/pkg/sql/opt/norm/comp_funcs.go @@ -177,27 +177,61 @@ 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() +// 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 { var not bool var name string - fnName := "st_dwithin" - if fullyWithin { - fnName = "st_dfullywithin" - } incName := fnName exName := fnName + "exclusive" switch op { @@ -245,15 +279,7 @@ func (c *CustomFuncs) MaybeMakeSTDWithin( 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]) @@ -276,5 +302,12 @@ func (c *CustomFuncs) MaybeMakeSTDWithin( Properties: props, Overload: overload, }) - return within, true + 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 } diff --git a/pkg/sql/opt/norm/rules/comp.opt b/pkg/sql/opt/norm/rules/comp.opt index 257f2eb6b0a8..d123f9379003 100644 --- a/pkg/sql/opt/norm/rules/comp.opt +++ b/pkg/sql/opt/norm/rules/comp.opt @@ -258,6 +258,68 @@ (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 bf1145d2ec4a..c78fc082dd1f 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,70 +883,36 @@ 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 +norm expect=FoldEqZeroSTDistance SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 0 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 0.0 [outer=(1), immutable] + └── st_intersects(geom:1, '010100000000000000000000000000000000000000') [outer=(1), immutable, constraints=(/1: (/NULL - ])] # Geography case with use_spheroid=false. -opt expect=GenerateInvertedIndexScans +norm expect=FoldEqZeroSTDistance SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', false) = 0 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', false) = 0.0 [outer=(2), immutable] + └── st_intersects(geog:2, '0101000020E610000000000000000000000000000000000000') [outer=(2), immutable, constraints=(/2: (/NULL - ])] # No-op case because the constant is nonzero. -opt expect-not=GenerateInvertedIndexScans +norm expect-not=FoldEqZeroSTDistance SELECT * FROM geom_geog WHERE st_distance(geom, 'POINT(0.0 0.0)') = 1 ---- select @@ -960,7 +924,7 @@ select └── st_distance(geom:1, '010100000000000000000000000000000000000000') = 1.0 [outer=(1), immutable] # No-op case because use_spheroid=true implicitly. -opt expect-not=GenerateInvertedIndexScans +norm expect-not=FoldEqZeroSTDistance SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)') = 0 ---- select @@ -972,7 +936,7 @@ select └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') = 0.0 [outer=(2), immutable] # No-op case because use_spheroid=true. -opt expect-not=GenerateInvertedIndexScans +norm expect-not=FoldEqZeroSTDistance SELECT * FROM geom_geog WHERE st_distance(geog, 'POINT(0.0 0.0)', true) = 0 ---- select @@ -988,69 +952,31 @@ select # -------------------------------------------------- # Geometry case with '<=' operator. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') <= 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] + └── st_dwithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] # Geometry case with '<' operator. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance('point(0.0 0.0)', geom) < 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] + └── st_dwithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] # Geometry case with '>=' operator. -# Inverted index scan not expected because derived expression is -# NOT st_dwithinexclusive. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') >= 5 ---- select @@ -1059,11 +985,10 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] + └── NOT st_dwithinexclusive(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 +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance(geom, 'point(0.0 0.0)') > 5 ---- select @@ -1072,511 +997,169 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] + └── NOT st_dwithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable] # Geography case with '<=' operator. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') <= 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') <= 5.0 [outer=(2), immutable] + └── st_dwithin(geog:2, '0101000020E610000000000000000000000000000000000000', 5.0) [outer=(2), immutable, constraints=(/2: (/NULL - ])] # Geography case with '<' operator. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)') < 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000') < 5.0 [outer=(2), immutable] + └── st_dwithinexclusive(geog:2, '0101000020E610000000000000000000000000000000000000', 5.0) [outer=(2), immutable, constraints=(/2: (/NULL - ])] # Regression test for #54326. Ensure the distance param is cast to a float. -opt expect=GenerateInvertedIndexScans format=(show-scalars,show-types) +norm expect=FoldCmpSTDistanceLeft 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) + ├── columns: geom:1(geometry) geog:2(geography!null) 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) + ├── scan geom_geog + │ └── columns: geom:1(geometry) geog:2(geography) val:3(float) └── 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) + └── 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) 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) + ├── columns: geom:1(geometry) geog:2(geography!null) 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] + └── 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] # Regression test for #55675. Handle use_spheroid param. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceLeft SELECT * FROM geom_geog WHERE st_distance(geog, 'point(0.0 0.0)', true) <= 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1 geog:2!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_distance(geog:2, '0101000020E610000000000000000000000000000000000000', true) <= 5.0 [outer=(2), immutable] + └── st_dwithin(geog:2, '0101000020E610000000000000000000000000000000000000', 5.0, true) [outer=(2), immutable, constraints=(/2: (/NULL - ])] # -------------------------------------------------- # FoldCmpSTDistanceRight # -------------------------------------------------- # Case with '<=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceRight SELECT * FROM geom_geog WHERE val <= st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3 ├── 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 - ])] + └── NOT st_dwithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] # Case with '<' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceRight SELECT * FROM geom_geog WHERE val < st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3 ├── 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 - ])] + └── NOT st_dwithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] # Case with '>=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceRight SELECT * FROM geom_geog WHERE val >= st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1!null 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 - ])] + └── st_dwithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] # Case with '>' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTDistanceRight SELECT * FROM geom_geog WHERE val > st_distance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1!null 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 - ])] + └── st_dwithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /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 +norm expect=FoldCmpSTDistanceRight SELECT * FROM geom_geog WHERE val > st_distance(geog, 'point(0.0 0.0)', false) ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1 geog:2!null 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 - ])] + └── st_dwithinexclusive(geog:2, '0101000020E610000000000000000000000000000000000000', val:3, false) [outer=(2,3), immutable, constraints=(/2: (/NULL - ]; /3: (/NULL - ])] # -------------------------------------------------- # FoldCmpSTMaxDistanceLeft # -------------------------------------------------- # Case with '<=' operator. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTMaxDistanceLeft SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') <= 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') <= 5.0 [outer=(1), immutable] + └── st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] # Case with '<' operator. -opt expect=GenerateInvertedIndexScans +norm expect=FoldCmpSTMaxDistanceLeft SELECT * FROM geom_geog WHERE st_maxdistance('point(0.0 0.0)', geom) < 5 ---- select - ├── columns: geom:1 geog:2 val:3 + ├── columns: geom:1!null 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) + ├── scan geom_geog + │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_maxdistance('010100000000000000000000000000000000000000', geom:1) < 5.0 [outer=(1), immutable] + └── st_dfullywithinexclusive('010100000000000000000000000000000000000000', geom:1, 5.0) [outer=(1), immutable, constraints=(/1: (/NULL - ])] # Case with '>=' operator. -# Inverted index scan not expected because derived expression is -# NOT st_dwithinexclusive. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTMaxDistanceLeft SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') >= 5 ---- select @@ -1585,11 +1168,10 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') >= 5.0 [outer=(1), immutable] + └── NOT st_dfullywithinexclusive(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 +norm expect=FoldCmpSTMaxDistanceLeft SELECT * FROM geom_geog WHERE st_maxdistance(geom, 'point(0.0 0.0)') > 5 ---- select @@ -1598,67 +1180,59 @@ select ├── scan geom_geog │ └── columns: geom:1 geog:2 val:3 └── filters - └── st_maxdistance(geom:1, '010100000000000000000000000000000000000000') > 5.0 [outer=(1), immutable] + └── NOT st_dfullywithin(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 +norm expect=FoldCmpSTMaxDistanceRight SELECT * FROM geom_geog WHERE val <= st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3 ├── 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 - ])] + └── NOT st_dfullywithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] # Case with '<' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTMaxDistanceRight SELECT * FROM geom_geog WHERE val < st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1 geog:2 val:3 ├── 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 - ])] + └── NOT st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable] # Case with '>=' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTMaxDistanceRight SELECT * FROM geom_geog WHERE val >= st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1!null 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 - ])] + └── st_dfullywithin(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] # Case with '>' operator. -# Inverted index scan not expected because `st_distance` result compared -# with column. -opt expect-not=GenerateInvertedIndexScans +norm expect=FoldCmpSTMaxDistanceRight SELECT * FROM geom_geog WHERE val > st_maxdistance(geom, 'point(0.0 0.0)') ---- select - ├── columns: geom:1 geog:2 val:3!null + ├── columns: geom:1!null 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 - ])] + └── st_dfullywithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] # -------------------------------------------------- # FoldEqTrue + FoldEqFalse @@ -1726,11 +1300,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 +1331,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/coster/spatial-filters b/pkg/sql/opt/xform/testdata/coster/spatial-filters index 32aeb5c72f31..b845c8e919e9 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=166666.7] - ├── cost: 1036695.34 + ├── stats: [rows=55000] + ├── cost: 535578.67 ├── key: (1) └── select - ├── columns: id:1!null geog:4 + ├── columns: id:1!null geog:4!null ├── immutable - ├── stats: [rows=166666.7] - ├── cost: 1035028.65 + ├── stats: [rows=55000, distinct(4)=50000, null(4)=0] + ├── cost: 535028.65 ├── key: (1) ├── fd: (1)-->(4) ├── scan g │ ├── columns: id:1!null geog:4 - │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0] + │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0, distinct(4)=50000, null(4)=5000] │ ├── cost: 530028.62 │ ├── key: (1) │ └── fd: (1)-->(4) └── filters - └── st_distance(geog:4, '0101000020E6100000000000000000F03F000000000000F03F') < 200.0 [outer=(4), immutable] + └── st_dwithinexclusive(geog:4, '0101000020E6100000000000000000F03F000000000000F03F', 200.0) [outer=(4), immutable, constraints=(/4: (/NULL - ])] 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=166666.7] - ├── cost: 1541695.44 + ├── stats: [rows=55000] + ├── cost: 1040578.77 ├── key: (1) └── select - ├── columns: id:1!null a:2 geog:4 + ├── columns: id:1!null a:2 geog:4!null ├── immutable - ├── stats: [rows=166666.7] - ├── cost: 1540028.75 + ├── stats: [rows=55000, distinct(4)=50000, null(4)=0] + ├── cost: 1040028.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] + │ ├── stats: [rows=500000, distinct(1)=500000, null(1)=0, distinct(4)=50000, null(4)=5000] │ ├── cost: 535028.72 │ ├── key: (1) │ └── fd: (1)-->(2,4) └── filters - └── st_distance(geog:4, st_makepoint(a:2, 1.0)::GEOGRAPHY) < 200.0 [outer=(2,4), immutable] + └── st_dwithinexclusive(geog:4, st_makepoint(a:2, 1.0)::GEOGRAPHY, 200.0) [outer=(2,4), immutable, constraints=(/4: (/NULL - ])] opt SELECT id FROM g From 311eb8fef0a7cc880da062e78be0ae018da81dcf Mon Sep 17 00:00:00 2001 From: Michael Erickson Date: Thu, 21 Sep 2023 11:40:13 -0700 Subject: [PATCH 6/6] opt: prevent re-occurrence of rare unoptimized-query-oracle failure We have already fixed #103616 in 23.2, but are choosing to revert the backport to 23.1 to reduce risk. Add the involved optimizer rules to the essential rule list to try and prevent duplicate failures of the test in release-23.1. Informs: #103616 Release note: None --- pkg/sql/opt/xform/optimizer.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/sql/opt/xform/optimizer.go b/pkg/sql/opt/xform/optimizer.go index 32dc7fc2932e..030766ba1658 100644 --- a/pkg/sql/opt/xform/optimizer.go +++ b/pkg/sql/opt/xform/optimizer.go @@ -1081,6 +1081,12 @@ func (o *Optimizer) disableRulesRandom(probability float64) { // Needed to ensure that all uncorrelated EXISTS subqueries are // converted to COALESCE+subquery expressions. int(opt.ConvertUncorrelatedExistsToCoalesceSubquery), + // Prevent re-occurance of #103616 on release-23.1 branch (fixed in 23.2). + int(opt.FoldEqZeroSTDistance), + int(opt.FoldCmpSTDistanceLeft), + int(opt.FoldCmpSTDistanceRight), + int(opt.FoldCmpSTMaxDistanceLeft), + int(opt.FoldCmpSTMaxDistanceRight), ) var disabledRules RuleSet