-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
opt: fix geospatial function NULL argument panics #62893
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: complete! 1 of 0 LGTMs obtained (waiting on @rytaft and @sumeerbhola)
bbbe7ba
to
be94f2c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! I think this should fix #60527 too. Does that seem right?
Reviewed 3 of 3 files at r1.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @sumeerbhola)
Actually, I don't think this would fix that issue. The reproduction I just found is:
If it's easy to fix that one in this PR please do, otherwise I'll fix it in a separate PR. |
I'll give it a shot. |
be94f2c
to
6431538
Compare
@rytaft PTAL. I couldn't produce a panic with a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 2 of 2 files at r2.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @sumeerbhola)
6431538
to
6e2ce57
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This panic only occurred when the constant NULL argument was cast to
an acceptable type for the function overload, for example
ST_DWithin(a, b, NULL::FLOAT8) or ST_DWithin(NULL:GEOMETRY, b, 1).
Without the cast, no panic occured because tree.FuncExpr.TypeCheck
rewrites a tree.FuncExpr as tree.DNull when any of the arguments
have an unknown type, such as an uncast NULL. This rewriting happens
even before the tree.FuncExpr is built into a memo.FunctionExpr by
optbuilder.
I'm confused: makeSTDWithinBuiltin
does not set NullableArgs
to true, so why is NULL::FLOAT8
or NULL:GEOGRAPHY
being passed through. And the optimizer plan in the new datadriven tests suggest that the ST_DWithin
builtin will actually be called, when it isn't equipped to handle these null args (it does things like dist := tree.MustBeDFloat(args[2])
followed by float64(dist)
).
Reviewed 1 of 3 files at r1, 2 of 2 files at r2.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @mgartner)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused: makeSTDWithinBuiltin does not set NullableArgs to true, so why is NULL::FLOAT8 or NULL:GEOGRAPHY being passed through.
I'm not sure what you mean by "being passed through", but this might clear things up:
When there is a function with NullableArgs=false
and a constant untyped NULL
argument, type-checking code run during the optbuild phase rewrites that function to tree.DNull
here. When the NULL
is typed (type is not unknown), this rewrite does not occur. (I'm hoping this rewrite can be removed in favor of a normalization rule that I want to add: #62924).
And the optimizer plan in the new datadriven tests suggest that the ST_DWithin builtin will actually be called, when it isn't equipped to handle these null args (it does things like dist := tree.MustBeDFloat(args[2]) followed by float64(dist)).
I'm not 100% sure, but it appears that we avoid evaluating a function at all if NullableArgs=false
and one of its arguments is NULL
. That logic appears to take place here. Therefore, the dist := tree.MustBeDFloat(args[2])
code never executes.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @mgartner)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the NULL is typed (type is not unknown), this rewrite does not occur.
To clarify, the NULL
is typed when there is a cast, like NULL::FLOAT8
.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @mgartner)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me replay this back: with a constant untyped NULL
argument we rewrite the function to tree.DNull
in the optimizer when type checking, but when it is a typed NULL
we wait until execution time in eval.go
to skip the evaluation -- is that accurate?
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @mgartner)
pkg/sql/logictest/testdata/logic_test/inverted_join_geospatial, line 491 at r2 (raw file):
statement ok SELECT * FROM t62686 t1 JOIN t62686 t2 ON ST_DFullyWithin(t1.c, t2.c, NULL::FLOAT8)
what plan is generated for such a join that cannot be index accelerated? Is it a cross join that will yield no result after doing all the work?
pkg/sql/opt/xform/testdata/rules/select, line 4861 at r2 (raw file):
# TODO(mgartner): Functions with NullableArgs=false and a NULL argument can be # normalized to NULL. This would simplify this query plan to an empty Values # expression.
which would mean avoiding the wasteful scan (though I guess this is not a query that will really occur in practice)?
6e2ce57
to
5d5dca5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me replay this back: with a constant untyped NULL argument we rewrite the function to tree.DNull in the optimizer when type checking, but when it is a typed NULL we wait until execution time in eval.go to skip the evaluation -- is that accurate?
Exactly.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @mgartner)
This commit fixes panics that occurred when planning inverted index scans and when performing inverted joins on `GEOMETRY` and `GEOGRAPHY` inverted indexes for queries containing geospatial functions with an `NULL` constant argument. This panic only occurred when the constant `NULL` argument was cast to an acceptable type for the function overload, for example `ST_DWithin(a, b, NULL::FLOAT8)` or `ST_DWithin(NULL:GEOMETRY, b, 1)`. Without the cast, no panic occured because `tree.FuncExpr.TypeCheck` rewrites a `tree.FuncExpr` as `tree.DNull` when any of the arguments have an unknown type, such as an uncast `NULL`. This rewriting happens even before the `tree.FuncExpr` is built into a `memo.FunctionExpr` by optbuilder. Fixes cockroachdb#60527 Fixes cockroachdb#62686 Release note (bug fix): Queries that reference tables with `GEOMETRY` or `GEOGRAPHY` inverted indexes and that call geospatial functions with constant `NULL` values cast to a type, like `NULL::GEOMETRY` or `NULL::FLOAT8`, no longer error. This bug was present since 20.2.
5d5dca5
to
894cdb3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: complete! 0 of 0 LGTMs obtained (and 2 stale) (waiting on @rytaft and @sumeerbhola)
pkg/sql/logictest/testdata/logic_test/inverted_join_geospatial, line 491 at r2 (raw file):
Previously, sumeerbhola wrote…
what plan is generated for such a join that cannot be index accelerated? Is it a cross join that will yield no result after doing all the work?
Correct. The plan looks like this:
query T
EXPLAIN (OPT, VERBOSE) SELECT * FROM t62686 t1 JOIN t62686 t2 ON ST_DFullyWithin(t1.c, t2.c, NULL::FLOAT8)
----
inner-join (cross)
├── columns: c:1 c:6
├── immutable
├── stats: [rows=9801]
├── cost: 100012179
├── scan t62686 [as=t1]
│ ├── columns: t1.c:1
│ ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
│ ├── cost: 1074.61
│ ├── prune: (1)
│ └── unfiltered-cols: (1-5)
├── scan t62686 [as=t2]
│ ├── columns: t2.c:6
│ ├── stats: [rows=1000, distinct(6)=100, null(6)=10]
│ ├── cost: 1074.61
│ ├── prune: (6)
│ └── unfiltered-cols: (6-10)
└── filters
└── st_dfullywithin(t1.c:1, t2.c:6, CAST(NULL AS FLOAT8)) [outer=(1,6), immutable, constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
This could also be normalized to an empty Values expression (no scans or joins) because it should always result in no rows.
pkg/sql/opt/xform/testdata/rules/select, line 4861 at r2 (raw file):
Previously, sumeerbhola wrote…
which would mean avoiding the wasteful scan (though I guess this is not a query that will really occur in practice)?
Correct. ST_DFullyWithin(c, ST_GeomFromText('POINT(1 1)'), NULL::FLOAT8)
will always evaluate to NULL
, so it's a contradiction. There's no need to scan at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 1 of 1 files at r3.
Reviewable status: complete! 1 of 0 LGTMs obtained (and 2 stale) (waiting on @sumeerbhola)
TFTR! bors r+ |
Build succeeded: |
This commit fixes panics that occurred when planning inverted index
scans and when performing inverted joins on
GEOMETRY
andGEOGRAPHY
inverted indexes for queries containing geospatial functions with an
NULL
constant argument.This panic only occurred when the constant
NULL
argument was cast toan acceptable type for the function overload, for example
ST_DWithin(a, b, NULL::FLOAT8)
orST_DWithin(NULL:GEOMETRY, b, 1)
.Without the cast, no panic occured because
tree.FuncExpr.TypeCheck
rewrites a
tree.FuncExpr
astree.DNull
when any of the argumentshave an unknown type, such as an uncast
NULL
. This rewriting happenseven before the
tree.FuncExpr
is built into amemo.FunctionExpr
byoptbuilder.
Fixes #60527
Fixes #62686
Release note (bug fix): Queries that reference tables with
GEOMETRY
orGEOGRAPHY
inverted indexes and that call geospatial functions withconstant
NULL
values cast to a type, likeNULL::GEOMETRY
orNULL::FLOAT8
, no longer error. This bug was present since 20.2.