Skip to content

Commit 72ab42b

Browse files
committed
invertedidx: harden geo code to avoid panicking
Previously, we could panic when trying to construnct an inverted expression for a geo type. Such panics are not recovered from by the vectorized panic-catcher (since we haven't allow-listed the package), so it's better to propagate errors explicitly. In particular, this allows us to prevent a crash when trying to evaluate `st_dwithin`-like functions with infinite distance (this hits an error in geos, so now we'll return an error, matching postgres). Release note: None
1 parent 52833a4 commit 72ab42b

File tree

2 files changed

+63
-33
lines changed

2 files changed

+63
-33
lines changed

pkg/sql/logictest/testdata/logic_test/inverted_index

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,3 +2119,25 @@ c" STRING
21192119
statement ok
21202120
CREATE INVERTED INDEX ON t126444 ("b
21212121
c" gin_trgm_ops)
2122+
2123+
# Regression test for panicking with some GEOS errors (#151995).
2124+
statement ok
2125+
CREATE TABLE t151995 (
2126+
c0 INT PRIMARY KEY,
2127+
c1 GEOMETRY,
2128+
INVERTED INDEX (c1 ASC)
2129+
);
2130+
2131+
statement ok
2132+
INSERT INTO t151995 (c0, c1) VALUES (
2133+
0,
2134+
'010300004001000000040000004ED667271F45EEC180F702131A41A7C138EDC3641D46F041E9E58A67A768F0C1CBB3A399C439FEC1189C38E3BBDDEEC16074F4D5F0C7FA414CCB652363EC01429029CED0E787E1C14ED667271F45EEC180F702131A41A7C138EDC3641D46F041':::GEOMETRY
2135+
);
2136+
2137+
statement ok
2138+
ANALYZE t151995;
2139+
2140+
statement error geos error: IllegalArgumentException: BufferOp::getResultGeometry distance must be a finite value
2141+
SELECT *
2142+
FROM t151995 AS t1
2143+
JOIN t151995 AS t2 ON st_dfullywithinexclusive(t2.c1, t1.c1, '+Inf');

pkg/sql/opt/invertedidx/geo.go

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func GetGeoIndexRelationship(expr opt.ScalarExpr) (_ geoindex.RelationshipType,
6161
// and getSpanExprForGeometryIndex and used in extractGeoFilterCondition.
6262
type getSpanExprForGeoIndexFn func(
6363
context.Context, tree.Datum, []tree.Datum, geoindex.RelationshipType, geopb.Config,
64-
) inverted.Expression
64+
) (inverted.Expression, error)
6565

6666
// getSpanExprForGeographyIndex gets a SpanExpression that constrains the given
6767
// geography index according to the given constant and geospatial relationship.
@@ -71,67 +71,67 @@ func getSpanExprForGeographyIndex(
7171
additionalParams []tree.Datum,
7272
relationship geoindex.RelationshipType,
7373
indexConfig geopb.Config,
74-
) inverted.Expression {
74+
) (inverted.Expression, error) {
7575
geogIdx := geoindex.NewS2GeographyIndex(*indexConfig.S2Geography)
7676
geog := d.(*tree.DGeography).Geography
7777

7878
switch relationship {
7979
case geoindex.Covers:
8080
unionKeySpans, err := geogIdx.Covers(ctx, geog)
8181
if err != nil {
82-
panic(err)
82+
return nil, err
8383
}
84-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
84+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
8585

8686
case geoindex.CoveredBy:
8787
rpKeyExpr, err := geogIdx.CoveredBy(ctx, geog)
8888
if err != nil {
89-
panic(err)
89+
return nil, err
9090
}
9191
spanExpr, err := invertedexpr.GeoRPKeyExprToSpanExpr(rpKeyExpr)
9292
if err != nil {
93-
panic(err)
93+
return nil, err
9494
}
95-
return spanExpr
95+
return spanExpr, nil
9696

9797
case geoindex.DWithin:
9898
// Parameters are type checked earlier. Keep this consistent with the definition
9999
// in geo_builtins.go.
100100
if len(additionalParams) != 1 && len(additionalParams) != 2 {
101-
panic(errors.AssertionFailedf("unexpected param length %d", len(additionalParams)))
101+
return nil, errors.AssertionFailedf("unexpected param length %d", len(additionalParams))
102102
}
103103
d, ok := additionalParams[0].(*tree.DFloat)
104104
if !ok {
105-
panic(errors.AssertionFailedf(
106-
"parameter %s is not float", additionalParams[0].ResolvedType()))
105+
return nil, errors.AssertionFailedf(
106+
"parameter %s is not float", additionalParams[0].ResolvedType())
107107
}
108108
distanceMeters := float64(*d)
109109
useSpheroid := geogfn.UseSpheroid
110110
if len(additionalParams) == 2 {
111111
b, ok := additionalParams[1].(*tree.DBool)
112112
if !ok {
113-
panic(errors.AssertionFailedf(
114-
"parameter %s is not bool", additionalParams[1].ResolvedType()))
113+
return nil, errors.AssertionFailedf(
114+
"parameter %s is not bool", additionalParams[1].ResolvedType())
115115
}
116116
if !*b {
117117
useSpheroid = geogfn.UseSphere
118118
}
119119
}
120120
unionKeySpans, err := geogIdx.DWithin(ctx, geog, distanceMeters, useSpheroid)
121121
if err != nil {
122-
panic(err)
122+
return nil, err
123123
}
124-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
124+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
125125

126126
case geoindex.Intersects:
127127
unionKeySpans, err := geogIdx.Intersects(ctx, geog)
128128
if err != nil {
129-
panic(err)
129+
return nil, err
130130
}
131-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
131+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
132132

133133
default:
134-
panic(errors.AssertionFailedf("unhandled relationship: %v", relationship))
134+
return nil, errors.AssertionFailedf("unhandled relationship: %v", relationship)
135135
}
136136
}
137137

@@ -158,54 +158,54 @@ func getSpanExprForGeometryIndex(
158158
additionalParams []tree.Datum,
159159
relationship geoindex.RelationshipType,
160160
indexConfig geopb.Config,
161-
) inverted.Expression {
161+
) (inverted.Expression, error) {
162162
geomIdx := geoindex.NewS2GeometryIndex(*indexConfig.S2Geometry)
163163
geom := d.(*tree.DGeometry).Geometry
164164

165165
switch relationship {
166166
case geoindex.Covers:
167167
unionKeySpans, err := geomIdx.Covers(ctx, geom)
168168
if err != nil {
169-
panic(err)
169+
return nil, err
170170
}
171-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
171+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
172172

173173
case geoindex.CoveredBy:
174174
rpKeyExpr, err := geomIdx.CoveredBy(ctx, geom)
175175
if err != nil {
176-
panic(err)
176+
return nil, err
177177
}
178178
spanExpr, err := invertedexpr.GeoRPKeyExprToSpanExpr(rpKeyExpr)
179179
if err != nil {
180-
panic(err)
180+
return nil, err
181181
}
182-
return spanExpr
182+
return spanExpr, nil
183183

184184
case geoindex.DFullyWithin:
185185
distance := getDistanceParam(additionalParams)
186186
unionKeySpans, err := geomIdx.DFullyWithin(ctx, geom, distance)
187187
if err != nil {
188-
panic(err)
188+
return nil, err
189189
}
190-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
190+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
191191

192192
case geoindex.DWithin:
193193
distance := getDistanceParam(additionalParams)
194194
unionKeySpans, err := geomIdx.DWithin(ctx, geom, distance)
195195
if err != nil {
196-
panic(err)
196+
return nil, err
197197
}
198-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
198+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
199199

200200
case geoindex.Intersects:
201201
unionKeySpans, err := geomIdx.Intersects(ctx, geom)
202202
if err != nil {
203-
panic(err)
203+
return nil, err
204204
}
205-
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans)
205+
return invertedexpr.GeoUnionKeySpansToSpanExpr(unionKeySpans), nil
206206

207207
default:
208-
panic(errors.AssertionFailedf("unhandled relationship: %v", relationship))
208+
return nil, errors.AssertionFailedf("unhandled relationship: %v", relationship)
209209
}
210210
}
211211

@@ -630,7 +630,11 @@ func extractGeoFilterCondition(
630630
preFilterExpr :=
631631
makeExprFromRelationshipAndParams(factory, expr, args, commuteArgs, relationship)
632632

633-
return getSpanExpr(ctx, d, additionalParams, relationship, index.GeoConfig()),
633+
invExpr, err := getSpanExpr(ctx, d, additionalParams, relationship, index.GeoConfig())
634+
if err != nil {
635+
return inverted.NonInvertedColExpression{}, nil
636+
}
637+
return invExpr,
634638
&invertedexpr.PreFiltererStateForInvertedFilterer{
635639
Expr: preFilterExpr,
636640
Col: arg2.Col,
@@ -1022,7 +1026,11 @@ func NewGeoDatumsToInvertedExpr(
10221026
// it for every row.
10231027
var invertedExpr inverted.Expression
10241028
if d, ok := nonIndexParam.(tree.Datum); ok {
1025-
invertedExpr = g.getSpanExpr(ctx, d, additionalParams, relationship, g.indexConfig)
1029+
var err error
1030+
invertedExpr, err = g.getSpanExpr(ctx, d, additionalParams, relationship, g.indexConfig)
1031+
if err != nil {
1032+
return nil, err
1033+
}
10261034
} else if funcExprCount == 1 {
10271035
// Currently pre-filtering is limited to a single FuncExpr.
10281036
preFilterRelationship = relationship
@@ -1079,7 +1087,7 @@ func (g *geoDatumsToInvertedExpr) Convert(
10791087
if g.filterer != nil {
10801088
preFilterState = g.filterer.Bind(d)
10811089
}
1082-
return g.getSpanExpr(ctx, d, t.additionalParams, t.relationship, g.indexConfig), nil
1090+
return g.getSpanExpr(ctx, d, t.additionalParams, t.relationship, g.indexConfig)
10831091

10841092
default:
10851093
return nil, fmt.Errorf("unsupported expression %v", t)

0 commit comments

Comments
 (0)