From b30d0da881969fe010e4146b5a061efd949cc6e3 Mon Sep 17 00:00:00 2001 From: sumeerbhola Date: Mon, 31 Aug 2020 19:12:37 -0400 Subject: [PATCH] geoindex,invertedidx,rowexec,encoding,...: add bounding box to inverted column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bounding box is encoded after the cellid, and used to perform "pre-filtering", i.e., filtering after the inverted row is fetched but before it is added to various set expressions. The fundamental weakness with the cell covering approach is that the coverings for convex shapes are typically worse than a bounding box and we pay significant cost in doing a lookup join to eliminate the false positives. This attempts to reduce those false positives for the lookup join, and part of the inverted join (the invertedJoiner can avoid adding a row to the RowContainer). For the canonical nyc st_intersects and st_dwithin queries the joinReader would see a much higher input row count than the output due to false positives. Specifically, - st_intersects: input 3110, output 490 - st_dwithin: input 23183, output 8606 With this change the input counts are 1064 and 11177. There is the additional cost of reading extra bytes and applying the bounding box filter, but it is more than offset by the gains (see comparison numbers below). This is a format change for the index, so is easier to do before the launch of spatial features. The encoding here is trivial, and consumes 17 bytes for points and 33 bytes for others. An experiment with a more complicated encoding that converted to integers while bounding the relative error did not show any improvement wrt the following benchmarks. There are TODOs for followup PRs: - Do such pre-filtering for invertedFilterer. This will need a spec change. - Support pre-filtering for expressions containing more than one function. Comparison numbers: old is master name old ms new ms delta Test/nyc/nyc-intersects 80.0 ±23% 69.0 ±30% -13.76% (p=0.000 n=49+48) Test/nyc/nyc-dwithin 225 ±13% 169 ± 8% -24.88% (p=0.000 n=46+45) Test/nyc/nyc-coveredby 84.1 ±11% 69.0 ±14% -17.96% (p=0.000 n=50+49) Test/nyc/nyc-expand-blocks 85.7 ± 6% 84.6 ± 8% -1.22% (p=0.030 n=45+47) Test/nyc/nyc-dwithin-and-dfullywithin 195 ± 7% 195 ± 6% ~ (p=1.000 n=46+49) Test/postgis_geometry_tutorial/11.2b 1.66 ±24% 1.53 ±45% -7.87% (p=0.006 n=50+50) Test/postgis_geometry_tutorial/11.6 1.61 ±19% 1.66 ±32% ~ (p=0.329 n=46+50) Test/postgis_geometry_tutorial/12.1.2 0.89 ±26% 0.91 ±26% ~ (p=0.532 n=46+50) Test/postgis_geometry_tutorial/12.2.3 1.33 ±20% 1.38 ±36% ~ (p=0.308 n=49+49) Test/postgis_geometry_tutorial/12.2.4 1.30 ±27% 1.29 ±18% ~ (p=0.746 n=49+45) Test/postgis_geometry_tutorial/13.0 1.49 ±36% 1.55 ±33% ~ (p=0.141 n=49+50) Test/postgis_geometry_tutorial/13.1a 233 ± 7% 197 ± 7% -15.45% (p=0.000 n=48+47) Test/postgis_geometry_tutorial/13.1c 24.9 ±15% 19.4 ±18% -22.17% (p=0.000 n=50+48) Test/postgis_geometry_tutorial/13.2 309 ± 4% 257 ± 7% -16.92% (p=0.000 n=43+47) Test/postgis_geometry_tutorial/14.1a 1.51 ±15% 1.70 ±16% +12.93% (p=0.000 n=48+44) Test/postgis_geometry_tutorial/14.2b 6.28 ±22% 6.07 ±19% ~ (p=0.050 n=50+45) Test/postgis_geometry_tutorial/14.2c 3.70 ±14% 3.47 ±10% -5.98% (p=0.000 n=48+46) Test/postgis_geometry_tutorial/14.3c 31.9 ±11% 25.9 ±12% -18.60% (p=0.000 n=48+49) Test/postgis_geometry_tutorial/15.0 1.50 ±20% 1.50 ±20% ~ (p=0.860 n=48+49) Release justification: This is an incompatible format change, and discussions with engineering and product indicated a preference to do this now before we launch spatial features Release note: None --- pkg/geo/geo.go | 5 + pkg/geo/geoindex/geoindex.go | 19 +- pkg/geo/geoindex/s2_geography_index.go | 16 +- pkg/geo/geoindex/s2_geometry_index.go | 16 +- pkg/geo/geoindex/s2_geometry_index_test.go | 2 +- pkg/geo/geoindex/testdata/s2_geography | 13 + pkg/geo/geoindex/testdata/s2_geometry | 8 + pkg/geo/geoindex/utils_test.go | 8 +- .../testdata/logic_test/distsql_stats | 16 +- .../logictest/testdata/logic_test/inv_stats | 10 +- .../inverted_filter_geospatial_dist | 20 +- .../inverted_filter_geospatial_explain_local | 4 +- .../logic_test/inverted_index_geospatial | 6 +- .../opt/exec/execbuilder/testdata/geospatial | 24 +- .../exec/execbuilder/testdata/inverted_index | 40 +-- pkg/sql/opt/invertedexpr/expression.go | 41 ++- pkg/sql/opt/invertedexpr/geo_expression.go | 6 +- .../opt/invertedexpr/geo_expression_test.go | 60 ++--- pkg/sql/opt/invertedidx/geo.go | 211 ++++++++++++++- pkg/sql/opt/invertedidx/geo_test.go | 213 +++++++++++++++ pkg/sql/opt/memo/testdata/stats/inverted-geo | 64 ++--- .../testdata/external/postgis-tutorial-idx | 20 +- pkg/sql/opt/xform/testdata/rules/join | 4 +- pkg/sql/opt/xform/testdata/rules/limit | 12 +- pkg/sql/opt/xform/testdata/rules/select | 252 +++++++++--------- pkg/sql/rowenc/index_encoding.go | 18 +- pkg/sql/rowexec/inverted_expr_evaluator.go | 107 +++++++- .../rowexec/inverted_expr_evaluator_test.go | 165 ++++++++++-- pkg/sql/rowexec/inverted_filterer.go | 12 +- pkg/sql/rowexec/inverted_filterer_test.go | 8 +- pkg/sql/rowexec/inverted_joiner.go | 29 +- pkg/sql/rowexec/inverted_joiner_test.go | 105 ++++++-- pkg/util/encoding/encoding.go | 116 +++++++- pkg/util/encoding/encoding_test.go | 57 ++++ 34 files changed, 1350 insertions(+), 357 deletions(-) diff --git a/pkg/geo/geo.go b/pkg/geo/geo.go index 75382273fd99..a6fcbb81764c 100644 --- a/pkg/geo/geo.go +++ b/pkg/geo/geo.go @@ -284,6 +284,11 @@ func (g *Geometry) CartesianBoundingBox() *CartesianBoundingBox { return &CartesianBoundingBox{BoundingBox: *g.spatialObject.BoundingBox} } +// BoundingBoxRef returns a pointer to the BoundingBox, if any. +func (g *Geometry) BoundingBoxRef() *geopb.BoundingBox { + return g.spatialObject.BoundingBox +} + // SpaceCurveIndex returns an uint64 index to use representing an index into a space-filling curve. // This will return 0 for empty spatial objects, and math.MaxUint64 for any object outside // the defined bounds of the given SRID projection. diff --git a/pkg/geo/geoindex/geoindex.go b/pkg/geo/geoindex/geoindex.go index f39cba2adb1f..aef051bee1b4 100644 --- a/pkg/geo/geoindex/geoindex.go +++ b/pkg/geo/geoindex/geoindex.go @@ -19,6 +19,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/geo" "github.com/cockroachdb/cockroach/pkg/geo/geogfn" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" "github.com/golang/geo/s2" ) @@ -114,8 +115,9 @@ var CommuteRelationshipMap = map[RelationshipType]RelationshipType{ // GeographyIndex is an index over the unit sphere. type GeographyIndex interface { // InvertedIndexKeys returns the keys to store this object under when adding - // it to the index. - InvertedIndexKeys(c context.Context, g geo.Geography) ([]Key, error) + // it to the index. Additionally returns a bounding box, which is non-empty + // iff the key slice is non-empty. + InvertedIndexKeys(c context.Context, g geo.Geography) ([]Key, geopb.BoundingBox, error) // Acceleration for topological relationships (see // https://postgis.net/docs/reference.html#Spatial_Relationships). Distance @@ -153,8 +155,9 @@ type GeographyIndex interface { // GeometryIndex is an index over 2D cartesian coordinates. type GeometryIndex interface { // InvertedIndexKeys returns the keys to store this object under when adding - // it to the index. - InvertedIndexKeys(c context.Context, g geo.Geometry) ([]Key, error) + // it to the index. Additionally returns a bounding box, which is non-empty + // iff the key slice is non-empty. + InvertedIndexKeys(c context.Context, g geo.Geometry) ([]Key, geopb.BoundingBox, error) // Acceleration for topological relationships (see // https://postgis.net/docs/reference.html#Spatial_Relationships). Distance @@ -218,9 +221,11 @@ const ( ) var geoRelationshipTypeStr = map[RelationshipType]string{ - Covers: "covers", - CoveredBy: "covered by", - Intersects: "intersects", + Covers: "covers", + CoveredBy: "covered by", + Intersects: "intersects", + DWithin: "dwithin", + DFullyWithin: "dfullywithin", } func (gr RelationshipType) String() string { diff --git a/pkg/geo/geoindex/s2_geography_index.go b/pkg/geo/geoindex/s2_geography_index.go index 5dcc2ca3ea6e..836217f3ab09 100644 --- a/pkg/geo/geoindex/s2_geography_index.go +++ b/pkg/geo/geoindex/s2_geography_index.go @@ -15,6 +15,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/geo" "github.com/cockroachdb/cockroach/pkg/geo/geogfn" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" "github.com/cockroachdb/cockroach/pkg/geo/geoprojbase" "github.com/cockroachdb/errors" "github.com/golang/geo/s1" @@ -108,12 +109,21 @@ func isBadGeogCovering(cu s2.CellUnion) bool { } // InvertedIndexKeys implements the GeographyIndex interface. -func (i *s2GeographyIndex) InvertedIndexKeys(c context.Context, g geo.Geography) ([]Key, error) { +func (i *s2GeographyIndex) InvertedIndexKeys( + c context.Context, g geo.Geography, +) ([]Key, geopb.BoundingBox, error) { r, err := g.AsS2(geo.EmptyBehaviorOmit) if err != nil { - return nil, err + return nil, geopb.BoundingBox{}, err + } + rect := g.BoundingRect() + bbox := geopb.BoundingBox{ + LoX: rect.Lng.Lo, + HiX: rect.Lng.Hi, + LoY: rect.Lat.Lo, + HiY: rect.Lat.Hi, } - return invertedIndexKeys(c, geogCovererWithBBoxFallback{rc: i.rc, g: g}, r), nil + return invertedIndexKeys(c, geogCovererWithBBoxFallback{rc: i.rc, g: g}, r), bbox, nil } // Covers implements the GeographyIndex interface. diff --git a/pkg/geo/geoindex/s2_geometry_index.go b/pkg/geo/geoindex/s2_geometry_index.go index 6ef83a0b7611..c34ccfc0b5e2 100644 --- a/pkg/geo/geoindex/s2_geometry_index.go +++ b/pkg/geo/geoindex/s2_geometry_index.go @@ -172,14 +172,16 @@ func isBadGeomCovering(cu s2.CellUnion) bool { } // InvertedIndexKeys implements the GeometryIndex interface. -func (s *s2GeometryIndex) InvertedIndexKeys(c context.Context, g geo.Geometry) ([]Key, error) { +func (s *s2GeometryIndex) InvertedIndexKeys( + c context.Context, g geo.Geometry, +) ([]Key, geopb.BoundingBox, error) { // If the geometry exceeds the bounds, we index the clipped geometry in // addition to the special cell, so that queries for geometries that don't // exceed the bounds don't need to query the special cell (which would // become a hotspot in the key space). gt, clipped, err := s.convertToGeomTAndTryClip(g) if err != nil { - return nil, err + return nil, geopb.BoundingBox{}, err } var keys []Key if gt != nil { @@ -189,7 +191,15 @@ func (s *s2GeometryIndex) InvertedIndexKeys(c context.Context, g geo.Geometry) ( if clipped { keys = append(keys, Key(exceedsBoundsCellID)) } - return keys, nil + bbox := geopb.BoundingBox{} + bboxRef := g.BoundingBoxRef() + if bboxRef == nil && len(keys) > 0 { + return keys, bbox, errors.AssertionFailedf("non-empty geometry should have bounding box") + } + if bboxRef != nil { + bbox = *bboxRef + } + return keys, bbox, nil } // Covers implements the GeometryIndex interface. diff --git a/pkg/geo/geoindex/s2_geometry_index_test.go b/pkg/geo/geoindex/s2_geometry_index_test.go index 4c59a7bd39be..d296e240a479 100644 --- a/pkg/geo/geoindex/s2_geometry_index_test.go +++ b/pkg/geo/geoindex/s2_geometry_index_test.go @@ -140,7 +140,7 @@ func TestNoClippingAtSRIDBounds(t *testing.T) { for i := range xCorners { g, err := geo.MakeGeometryFromPointCoords(xCorners[i], yCorners[i]) require.NoError(t, err) - keys, err := index.InvertedIndexKeys(context.Background(), g) + keys, _, err := index.InvertedIndexKeys(context.Background(), g) require.NoError(t, err) require.Equal(t, 1, len(keys)) require.NotEqual(t, Key(exceedsBoundsCellID), keys[0], diff --git a/pkg/geo/geoindex/testdata/s2_geography b/pkg/geo/geoindex/testdata/s2_geography index 0ffc2aeb81f1..0bf3f3df919a 100644 --- a/pkg/geo/geoindex/testdata/s2_geography +++ b/pkg/geo/geoindex/testdata/s2_geography @@ -25,17 +25,21 @@ MULTIPOLYGON(((-74.0126927094665 40.6600480732464,-74.0127064547618 40.660044746 init minlevel=0 maxlevel=30 maxcells=2 ---- +# NB: Bounding boxes use radian units for longitude and latitude. index-keys name=nys-approx1 ---- F4/L5/10321, F4/L5/10322 +BoundingBox: lo_x:-1.3788101090755203 hi_x:-1.3439035240356338 lo_y:0.7155849933176747 hi_y:0.7679448708775054 index-keys name=nys-approx2 ---- F4/L5/10321, F4/L5/10322 +BoundingBox: lo_x:-1.3788101090755203 hi_x:-1.3439035240356338 lo_y:0.7155849933176747 hi_y:0.7679448708775054 index-keys name=point1 ---- F0/L30/200023210133112000102232313101, F0/L30/200033223021330130001101002333 +BoundingBox: lo_x:0.017453292519943295 hi_x:0.05235987755982989 lo_y:0.06981317007977318 hi_y:0.08726646259971647 covers name=point1 ---- @@ -115,6 +119,7 @@ F0/L2/20, F0/L1/2, F0/L0/ index-keys name=shortline1 ---- F4/L19/1010131200020223230, F4/L21/101013120002022323312 +BoundingBox: lo_x:-1.5116288797541255 hi_x:-1.5116268028123157 lo_y:0.5707865269994759 hi_y:0.5707869284252046 covers name=shortline1 ---- @@ -168,6 +173,7 @@ F4/L2/10, F4/L1/1, F4/L0/ index-keys name=nystate ---- F2/L2/12, F4/L2/10 +BoundingBox: lo_x:-1.392116499292725 hi_x:-1.2523034089032152 lo_y:0.7064604119882483 hi_y:0.7856651987730039 covers name=nystate ---- @@ -184,6 +190,7 @@ F4/L1/1, F4/L0/ index-keys name=red-hook-nyc ---- F4/L13/1032010231102, F4/L13/1032010231113 +BoundingBox: lo_x:-1.2917981498721696 hi_x:-1.2917367221517457 lo_y:0.7096516548356566 hi_y:0.7096900164191924 init minlevel=0 maxlevel=30 maxcells=8 ---- @@ -191,10 +198,12 @@ init minlevel=0 maxlevel=30 maxcells=8 index-keys name=nystate ---- F2/L5/12112, F2/L5/12121, F2/L5/12122, F2/L7/1221111, F4/L4/1001, F4/L4/1032, F4/L5/10330, F4/L5/10331 +BoundingBox: lo_x:-1.392116499292725 hi_x:-1.2523034089032152 lo_y:0.7064604119882483 hi_y:0.7856651987730039 index-keys name=red-hook-nyc ---- F4/L16/1032010231102232, F4/L16/1032010231102233, F4/L15/103201023110230, F4/L17/10320102311132301, F4/L18/103201023111323020, F4/L16/1032010231113233, F4/L17/10320102311133000, F4/L17/10320102311133003 +BoundingBox: lo_x:-1.2917981498721696 hi_x:-1.2917367221517457 lo_y:0.7096516548356566 hi_y:0.7096900164191924 init minlevel=0 maxlevel=30 maxcells=1 ---- @@ -202,6 +211,7 @@ init minlevel=0 maxlevel=30 maxcells=1 index-keys name=point1 ---- F0/L30/200023210133112000102232313101, F0/L30/200033223021330130001101002333 +BoundingBox: lo_x:0.017453292519943295 hi_x:0.05235987755982989 lo_y:0.06981317007977318 hi_y:0.08726646259971647 covers name=point1 ---- @@ -281,6 +291,7 @@ F0/L2/20, F0/L1/2, F0/L0/ index-keys name=shortline1 ---- F4/L18/101013120002022323 +BoundingBox: lo_x:-1.5116288797541255 hi_x:-1.5116268028123157 lo_y:0.5707865269994759 hi_y:0.5707869284252046 covers name=shortline1 ---- @@ -344,6 +355,7 @@ init minlevel=0 maxlevel=16 maxcells=2 index-keys name=point1 ---- F0/L16/2000232101331120, F0/L16/2000332230213301 +BoundingBox: lo_x:0.017453292519943295 hi_x:0.05235987755982989 lo_y:0.06981317007977318 hi_y:0.08726646259971647 d-within distance=2000 name=point1 ---- @@ -383,6 +395,7 @@ F0/L2/20, F0/L1/2 index-keys name=nys-approx1 ---- F4/L5/10321, F4/L5/10322 +BoundingBox: lo_x:-1.3788101090755203 hi_x:-1.3439035240356338 lo_y:0.7155849933176747 hi_y:0.7679448708775054 intersects name=nys-approx1 ---- diff --git a/pkg/geo/geoindex/testdata/s2_geometry b/pkg/geo/geoindex/testdata/s2_geometry index 29010d27b04f..992235fe6a72 100644 --- a/pkg/geo/geoindex/testdata/s2_geometry +++ b/pkg/geo/geoindex/testdata/s2_geometry @@ -29,16 +29,19 @@ init minlevel=0 maxlevel=30 maxcells=4 minx=10 miny=10 maxx=20 maxy=20 index-keys name=poly1 ---- F0/L30/022222222222222222222222222222, F0/L30/133333333333333333333333333333, F0/L2/20, F0/L30/311111111111111111111111111111 +BoundingBox: lo_x:15 hi_x:16 lo_y:15 hi_y:17 index-keys name=poly1-different-orientation ---- F0/L30/022222222222222222222222222222, F0/L30/133333333333333333333333333333, F0/L2/20, F0/L30/311111111111111111111111111111 +BoundingBox: lo_x:15 hi_x:16 lo_y:15 hi_y:17 # Point exceeds the defined bounds. index-keys name=point1 ---- F0/L30/002200220022002200220022002200, spilled +BoundingBox: lo_x:12 hi_x:21 lo_y:12 hi_y:21 covers name=point1 ---- @@ -90,6 +93,7 @@ F0/L2/00, F0/L1/0, F0/L0/ index-keys name=shortline1 ---- F0/L5/00022, F0/L3/002, F0/L30/003111111111111111111111111111 +BoundingBox: lo_x:11 hi_x:12 lo_y:11 hi_y:12 covers name=shortline1 ---- @@ -155,6 +159,7 @@ F0/L2/00, F0/L1/0, F0/L0/ index-keys name=line-exceeds-bound ---- F0/L30/030033003300330033003300330033, F0/L1/1, F0/L2/21, F0/L4/2211, spilled +BoundingBox: lo_x:12 hi_x:22 lo_y:15 hi_y:23 covers name=line-exceeds-bound ---- @@ -256,6 +261,7 @@ init minlevel=0 maxlevel=4 maxcells=2 minx=10 miny=10 maxx=20 maxy=20 index-keys name=point1 ---- F0/L4/0022, spilled +BoundingBox: lo_x:12 hi_x:21 lo_y:12 hi_y:21 covers name=point1 ---- @@ -354,7 +360,9 @@ MULTIPOLYGON (((592585.8247029320336878299713 4527600.0504941008985042572021, 59 index-keys name=troubled-poly1 ---- F0/L17/02213031322200022, F0/L18/022130313222001333, F0/L16/0221303132220020, F0/L16/0221303132220031 +BoundingBox: lo_x:586773.9053534917 hi_x:586890.2225210913 lo_y:4.500142974460221e+06 hi_y:4.500270330213479e+06 index-keys name=troubled-poly2 ---- F0/L16/0221303121021212, F0/L16/0221303121021221, F0/L16/0221303121312112, F0/L16/0221303121312121 +BoundingBox: lo_x:592324.7216157623 hi_x:592592.2261793566 lo_y:4.527481727534395e+06 hi_y:4.527693397730719e+06 diff --git a/pkg/geo/geoindex/utils_test.go b/pkg/geo/geoindex/utils_test.go index a7f8ed02dc29..167a578e9705 100644 --- a/pkg/geo/geoindex/utils_test.go +++ b/pkg/geo/geoindex/utils_test.go @@ -16,6 +16,7 @@ import ( "strings" "testing" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" "github.com/cockroachdb/datadriven" "github.com/golang/geo/s2" ) @@ -39,15 +40,18 @@ func s2Config(t *testing.T, d *datadriven.TestData) S2Config { } } -func keysToString(keys []Key, err error) string { +func keysToString(keys []Key, bbox geopb.BoundingBox, err error) string { if err != nil { return err.Error() } + if len(keys) == 0 { + return "" + } var cells []string for _, k := range keys { cells = append(cells, k.String()) } - return strings.Join(cells, ", ") + return fmt.Sprintf("%s\nBoundingBox: %s", strings.Join(cells, ", "), bbox.String()) } func cellUnionToString(cells s2.CellUnion) string { diff --git a/pkg/sql/logictest/testdata/logic_test/distsql_stats b/pkg/sql/logictest/testdata/logic_test/distsql_stats index da004136b3f1..6cd1b8b7728f 100644 --- a/pkg/sql/logictest/testdata/logic_test/distsql_stats +++ b/pkg/sql/logictest/testdata/logic_test/distsql_stats @@ -925,9 +925,9 @@ WHERE statistics_name = 's' AND column_names = '{geog}' query TIRI colnames SHOW HISTOGRAM $hist_id_1 ---- -upper_bound range_rows distinct_range_rows equal_rows -'\xfd1000000000000000' 0 0 1 -'\xfd5000000000000000' 0 0 1 +upper_bound range_rows distinct_range_rows equal_rows +'\x42fd1000000000000000000000000000000000bcc00000000000003ffbecde5da115a83ff661bdc396bcdc' 0 0 1 +'\x42fd5000000000000000000000000000000000bcc00000000000003ffbecde5da115a83ff661bdc396bcdc' 0 0 1 statement ok DROP INDEX geo_table@geog_idx_1; @@ -941,8 +941,8 @@ WHERE statistics_name = 's' AND column_names = '{geog}' query TIRI colnames SHOW HISTOGRAM $hist_id_1 ---- -upper_bound range_rows distinct_range_rows equal_rows -'\xfd1000000000000000' 0 0 1 -'\xfd4500000000000000' 0 0 1 -'\xfd4700000000000000' 0 0 1 -'\xfd5ad4000000000000' 0 0 1 +upper_bound range_rows distinct_range_rows equal_rows +'\x42fd1000000000000000000000000000000000bcc00000000000003ffbecde5da115a83ff661bdc396bcdc' 0 0 1 +'\x42fd4500000000000000000000000000000000bcc00000000000003ffbecde5da115a83ff661bdc396bcdc' 0 0 1 +'\x42fd4700000000000000000000000000000000bcc00000000000003ffbecde5da115a83ff661bdc396bcdc' 0 0 1 +'\x42fd5ad4000000000000000000000000000000bcc00000000000003ffbecde5da115a83ff661bdc396bcdc' 0 0 1 diff --git a/pkg/sql/logictest/testdata/logic_test/inv_stats b/pkg/sql/logictest/testdata/logic_test/inv_stats index 1facbb387f80..4cf6ecd70ad9 100644 --- a/pkg/sql/logictest/testdata/logic_test/inv_stats +++ b/pkg/sql/logictest/testdata/logic_test/inv_stats @@ -56,11 +56,11 @@ SELECT histogram_id FROM [SHOW STATISTICS FOR TABLE t] WHERE statistics_name = ' query TIRI colnames SHOW HISTOGRAM $hist_id_1 ---- -upper_bound range_rows distinct_range_rows equal_rows -'\xfd0555555555555555' 0 0 1 -'\xfd0fffffff00000000' 0 0 1 -'\xfd1000000100000000' 0 0 1 -'\xfd1aaaaaab00000000' 0 0 1 +upper_bound range_rows distinct_range_rows equal_rows +'\x42fd055555555555555500000000000000000000000000000000003ff00000000000003ff0000000000000' 0 0 1 +'\x42fd0fffffff0000000000000000000000000000000000000000003ff00000000000003ff0000000000000' 0 0 1 +'\x42fd100000010000000000000000000000000000000000000000003ff00000000000003ff0000000000000' 0 0 1 +'\x42fd1aaaaaab0000000000000000000000000000000000000000003ff00000000000003ff0000000000000' 0 0 1 # Regression for #50596. statement ok diff --git a/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_dist b/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_dist index a18e2bd4b696..a1c3b16cad1f 100644 --- a/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_dist +++ b/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_dist @@ -35,7 +35,7 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE ST_Intersects('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU29P2z4Qfv_7FKd7Q6uf19pOKNSvykbYMhXK0k4bIhXKmhOLCHZmuxMT6nef0jCgZe2oXyS5P889z93F9-h-lKhwHA2jdxOY2xJOktEpXEZfz4dH8Rm0juPxZPxp2IaHlJsm4ZrMlc--lQRfPkRJBM5fFdqTdTTzrrV3-nk4ic9H8dmk1ZIdCbIj2wxaQYdD0OHt9p5S76PRaTRJLlhd67YNo-Q4SuDtBdxMkaE2OZ1lt-RQXaLAKcPKmhk5Z2ztul8mxPkdKs6w0NXc1-4pw5mxhOoefeFLQoWTWmRCWU62y5FhTj4rymXZxx4GtYKrQud0hwzHVaadgq4Q-7IvxT4Pe7x3GPb6B703f3EeQKZzCA_B-O9kHU4XDM3cPylyPrsmVGLBXq861j_JespPitKTJdsVq9L_xKO7yoLRMBAKXK0bnM-sVymmaXCwn6Zc8jTl_F8PBNL5jiiRIqz1znA09woGYuMU5C5T-GgK_bA6uWl1lS1uM_vriZoN5Eb2YBf2sbGebDdYZR6I_5Fhsxe1_ttzwUPeHPnwFlw0H_2j_uPhInxhP2WunMPwhb2nnt-egWy_Yu7hLp0n5CqjHa10vqkyX0wZUn5Nzb10Zm5ndG7NbEnTmKMlbunIyfkmKhoj1k2oFvgcLLaC5Xaw3AoOtoODreBwDTxd_Pc7AAD__89Km1E= +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU99P2z4Qf__-Fda90OrrtbbTaeCnwghbpkJZmmlDOEJZc2MRwc5sd8qE-r9PSRjQohZ6D07ux-fuPne6O3C_SpAwCyfh-4QsbElO4ukpuQy_nU8OozPSO45myezzpE_uQ266gGs0Vz77XiL5-jGMQ-L8VaE9Wodz73p7p18mSXQ-jc6SXk8MBBED0aekFwwYCQas39-T8kM4PQ2T-II2uW77ZBofhzE5uiA3KVDQJsez7BYdyEvgkFKorJmjc8Y2prs2IMprkIxCoauFb8wphbmxCPIOfOFLBAlJ02SMWY52yIBCjj4ryjbtA4dx08FVoXOsgcKsyrSTZKjgSKn6R65UzZlSNXvpgTe7YrgCkumcjPaJ8T_ROkiXFMzCP_JxPrtGkHxJX8850r_ResxPitKjRTvkq8T_-cO6ssRoMuaSuIY1cT6zXrYsgndvlWKCKcXYSw8Q1PmusIb8GnsK04WXZMw3zkHsModPptD3qxebVl_Z4jazfx5L07HYWD3YpfrMWI92GKxWHvP_gUK3Gbl-NoyzEetE3H85493PweHBgzA-eqY_Rq7I_uiZviefXt9Y9F8x99EuzGN0ldEOV5hvysyWKQXMr7G7a2cWdo7n1szbMp06bXGtIUfnOy_vlEh3rqbBp2C-FSy2g8VWcLAdHGwFj9bA6fK_vwEAAP__phmzgw== # The inverted filterer handles five inverted index rows with decoded # datums, where the first column is the PK (k) and the second is the cellid @@ -65,7 +65,7 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE ST_Intersects('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU29P2z4Qfv_7FKd7Q6uf19pOKNSvykbYMhXK0k4bIhXKmhOLCHZmuxMT6nef0jCgZe2oXyS5P889z93F9-h-lKhwHA2jdxOY2xJOktEpXEZfz4dH8Rm0juPxZPxp2IaHlJsm4ZrMlc--lQRfPkRJBM5fFdqTdTTzrrV3-nk4ic9H8dmk1ZIdCbIj2wxaQYdD0OHt9p5S76PRaTRJLlhd67YNo-Q4SuDtBdxMkaE2OZ1lt-RQXaLAKcPKmhk5Z2ztul8mxPkdKs6w0NXc1-4pw5mxhOoefeFLQoWTWmRCWU62y5FhTj4rymXZxx4GtYKrQud0hwzHVaadgq4Q-7IvxT4Pe7x3GPb6B703f3EeQKZzCA_B-O9kHU4XDM3cPylyPrsmVGLBXq861j_JespPitKTJdsVq9L_xKO7yoLRMBAKXK0bnM-sVymmaXCwn6Zc8jTl_F8PBNL5jiiRIqz1znA09woGYuMU5C5T-GgK_bA6uWl1lS1uM_vriZoN5Eb2YBf2sbGebDdYZR6I_5Fhsxe1_ttzwUPeHPnwFlw0H_2j_uPhInxhP2WunMPwhb2nnt-egWy_Yu7hLp0n5CqjHa10vqkyX0wZUn5Nzb10Zm5ndG7NbEnTmKMlbunIyfkmKhoj1k2oFvgcLLaC5Xaw3AoOtoODreBwDTxd_Pc7AAD__89Km1E= +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU99P2z4Qf__-Fda90OrrtbbTaeCnwghbpkJZmmlDOEJZc2MRwc5sd8qE-r9PSRjQohZ6D07ux-fuPne6O3C_SpAwCyfh-4QsbElO4ukpuQy_nU8OozPSO45myezzpE_uQ266gGs0Vz77XiL5-jGMQ-L8VaE9Wodz73p7p18mSXQ-jc6SXk8MBBED0aekFwwYCQas39-T8kM4PQ2T-II2uW77ZBofhzE5uiA3KVDQJsez7BYdyEvgkFKorJmjc8Y2prs2IMprkIxCoauFb8wphbmxCPIOfOFLBAlJ02SMWY52yIBCjj4ryjbtA4dx08FVoXOsgcKsyrSTZKjgSKn6R65UzZlSNXvpgTe7YrgCkumcjPaJ8T_ROkiXFMzCP_JxPrtGkHxJX8850r_ResxPitKjRTvkq8T_-cO6ssRoMuaSuIY1cT6zXrYsgndvlWKCKcXYSw8Q1PmusIb8GnsK04WXZMw3zkHsModPptD3qxebVl_Z4jazfx5L07HYWD3YpfrMWI92GKxWHvP_gUK3Gbl-NoyzEetE3H85493PweHBgzA-eqY_Rq7I_uiZviefXt9Y9F8x99EuzGN0ldEOV5hvysyWKQXMr7G7a2cWdo7n1szbMp06bXGtIUfnOy_vlEh3rqbBp2C-FSy2g8VWcLAdHGwFj9bA6fK_vwEAAP__phmzgw== statement ok ALTER INDEX geo_table@geom_index EXPERIMENTAL_RELOCATE VALUES (ARRAY[2], 1152921574000000000) @@ -89,7 +89,7 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE ST_Intersects('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJzcVF9v2j4Uff99Cuu-FPTzwHb-UPxEt6ZbJlq6wLRVDaoyctVFpTazzdSp4rtPIV0pFNKyx_kB4ut7fM-5uSf3YH9MQcIw6kfvRmRupuQkGZySy-jref8oPiON43g4Gn7qN8lDyk2VcI36ymXfpki-fIiSiFh3VSiHxuLE2cbB6ef-KD4fxGejRkO0BBEt0aSk4bUY8Vqs2TyQ8n00OI1GyQUt77ptkkFyHCXk7QW5GQMFpXM8y27RgrwEDhQEjCnMjJ6gtdqU4ftlUpzfgWQUCjWbuzI8pjDRBkHegyvcFEHCqCSaYJajaTOgkKPLiuny6kcdvZLFVaFyvAMKw1mmrCRtzgPRFTxgfsjCQz_sdsI3W4Idkqmc8C7R7jsaC-MFBT13K0bWZdcIki_o61nH6icah_lJMXVo0LTFOvU_59HdzBCtSI9LYkvexLrMOJlCmnqdIE2ZYGnK2Es_QFDle6J4CmSp3T98SbvYqX0lea60ydFgvqZ0vNjSnePCukJNXNtf70qvHJbB3EnS4zu5ePu8h4-6UA_DE-wanpkpbjPza1Wa9sTO6v4-1YfaODTtcFPl_0Chmgy5aT7Gmc-qJR7-OePVQ_eo-7gY95_tV5lr69B_tj-QTz3cE81X9D1YU85f71r-V67t-CXtMAi8J65dBSvXihcnl-_D-plrvX_UtVu0J2hnWlnccO_2m1npasyvsfoEWD03Ezw3erIsU20HS9wykKN11SmvNrGqjkqCT8G8FizWwHwTLGrBXn1lrxbs14P9WnCnHhzUgsN6cLhXw8aL_34HAAD__0SiiJk= +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU99P2z4Qf__-Fda90OrrtbbTaeCnwghbpkJZmmlDOEJZc2MRwc5sd8qE-r9PSRjQohZ6D07ux-fuPne6O3C_SpAwCyfh-4QsbElO4ukpuQy_nU8OozPSO45myezzpE_uQ266gGs0Vz77XiL5-jGMQ-L8VaE9Wodz73p7p18mSXQ-jc6SXk8MBBED0aekFwwYCQas39-T8kM4PQ2T-II2uW77ZBofhzE5uiA3KVDQJsez7BYdyEvgkFKorJmjc8Y2prs2IMprkIxCoauFb8wphbmxCPIOfOFLBAlJ02SMWY52yIBCjj4ryjbtA4dx08FVoXOsgcKsyrSTZKjgSKn6R65UzZlSNXvpgTe7YrgCkumcjPaJ8T_ROkiXFMzCP_JxPrtGkHxJX8850r_ResxPitKjRTvkq8T_-cO6ssRoMuaSuIY1cT6zXrYsgndvlWKCKcXYSw8Q1PmusIb8GnsK04WXZMw3zkHsModPptD3qxebVl_Z4jazfx5L07HYWD3YpfrMWI92GKxWHvP_gUK3Gbl-NoyzEetE3H85493PweHBgzA-eqY_Rq7I_uiZviefXt9Y9F8x99EuzGN0ldEOV5hvysyWKQXMr7G7a2cWdo7n1szbMp06bXGtIUfnOy_vlEh3rqbBp2C-FSy2g8VWcLAdHGwFj9bA6fK_vwEAAP__phmzgw== # Data is distributed, but the filterer can't be distributed since it is not a union. query I @@ -102,7 +102,7 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE ST_CoveredBy('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU99P2zAQft9fcboXWs1rbScU6qeyEbZMhbK004ZIhEJzYhEhzmwXgVD_9ykNA1rWjvohyf347vvuLn5A-7tAheNgGHyawMwUcBSNjuE8-Hk6PAhPoHUYjifjb8M2PKZcNwlXpC9celkQ_PgSRAFYdzHVt2Qou7xv7Rx_H07C01F4Mmm1ZEeC7Mg2g5bX4eB1eLu9o9TnYHQcTKIzVpe6acMoOgwi-HgG1wkyLHVGJ-kNWVTnKDBhWBk9JWu1qV0Pi4Qwu0PFGeZlNXO1O2E41YZQPaDLXUGocFJrjCjNyHQ5MszIpXmxKPvUwqBWcJGXGd0hw3GVllZBV4hd2Zdil_s93tv3e_293od_OPcgLTPw90G7X2QsJnOGeuaeFVmXXhEqMWdvVx2Wt2QcZUd54ciQ6Ypl6X_jwV1lQJcwEApsrRusS41TMcaxt7cbx1zyOOb8fw8EKrMtUSJGWOmd4WjmFAzE2inIbabwVefl4-rkutVVJr9Jzf0zNRvItezeNuxjbRyZrrfMPBDvkWGzF7Xy13PBfd4c-fgWXDQf_YP-0-HCf2U_Zy6dff-VvaNeXp6BbL9h7P42jUdkK11aWmp8XWU-TxhSdkXNtbR6ZqZ0avR0QdOYowVu4cjIuiYqGiMsm1At8CVYbATLzWC5EextBnsbwf4KOJm_-xMAAP__NiSaTw== +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU19P2zAQf9-nsO6FVvNa2-k08FNhhC1ToSzttCEcoVDfWESIM9tFQajffUrCgBa10Htwcn9-d_e7092D-5uDhEk4Cj9Pydzm5CgeH5Pz8NfpaD86IZ3DaDKdfB91yUPIdRtwhebCp5c5kp9fwzgkzl_MzC1a1Jd3nZ3jH6NpdDqOTqadjugJInqiS0kn6DES9Fi3uyPll3B8HE7jM1qnuumScXwYxuTgjFwnQKEwGk_SG3Qgz4FDQqG0ZobOGVub7puASFcgGYWsKOe-NicUZsYiyHvwmc8RJEzrHmNMNdo-AwoafZrlTdpHCsO6g4us0FgBhUmZFk6SvoIDparfWqmKM6Uq9toDH7bFcAUkLTQZ7BLj_6B1kCwomLl_4uN8eoUg-YK-nXNU3KL1qI-y3KNF2-fLxP_7w6q0xBRkyCVxNWvifGq9bFgEnz4qxQRTirHXHiBY6G1hNfkV9hTGcy_JkK-dg9hmDt9MVjysXqxbfWmzm9TePZWmQ7G2erBN9YmxHm0_WK485O-BQrsZuXI1jLMBa0U8fDnj7c_e_t6jMD54oT9FLsnu4IW-I58f31B03zD2wTbEY3SlKRwuEV-XmS0SCqivsD1rZ-Z2hqfWzJoyrTpucI1Bo_Otl7dKVLSuusHnYL4RLDaDxUZwsBkcbAQPVsDJ4t2_AAAA___UcrKB # Move all the index data that will be read to node 2 while the query executes # at node 1. The filtering moves to node 2 when it is distributable. @@ -129,7 +129,7 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE ST_Intersects('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk11P2zwUgO_fX3F0bmj1mtZ20kJ9VTbClqm0LO20IVKhrDliESHObHdiQv3vUxoGFNZAfdHmfDznU-cO7c8cFU6DUfB-BkuTw0k0OYWL4NvZ6CgcQ-s4nM6mn0dtuHe5rh2uSF-65HtO8PVjEAVg3WVWODKWFs629k6_jGbh2SQcz1ot2ZEgO7LNoOV1OHgd3m7vKfUhmJwGs-icVbFu2jCJjoMI3p3D9RwZFjqlcXJDFtUFCmQocc6wNHpB1mpTqe_WTmF6i4ozzIpy6Sr1nOFCG0J1hy5zOaHCsd7XZddHhim5JMvXbiuGeukeIeuSK0LVW7EngUVz4Fk1gYiSlEyXb4THhwENq_YusyKlW2Q4LZPCKugK0ZMDKXrc7_P-od8fHPT3_6E8gKRIwT8E7X6QsbitarFL1WHxi4yj9CTLHRkyXbFZ-l97cFsa0AUMhQJb1Q3WJcapGOPYO-jFMZc8jjl_7QeBinRHSsQIz3pnOFk6BUOxdQpylyl80llxvzq5bXWlyW4S8_sxNRvKrdm9XbJPtXFkut5m5qH4HxnWe1HPb4oL7vP6yft_wUX9MTgaPDwu_Bfyo-fGO_RfyHvq6WkOZfsNc_c3On_lGCOypS4sveka-WrOkNIrqg_e6qVZ0JnRi3WaWpysubUiJetqa68WwqI2VQU-hUUjLJth2Qh7zbDXCPvNsN8I82fwfPXfnwAAAP__6QTNaA== +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUlNFP2z4Qx99_f4V1L7T6mdZ2ggZ-KoywZSotSzttCFcoa24sotiZ7U6ZUP_3KQkDCmpK_eDk7Pvc5Xunyz24XwuQMImG0fspWdoFOUvG5-Qq-nYxPI5HpHMaT6aTz8MueXC5bRxu0Fz79PsCydePURIR569z7dE6nHvX2Tv_MpzGF-N4NO10RE8Q0RNdSjpBj5Ggx7rdPSk_ROPzaJpc0irWXZeMk9MoISeX5HYGFLTJcJTeoQN5BRwoCJhRKKyZo3PGVsf3tVOclSAZhVwXS18dzyjMjUWQ9-Bzv0CQMDL7puiHQCFDn-aL2m1FwSz9E-R8eoMgD1b0WWDeHnhaVSDBNEPbZ2vh4bFAg0reda4zLIHCpEi1k6Sv4ESp8kemVMmZUiXbtsH-rgxXQFKdkfCQGP8TrYNNmvkummP9G63H7CxfeLRo-3xd-L_7qCwsMZoMuCSuUk2cT62XtYrg3YFSTDClGNu2AUGd7YpV4l-opzBeekkGfGMdxC51-GRy_dB6san1hc3vUvvnKTUdiI3Zg12yT4z1aPvBeuYB_x8oNJ2RL2eScRayZomHJ2e8eTk6PnpcjIev7CfPtXUYvrL35PPRHojuG-oerinfMswJusJoh2-aZraaUcDsBpsfhjNLO8cLa-Z1msYc11x9kKHzze1BY8S6uao-8DnMW2HRDotWOGiHg1Y4bIfDVpi9gGer__4GAAD__9zC5Zo= query I SELECT k FROM geo_table WHERE ST_CoveredBy('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k @@ -142,7 +142,7 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE ST_CoveredBy('MULTIPOINT((2.2 2.2), (3.0 3.0))'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU99P2zAQft9fcboXWs1rbScU6qeyEbZMhbK004ZIhEJzYhEhzmwXgVD_9ykNA1rWjvohyf347vvuLn5A-7tAheNgGHyawMwUcBSNjuE8-Hk6PAhPoHUYjifjb8M2PKZcNwlXpC9celkQ_PgSRAFYdzHVt2Qou7xv7Rx_H07C01F4Mmm1ZEeC7Mg2g5bX4eB1eLu9o9TnYHQcTKIzVpe6acMoOgwi-HgG1wkyLHVGJ-kNWVTnKDBhWBk9JWu1qV0Pi4Qwu0PFGeZlNXO1O2E41YZQPaDLXUGocFJrjCjNyHQ5MszIpXmxKPvUwqBWcJGXGd0hw3GVllZBV4hd2Zdil_s93tv3e_293od_OPcgLTPw90G7X2QsJnOGeuaeFVmXXhEqMWdvVx2Wt2QcZUd54ciQ6Ypl6X_jwV1lQJcwEApsrRusS41TMcaxt7cbx1zyOOb8fw8EKrMtUSJGWOmd4WjmFAzE2inIbabwVefl4-rkutVVJr9Jzf0zNRvItezeNuxjbRyZrrfMPBDvkWGzF7Xy13PBfd4c-fgWXDQf_YP-0-HCf2U_Zy6dff-VvaNeXp6BbL9h7P42jUdkK11aWmp8XWU-TxhSdkXNtbR6ZqZ0avR0QdOYowVu4cjIuiYqGiMsm1At8CVYbATLzWC5EextBnsbwf4KOJm_-xMAAP__NiSaTw== +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU19P2zAQf9-nsO6FVvNa2-k08FNhhC1ToSzttCEcoVDfWESIM9tFQajffUrCgBa10Htwcn9-d_e7092D-5uDhEk4Cj9Pydzm5CgeH5Pz8NfpaD86IZ3DaDKdfB91yUPIdRtwhebCp5c5kp9fwzgkzl_MzC1a1Jd3nZ3jH6NpdDqOTqadjugJInqiS0kn6DES9Fi3uyPll3B8HE7jM1qnuumScXwYxuTgjFwnQKEwGk_SG3Qgz4FDQqG0ZobOGVub7puASFcgGYWsKOe-NicUZsYiyHvwmc8RJEzrHmNMNdo-AwoafZrlTdpHCsO6g4us0FgBhUmZFk6SvoIDparfWqmKM6Uq9toDH7bFcAUkLTQZ7BLj_6B1kCwomLl_4uN8eoUg-YK-nXNU3KL1qI-y3KNF2-fLxP_7w6q0xBRkyCVxNWvifGq9bFgEnz4qxQRTirHXHiBY6G1hNfkV9hTGcy_JkK-dg9hmDt9MVjysXqxbfWmzm9TePZWmQ7G2erBN9YmxHm0_WK485O-BQrsZuXI1jLMBa0U8fDnj7c_e_t6jMD54oT9FLsnu4IW-I58f31B03zD2wTbEY3SlKRwuEV-XmS0SCqivsD1rZ-Z2hqfWzJoyrTpucI1Bo_Otl7dKVLSuusHnYL4RLDaDxUZwsBkcbAQPVsDJ4t2_AAAA___UcrKB # Bounding box operations. statement ok @@ -152,22 +152,22 @@ query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE geom && 'POINT(3.0 3.0)'::geometry] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk29v2j4Qx5__XsXpntBKbrET_rR-xE9bumWiwABpmxpUZeTEogU7s52JCfHepyTdWuhISx5Yuj-fy33P5y3aHxlKnAXD4M0cCpPBzXR8C3fB58nw_3AEZ2_D2Xz2cXgODynf64QV6XsXf80IPr0PpkFpryEqOPd69QmtyTgczc_8Sw7-JT9vSfkuGN8G8-mXBTJUOqFRvCaL8g4FMvRwwTA3eknWalO6t1VSmGxQcoapygtXuhcMl9oQyi261GWEEkf6QudtHxkm5OI0q9J2DHXhHiHr4hWh7OzYk8KiufC8VDilOCHT5nvl8e8ABqX0-1QltEGGszxWVkJbiK537Yku7_R476rTu-73Lv7h7EOsEvA5aPeNjMVjXYtTug7VTzKOkps0c2TItMV-63_iwSY3oBUMhARb9g3WxcbJCKPI73ejiHs8ijh_6UAglZxIiQjhQDvDceEkDMTRKXinTOGDTtXD1XnHri436To2v5BhPSsJA-9gi7nggj__rjrP7JZ8XPFXiPH3xLyw4VOyuVaWXrXifLdgSMmK6ldkdWGWNDF6Wf2mNscVVzkSsq6OdmojVHWobPApLBphrxn2GmG_GfYbYX4AL3b__Q4AAP__ZFp--A== +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk19v2jAQwN_3Kax7oZXcYifsj_zE_qRbJgoMkLapjqoM31i0YGe2M2VCfPcpSbcWKkLhwdL57nfkd9ZtwP3KQcA8GkVvF6S0ObmaTa7JTfRlOnodj8nZu3i-mH8anZO7kp9twQrNrU-_5Ug-f4hmUR2viSwZC160J-lNJ_F4cRZeMhJesvOeEO-jyXW0mH1NgII2CsfpGh2IG-BAIYCEQmHNEp0ztr7eNEWxqkAwCpkuSl9fJxSWxiKIDfjM5wgCxubCFP0QKCj0aZY3ZVsKpvT3kPPpCkEMtvRBY97deFEbzjBVaPtspz38H8CwVr_NtMIKKMyLVDtB-hLeSFl9V1JWnElZsWMHXJzKcAkk1YqEjBj_A62DQ878FOdY_0brUV1luUeLts93xf_lo6qwxGgy5IK42po4n1ovGovw5XMpWcCkZOzYAQS1OhWr5ffsKUxKL8iQH5xDcMocPppM3z19cOjpC5utU_sHKLTTEmQY7G0B44yzx79Xg0dxT9yvyBNkwh2ZIxsyQ1cY7fBJK8K2CQVUK2y30JnSLnFqzbL5mzacNFxzodD5Njtog1i3qfoDH8K8Ew664aATDrvhsBNme3CyffY3AAD__xPOlyo= query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE 'POINT(3.0 3.0)'::geometry::box2d && geom] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk99v2jAQx9_3V5zuhVZyhe1QaP3EuqYaE4MOkIbUoCojJxYt2JntTEyI_31K0m2lLWnJg6X78bl873zeovuZocJpOAw_zKCwGdxMxp_hLpzfDt8PRnByPZjOpl-Gp_CQ8qNOWJG59_G3jODrx3ASQosLLvjz76LzzG4pdTWey2uICs5ltz7LgusFMtQmoVG8JofqDgUylLhgmFuzJOeMLd3bKmmQbFBxhqnOC1-6FwyXxhKqLfrUZ4QKR-bM5O0AGSbk4zSr0nYMTeH_Q87HK0LV2bFHhUVz4VnZ-oTihGyb75XHf5Pply3dpzqhDTKc5rF2CtpCnMtLKc55p8u7F53uZa979oKzB7FOIOBg_HeyDg-pFseoHuhfZD0lN2nmyZJti33pf-PhJrdgNPSFAlfqBudj61WEURT0zqOISx5FnL92IJBOjqREhPCkd4bjwivoi4NTkMdM4ZNJ9cPVyUNXl9t0HdvfyLCelYLW1Xh-EkDAAghOW-rFHe7LN2gN9rS-ssATcrnRjt60wXy3YEjJiupH4kxhl3RrzbL6TW2OK65yJOR8He3UxkDXoVLgY1g0wrIZlo1w0AwHjTB_Ai927_4EAAD__0vdfDM= +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk99v0zAQx9_5K0730k3yVDsZP-SnMpaJotGOthKV5moK9VEiUjvYDgqq-r-jJAPWjaZrHk66H5_Lfc-6DfofOUqcJtfJuxmULoeryfgj3Cbzm-u3wxGcXA6ns-mn61O4L_neFqzI3oX0S07w-X0ySaDHBRf86ffm_Infk_JiPI8uQZWcR69aWzdcL5ChsZpG6Zo8ylsUyDDCBcPC2SV5b10d3jRFQ12h5AwzU5ShDi8YLq0jlBsMWcgJJY7smS36MTLUFNIsb8q2DG0Z_kE-pCtCeb5lDxqL7sazWvqEUk2uz3fa49_NDGpJd5nRVCHDaZEaL6Gv8EKp6qtWqhJcqYofMnh2LCMUQmo0xBxs-EbO4z7N4hjNQ_OTXCB9leWBHLm-2BX-J59UhQNrYCAk-Fo1-JC6IBsV8euXSvGIK8X5IYNARh-L1eIfqWc4LoOEgdi7h-iYPXywmbl_-mjf0xcuW6fuFzJstyWhdzGen8QQsxji05787w0MomfMGu_MeuAAJuQLazw96wL4dsGQ9IraI_O2dEu6cXbZ_KZ1xw3XBDT50GbPW2do2lQ94ENYdMJRNxx1wnE3HHfC_BG82L74HQAA___QspRl query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE 'LINESTRING(1.0 1.0, 5.0 5.0)'::geometry ~ geom] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk99v2jAQx9_3V5zuhVZyi50EWP3EtIUuE4UuIG1Tg6qMnLpowc5sMzEh9rdPIe0GaKTFUhLdj8_lvmd7jfZHgRIn4TB8O4WlKWAQj2_gLvx8O3wTjeDsXTSZTj4Oz-Ex5Xud8ED63qVfC4JP78M4hNYwGoWTaRyNrs_EJQdxyRl0Lnn1nLekvA7HN-E0_gK_K3QxQ4ZKZzRKF2RR3qFAhh7OGJZGz8labSr3epsUZSuUnGGuyqWr3DOGc20I5Rpd7gpCiSN9ocu2jwwzcmlebNM2DPXS_YOsSx8IZbBhO4VFc-FpJTKmNCPT5nvl8e8M-pWk-1xltEKGkzJVVkJbiI535YkOD7q8-zroXvW6F_9x9iBVGfg90O4bGYvHuhandB2pn2QcZYO8cGTItMV-60_xcFUa0Ar6QoKt-gbrUuNkgkni9zpJwj2eJJw_90IglZ1IiQThQDvD8dJJ6IujU_BOmcIHnavHrfOObV1p8kVqfiHDelYSWlxwj9fr6buzBtwfNNkiCPih3ZJ7N6DvvUCpv6f0meMfky21svSi8883M4aUPVB9xaxemjndGj3f_qY2x1tu68jIujoa1Eak6lDV4C4sGmGvGfYaYb8Z9hthfgDPNq_-BAAA___AfYR8 +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk19v2jAQwN_3Kax7oZVcsBOqSn5if0KXiUIXkLapRlWGbyxasDPbTJkQ--xTknYDJEKxFEdn38_J76zbgPuZg4BpNIrezsja5mSYTO7IQ_T5fvQ6HpOLd_F0Nv04uiRPKT-ahCWaR59-zZF8eh8lEemM4nE0nSXx-PaCdxnhXUbJdZdVz2VHiNtochfNki_kT4Wu5kBBG4XjdIUOxANwoBDAnEJhzQKdM7Za3tRJsSpBMAqZLta-Wp5TWBiLIDbgM58jCBibK1P0QqCg0KdZXqdtKZi1_w85ny4RRH9Ldw7m7QfPKskEU4W2x_aOh381GFRKj5lWWAKFaZFqJ0hPwhspy29KypIzKUt2aoKrcxkugaRakfCGGP8drYNjzvwc51j_QutRDbPco0Xb4_viz_tRWVhiNBlwQVxlTZxPrRe1RXhzLSULmJSMnZqAoFbnYpX8gT2FydoLMuBH6xCcU4cPJtNPVx8cu_rCZqvU_gYKTbUE6TDOAtaM5_fOGLJw2Bbzfp8dxh2x10GD4AWm4Z7pifZJ0BVGO3xR_7DtnAKqJTYt6szaLvDemkX9mSac1Fy9oND5ZrffBLFutqof3IV5Kxy0w0ErHLbDYSvMDuD59tXfAAAA__9Blpyu query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table WHERE geom ~ 'LINESTRING(1.0 1.0, 5.0 5.0)'::geometry::box2d] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUkt9v2jAQx9_3V5zupa3kCdv8Gn5iXYPGxEoHSENqoirDJxYt2JltJibE_vYpCd1aNFjxA-h7d1_nc-fbov-eo8JpNIrezWDtchhMxh_hPprfjd4Ob-HyZjidTT-NrmBf8q0uWJJ9COmXnODz-2gSlXoFv-CCCy55fR7_n5wBbw5OadFq8UN9odT1eC5vEmRorKbbdEUe1T0KTBgWzi7Ie-vK0LYqGOoNKs4wM8U6lOGE4cI6QrXFkIWcUOGsRJ9Qqsk1ODLUFNIsr67901m_7OkhM5o2yHBapMYraAjRlj0p2rzV4Z03rU6v23n9j2AXUqOhJ8GGr-Q8JjuGdh3-EvmQLgmV2LGXUw_ND3KB9CDLAzlyDfEc_TEfbQoH1kBfKPAlN_iQuqBijONmtx3HXPI45vx_Pwhk9JkuESMc9M5wvA4K-uLoFOQ5U_hgM7N_Onns6QqXrVL3ExnWs1LQl-V6Xo_nlwIEa0P76kLtN-sFgM1zACfkC2s8PYM7djPfJQxJL6leYG_XbkF3zi6qz9RyXPmqgCYf6qyoxdDUqRLwqVmcNMvTZnnS3DwwJ7tXvwMAAP__dHFMpA== +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUU1Fv2jAQft-vON0LreQJOxRN8xNjDRoTKx0gDamOqgzfWLRgZ7YzZULst09J6NYiQYsfHH139znfdz5v0f_MUeI8nsTvF1C6HEaz6Se4i5e3k3fjG7i4Hs8X88-TS9iX_GgL1mTvQ_o1J_jyIZ7FNd7AH-hwwSPerofvozXivdEpLK6u-CHuSDmcLqPrBBkaq-km3ZBHeYcCE4aFsyvy3ro6tG0KxrpCyRlmpihDHU4YrqwjlFsMWcgJJS5q6TNKNbkuR4aaQprlzbH_nA1qT_eZ0VQhw3mRGi-hq3CoVPVNK1UJrlTFn9vw9bkcoRBSo-FtBDZ8J-cx2TG0Zfjvx4d0TSjFjr3c89j8IhdIj7I8kCPXFU-NP-TjqnBgDQyEBF-7Bh9SF2TjovemrxSPuFKcP7chkNHn0mrzB-4ZTssgYSCO9iE6pw8fbWb2Vx8du_rCZZvU_UaGbbckDKJ6vIfT5YUAwfrQv-zI_WS-QGDvHIEz8oU1np6IO3Yy3yUMSa-pfQDelm5Ft86umt-0cNrwmoAmH9qsaMHYtKla4GOyOEmOTpOjk-TeATnZvfobAAD__xyQZNY= diff --git a/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_explain_local b/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_explain_local index 47fe7c6a846e..af3bae6872b9 100644 --- a/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_explain_local +++ b/pkg/sql/logictest/testdata/logic_test/inverted_filter_geospatial_explain_local @@ -17,12 +17,12 @@ CREATE TABLE geo_table2( query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k FROM geo_table2 WHERE ST_Intersects('POINT(3.0 3.0)'::geometry, geom)] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk0uP2jwUhvffr7DOBpD8DbbDZcarVG2mTcUABaS2mkQoxUc0arBT21RUiP9ehUzFXBpasoh0Lo993tf2Htz3AiTMo1H0ekG2tiC3s8kduY8-TUev4jFpv4nni_mHUYc8tHyrG9Zolj77UqAgH99Fs4g4v8y1R-tw5V27NZ3E40U7uGIkuGKdlpRvo8ldtJh9phW66aRAQRuF42yDDuQ9cEgplNas0Dljq9T-2BCrHUhGIdfl1lfplMLKWAS5B5_7AkHCohpkhplC22VAQaHP8uK47GnOsNp3mWuFO6AwLzPtJOly3hc3gvdZb8AG173BzXDw_x-SQ5JpRQJGjP-K1gGFydZLEnIaBjQUkB4omK0_jeh8tkaQ_ED_XUasf6D1qG7zwqNF2-VPtfyuR7vSEqNJKCRxlQ7ifGa9TCBJgmE_SZhgScLY335AUKsLKZ4Aafai2QhxiRHvTa4fjlM0Hmdp801mfwKF2i_5_Aoyzjh7-V33XsQt-fh-hqJz0tQoKLhE0AxdabTDJ2KaVmaHlAKqNdaPwJmtXeHUmtVxmzqcHLljQqHzdZXXQazrUjXgY5ifhcV5WJyFg2dwevjvVwAAAP__Hi5XNQ== +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk9uO0zAQhu95Cmtu2kpmaycgkK_CIQtB3ba0lQCtoyrUQ4lI7WA7KKjqu6Mki7oHpbvNhaU5fPb8vyZ7cL8LELCMJ_G7FalsQS4XsytyHX-dT94kUzJ8nyxXy8-TEblp-dU1bNGsffa9wIB8-RgvYuL8OtcercONd8PBfJZMV8PwgpHwgo0GQnyIZ1fxavGNNuhulAIFbRROsx06ENfAIaVQWrNB54xtUvu2IVE1CEYh12Xlm3RKYWMsgtiDz32BIGDVDLLATKEdM6Cg0Gd50V57nDNq3l3nWmENFJZlpp0gYwlvpax_KClrzqSs2WMHPD-X4RJIphUJGTH-J1oHFGaVFyTiNAppFEB6oGAqfxTofLZFEPxAn25Cov-g9agu88KjRTvmd534X4_r0hKjSRQI4hoXiPOZ9aJVFb56KSULmJSMPXYAQa3OxRoz-t3otyI4x4pPJtc36xD0rkNp811m_wKFzjFxf4UZZ5w9_F6_eBAPxO39joLRUVOvoPAcQQt0pdEO74jpu5kdUgqottj9RM5UdoNzazbtM104a7k2odD5rsq7INFdqRnwNsxPwsFpODgJh_fg9PDsXwAAAP__ozFvZw== query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT k, k_plus_one FROM geo_table2 WHERE ST_Intersects('POINT(3.0 3.0)'::geometry, geom)] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk99v2jAQx9_3V1j3UpC8Yjv8aP2UaUu3TBQYIG1TE6EMn1jUYGe2MzEh_vcppFNLO9jIQ6T78bHve3fegvtRgIRZNIzezkllC3IzHd-Su-jLZPgmHpHWu3g2n30atslDyj0l94uyqNzCaGySV2gWPvtWoCCfP0TTiDi_yLVH63DpXetiMo5H81ZwyUhwydoXUr6PxrfRfPqV1ui6nQIFbRSOsjU6kHfAIaVQWrNE54ytXdt9Qqw2IBmFXJeVr90phaWxCHILPvcFgoR5XcgUM4W2w4CCQp_lxf7YxzrD-t5FrhVugMKszLSTpMN5T1wL3mPdPutfdfvXg_7rvzgHJNOKBIwY_x2tAwrjyksSchoGNBSQ7iiYyj-W6Hy2QpB8R_9fRqx_ovWobvLCo0Xb4Yda_sSjTWmJ0SQUkrhaB3E-s14mkCTBoJckTLAkYexfPyCo1ZkUT4Ac78XxRohzGvHR5PphnOLoOEubrzP7Cyg0_ZLPV5BxxtnL76r7wr6QT_czFO2D-R7VFJyjaYquNNrhgZ5jJ7NdSgHVCpt34ExllzixZrm_pjHHe27vUOh8E-WNEesmVBf4FOYnYXEaFifh4Bmc7l79DgAA__8eQVxP +https://cockroachdb.github.io/distsqlplan/decode.html#eJyUk11v0zAUhu_5Fda5aSuZ1U5AIF-FjwyCura0lQDNURXqQ4mW2sF2UFDV_46SDG3dlI72wtL5eOzzvj3Zg_tVgIBlPInfrUhlC3K5mF2R6_jrfPImmZLh-2S5Wn6ejMhtyw0lN-uyqNzaaOyat2jWPvteYEC-fIwXMXF-nWuP1uHGu-FgPkumq2F4wUh4wUYDIT7Es6t4tfhGG3Q3SoGCNgqn2Q4diGvgkFIordmgc8Y2qX3bkKgaBKOQ67LyTTqlsDEWQezB575AELBqBllgptCOGVBQ6LO8aK-9mzNq3l3nWmENFJZlpp0gYwlvpax_KClrzqSs2VMHPD-X4RJIphUJGTH-J1oHFGaVFyTiNAppFEB6oGAqfyfQ-WyLIPiB_r8Jif6N1qO6zAuPFu2YHzvxrx7XpSVGkygQxDUuEOcz60WrKnz1UkoWMCkZe-oAglqdizVm9LvRb0VwjhWfTK5v1yHoXYfS5rvM_gEKnWPi4Qozzjh7_Hv94lE8EPf3OwpGR_9wr6bwHE0LdKXRDo_09N3MDikFVFvsviNnKrvBuTWb9pkunLVcm1DofFflXZDortQMeB_mJ-HgNBychMMHcHp49jcAAP__Crh0gQ== query TTT EXPLAIN SELECT k, k_plus_one FROM geo_table2 WHERE ST_Intersects('POINT(3.0 3.0)'::geometry, geom) diff --git a/pkg/sql/logictest/testdata/logic_test/inverted_index_geospatial b/pkg/sql/logictest/testdata/logic_test/inverted_index_geospatial index b1a3c07862bd..044a97e3d015 100644 --- a/pkg/sql/logictest/testdata/logic_test/inverted_index_geospatial +++ b/pkg/sql/logictest/testdata/logic_test/inverted_index_geospatial @@ -30,7 +30,7 @@ SELECT k FROM geo_table WHERE ST_Intersects('SRID=26918;POINT(400003 4000003)':: query T SELECT url FROM [EXPLAIN ANALYZE SELECT k FROM geo_table WHERE ST_Intersects('SRID=26918;POINT(400003 4000003)'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJykU2FP2zAQ_b5fcbsvUM1T7SQLxdNEC4QtW2lZ2mljtEKhOaGIJO5sdytC_e9TEhhQqVHR-sGVz-9d3runu0PzK0OJo6AfHI1hoTM4iYancBH8OOv3wgH0Br3--c8Ado_D0Xj0td-Ce-hNDbwmdWnjq4zg-6cgCsDYy7SwpA3NrNndGUXh8QfH3xed92fDcDDe9Tjn3IXqj7utHSk_BsPTYByds7JX3oJhdBxEcHgON1NkWKiEBnFOBuUFCpwynGs1I2OULkt3FSBMlig5w7SYL2xZnjKcKU0o79CmNiOUOC5FRhQnpNscGSZk4zSr2v7z0C0VXKZFQktkOJrHhZHQPjg4eNt-PfjW7yNDrf4Y0BQnEnxkaGycZWDTnCRwgwyvbi09ABzhwyFOVwzVwj4qMza-JpRixbZXHxa_SVtKTtLMkibdFs8tPLwHy7kGVUBXSDClfjA21lZOcDJx996Vx942BwIVyT3LnyAgw-HCSuiKLWaQx0vIKVf6FhaGykFw-JJunoTzkkl8VmlxH6OzKca5TvNY3z6KZl1nC91V8LCGqotr2E1O3Jc4GSltSbfd5y664g0yrHOW6-vEBRfl4jjc8f19_vR35PdExxP1pcM7ouN5gSd25NMN6zqt_0tSNCfpvcR_RGauCkPP_G_qzFdThpRcU731Ri30jM60mlWfqa_DilcVEjK2fhX1JSzqp1LgU7JoJDvNZKeR7DaT3Uayt0aerl79DQAA__-mUsar +https://cockroachdb.github.io/distsqlplan/decode.html#eJykVFFP20AMft-vsPwC1W7qXZKVcNOkFghbttKytNLGaIVCY1BEkuvurlsR6n-fktABlRoVrQ-ubH-f48-O84DmV4YSR0E_OB7DQmdwGg3P4DL4cd7vhQPoDXr9i58B7J-Eo_HoW78Fj9C7GnhL6srG1xnB989BFICxV2lhSRuaWbO_N4rCk49O51D4H86H4WC873HOuQvVH3dbe1J-CoZnwTi6YGWtvAXD6CSI4OgC7qbIsFAJDeKcDMpLFDhlONdqRsYoXYYeKkCYLFFyhmkxX9gyPGU4U5pQPqBNbUYocVw2GVGckG5zZJiQjdOsKvtPQ7fs4CotEloiw9E8LoyE9gSPJpPlTVKam10MvltzaILIUKs_BjTFiYQOMjQ2zjKwaU4SuEGG1_eW1gDX9-EIpyuGamGf1Bgb3xJKsWK7Kw6L36QtJadpZkmTbouXstf5YDnXoAroCgmm1AzGxtrKSoN78L40B7sYBCqSNa0zQUCGw4WV0BU7TCGPl5BTrvQ9LAwlEhwOX9Pts3BeM4svKi0el-9sW_5cp3ms75-aZl1nh76r1wU2UHVwA7tNifsaJSOlLem2-1JFV7xFhvWm5eYRcsFFeW4OdzqdQ_78d9zpCd8TteNzX_ieF3hiTz6_y67T-r9NiuZNeq_RH5GZq8LQC_3bKvPVlCElt1R_K4xa6BmdazWrHlO7w4pXBRIyts6K2gmLOlU2-JwsGslOM9lpJLvNZLeR7G2Qp6s3fwMAAP__5HrbcQ== statement ok DROP TABLE geo_table @@ -66,7 +66,7 @@ SELECT k FROM geo_table WHERE ST_Intersects('SRID=26918;POINT(400003 4000003)':: query T SELECT url FROM [EXPLAIN ANALYZE SELECT k FROM geo_table WHERE ST_Intersects('SRID=26918;POINT(400003 4000003)'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJykVNFO20oQfb9fMZoXiO5eZXedm5itKiWAad2GhDqRWkoiZOIRtbC96e6mDUL598o2FAhKSlQ_rDUz54znzM74Du33DBWOgn5wNIaFyeAkGp7CRfDlrN8LB9Ab9PrnXwPYPw5H49GnfgPuoTc18Jr0pYuvMoLP74MoAOsu08KRsTRzdn9vFIXHb2X7QPhvzobhYLzf4pxzD6oX9xp7Sr0LhqfBODpnZa68AcPoOIjg8Bxupsiw0AkN4pwsqgsUOGU4N3pG1mpTuu4qQJgsUXGGaTFfuNI9ZTjThlDdoUtdRqhwXBYZUZyQaXJkmJCL06xK-1tDt6zgMi0SWiLD0TwurIKmFG3RkR1fCuF1fM_n_n8vfQcQFwl4HLT7RsYiQ6N_WjAUJwokMrQuzjJwaU4KeBm_unX0AOhIOMTpiqFeuEcJ1sXXhEqs2OtlhsUPMo6SkzRzZMg0xXOtD_FgOTegC-gKBbYUCtbFxqkJTiZe5__JhHOvPPgfDgQqkh1ZYoLwol3DhVPQFa9oXB4vIadcm1tYWCpRHD6mm_snd-nfB50W91MiN03J3KR5bG4fi2Zd-Yq6q7mCNVTtXMNuUuLtomSkjSPT9J6r6Ip_kWE9HWp9W7ngotxLyWW7fcCfPkftnvBbojZ87gu_1QpaYk89XeCubPzdTYrtN9naRX9Edq4LS8_0b8rMV1OGlFxT_VOxemFmdGb0rPpMbQ4rXuVIyLo6KmojLOpQWeBTsthKltvJcivZ2072tpJba-Tp6p9fAQAA__8xdd55 +https://cockroachdb.github.io/distsqlplan/decode.html#eJykVGFP2zoU_f5-xdX9AtXzU-0kr6_4aVILhK1baVlaaWOkQqG5YxFJ3NnuVoT63ycnMKBTM6r1gyvfe87NPcfXvkPzNUeJk3AYHk1hqXM4icancBF-PBv2ByPoj_rD808h7B8PJtPJ-2EL7qE3NfCa1KVNrnKCD2_CKARjL7PSkjY0t2Z_bxINjl95nQPR_f9sPBhN9wPOOfeh-uN-a0_K1-H4NJxG58zVKlowjo7DCA7P4WaGDEuV0igpyKC8QIEzhgut5mSM0i50VwEG6QolZ5iVi6V14RnDudKE8g5tZnNCiVPXZERJSrrNkWFKNsnyquxPDT3XwWVWprRChpNFUhoJ7RgP43j1OY3jFffdwn-z4D-7ckSMkJQp-ByU_ULaIEOtvhvQlKQSPGRobJLnYLOCJHCXv7q19AAQXgCHOFszVEv76IGxyTWhFGv2cp8G5TfSltKTLLekSbfFc7Me8uFqoUGV0BMSjHMKjE20lZVy_79_45g75dwJbFwQqEx3pTnDfnFsvLQSeuIF3hXJCgoqlL6FpSGH4vAu2-6gt4uDb1VW3g-at23QFjorEn372DTreS_ouxpN2EDVwQ3sNiX-LkomSlvSbf-5ip74GxnW8yE3LzwXXLir7XGv0zngT39Hnb7oBqLedHlXdIMgDMSefPoG9LzWn52kaD7JYBf9EZmFKg0907-tMl_PGFJ6TfW7ZNRSz-lMq3n1mXo7rnhVICVj66yoN4OyTrkGn5JFI9lrJnuNZL-Z7DeSgw3ybP3XjwAAAP__-mH3Sw== # Also works when creating an index. statement ok @@ -78,4 +78,4 @@ CREATE INVERTED INDEX geom_index ON geo_table(geom) query T SELECT url FROM [EXPLAIN ANALYZE SELECT k FROM geo_table WHERE ST_Intersects('SRID=26918;POINT(400003 4000003)'::geometry, geom) ORDER BY k] ---- -https://cockroachdb.github.io/distsqlplan/decode.html#eJykVNFO20oQfb9fMZoXiO5eZXedm5itKiWAad2GhDqRWkoiZOIRtbC96e6mDUL598o2FAhKSlQ_rDUz54znzM74Du33DBWOgn5wNIaFyeAkGp7CRfDlrN8LB9Ab9PrnXwPYPw5H49GnfgPuoTc18Jr0pYuvMoLP74MoAOsu08KRsTRzdn9vFIXHb2X7QPhvzobhYLzf4pxzD6oX9xp7Sr0LhqfBODpnZa68AcPoOIjg8Bxupsiw0AkN4pwsqgsUOGU4N3pG1mpTuu4qQJgsUXGGaTFfuNI9ZTjThlDdoUtdRqhwXBYZUZyQaXJkmJCL06xK-1tDt6zgMi0SWiLD0TwurIKmFG3RkR1fCuF1fM_n_n8vfQcQFwl4HLT7RsYiQ6N_WjAUJwokMrQuzjJwaU4KeBm_unX0AOhIOMTpiqFeuEcJ1sXXhEqs2OtlhsUPMo6SkzRzZMg0xXOtD_FgOTegC-gKBbYUCtbFxqkJTiZe5__JhHOvPPgfDgQqkh1ZYoLwol3DhVPQFa9oXB4vIadcm1tYWCpRHD6mm_snd-nfB50W91MiN03J3KR5bG4fi2Zd-Yq6q7mCNVTtXMNuUuLtomSkjSPT9J6r6Ip_kWE9HWp9W7ngotxLyWW7fcCfPkftnvBbojZ87gu_1QpaYk89XeCubPzdTYrtN9naRX9Edq4LS8_0b8rMV1OGlFxT_VOxemFmdGb0rPpMbQ4rXuVIyLo6KmojLOpQWeBTsthKltvJcivZ2072tpJba-Tp6p9fAQAA__8xdd55 +https://cockroachdb.github.io/distsqlplan/decode.html#eJykVGFP2zoU_f5-xdX9AtXzU-0kr6_4aVILhK1baVlaaWOkQqG5YxFJ3NnuVoT63ycnMKBTM6r1gyvfe87NPcfXvkPzNUeJk3AYHk1hqXM4icancBF-PBv2ByPoj_rD808h7B8PJtPJ-2EL7qE3NfCa1KVNrnKCD2_CKARjL7PSkjY0t2Z_bxINjl95nQPR_f9sPBhN9wPOOfeh-uN-a0_K1-H4NJxG58zVKlowjo7DCA7P4WaGDEuV0igpyKC8QIEzhgut5mSM0i50VwEG6QolZ5iVi6V14RnDudKE8g5tZnNCiVPXZERJSrrNkWFKNsnyquxPDT3XwWVWprRChpNFUhoJ7RgP43j1OY3jFffdwn-z4D-7ckSMkJQp-ByU_ULaIEOtvhvQlKQSPGRobJLnYLOCJHCXv7q19AAQXgCHOFszVEv76IGxyTWhFGv2cp8G5TfSltKTLLekSbfFc7Me8uFqoUGV0BMSjHMKjE20lZVy_79_45g75dwJbFwQqEx3pTnDfnFsvLQSeuIF3hXJCgoqlL6FpSGH4vAu2-6gt4uDb1VW3g-at23QFjorEn372DTreS_ouxpN2EDVwQ3sNiX-LkomSlvSbf-5ip74GxnW8yE3LzwXXLir7XGv0zngT39Hnb7oBqLedHlXdIMgDMSefPoG9LzWn52kaD7JYBf9EZmFKg0907-tMl_PGFJ6TfW7ZNRSz-lMq3n1mXo7rnhVICVj66yoN4OyTrkGn5JFI9lrJnuNZL-Z7DeSgw3ybP3XjwAAAP__-mH3Sw== diff --git a/pkg/sql/opt/exec/execbuilder/testdata/geospatial b/pkg/sql/opt/exec/execbuilder/testdata/geospatial index 9e0cc0272404..92aa6de4d08e 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/geospatial +++ b/pkg/sql/opt/exec/execbuilder/testdata/geospatial @@ -35,13 +35,13 @@ INSERT INTO c VALUES (2, 'LINESTRING(1.0 1.0, 2.0 2.0)', 'POINT(1.0 1.0)') ---- CPut /Table/54/1/1/0 -> /TUPLE/ -InitPut /Table/54/2/1153277837650709461/1/0 -> /BYTES/ -InitPut /Table/54/3/1152921526351450449/1/0 -> /BYTES/ +InitPut /Table/54/2/"B\xfd\x10\x01D\x15@\x80K\xd5\x01?\x91\xdfF\xa2R\x9d9?\x91\xdfF\xa2R\x9d9\x89\x88" -> /BYTES/ +InitPut /Table/54/3/"B\xfd\x10\x00\x00\x05\x10\x14QQ\x01@\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x89\x88" -> /BYTES/ CPut /Table/54/1/2/0 -> /TUPLE/ -InitPut /Table/54/2/1153290940513779712/2/0 -> /BYTES/ -InitPut /Table/54/2/1154047404446580736/2/0 -> /BYTES/ -InitPut /Table/54/2/1154328879490400256/2/0 -> /BYTES/ -InitPut /Table/54/3/1152921510042997845/2/0 -> /BYTES/ +InitPut /Table/54/2/"B\xfd\x10\x01P\x00\x00\x00\x00\x00\x00?\x91\xdfF\xa2R\x9d8?\x91\xdfF\xa2R\x9c\xb9?\xa1\xdfF\xa2R\x9d9?\xa1\xdfF\xa2R\x9dx\x8a\x88" -> /BYTES/ +InitPut /Table/54/2/"B\xfd\x10\x03\xff\xff\xfc\x00\x00\x00\x00?\x91\xdfF\xa2R\x9d8?\x91\xdfF\xa2R\x9c\xb9?\xa1\xdfF\xa2R\x9d9?\xa1\xdfF\xa2R\x9dx\x8a\x88" -> /BYTES/ +InitPut /Table/54/2/"B\xfd\x10\x05\x00\x00\x00\x00\x00\x00\x00?\x91\xdfF\xa2R\x9d8?\x91\xdfF\xa2R\x9c\xb9?\xa1\xdfF\xa2R\x9d9?\xa1\xdfF\xa2R\x9dx\x8a\x88" -> /BYTES/ +InitPut /Table/54/3/"B\xfd\x10\x00\x00\x01D\x05\x14U\x01?\xf0\x00\x00\x00\x00\x00\x00?\xf0\x00\x00\x00\x00\x00\x00\x8a\x88" -> /BYTES/ statement ok CREATE INVERTED INDEX geog_idx ON b(geog) @@ -55,13 +55,13 @@ INSERT INTO b VALUES (4, 'LINESTRING(1.0 1.0, 2.0 2.0)', 'POINT(1.0 1.0)') ---- CPut /Table/53/1/3/0 -> /TUPLE/ -InitPut /Table/53/2/1153277837650709461/3/0 -> /BYTES/ -InitPut /Table/53/3/1152921526351450449/3/0 -> /BYTES/ +InitPut /Table/53/2/"B\xfd\x10\x01D\x15@\x80K\xd5\x01?\x91\xdfF\xa2R\x9d9?\x91\xdfF\xa2R\x9d9\x8b\x88" -> /BYTES/ +InitPut /Table/53/3/"B\xfd\x10\x00\x00\x05\x10\x14QQ\x01@\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x8b\x88" -> /BYTES/ CPut /Table/53/1/4/0 -> /TUPLE/ -InitPut /Table/53/2/1153290940513779712/4/0 -> /BYTES/ -InitPut /Table/53/2/1154047404446580736/4/0 -> /BYTES/ -InitPut /Table/53/2/1154328879490400256/4/0 -> /BYTES/ -InitPut /Table/53/3/1152921510042997845/4/0 -> /BYTES/ +InitPut /Table/53/2/"B\xfd\x10\x01P\x00\x00\x00\x00\x00\x00?\x91\xdfF\xa2R\x9d8?\x91\xdfF\xa2R\x9c\xb9?\xa1\xdfF\xa2R\x9d9?\xa1\xdfF\xa2R\x9dx\x8c\x88" -> /BYTES/ +InitPut /Table/53/2/"B\xfd\x10\x03\xff\xff\xfc\x00\x00\x00\x00?\x91\xdfF\xa2R\x9d8?\x91\xdfF\xa2R\x9c\xb9?\xa1\xdfF\xa2R\x9d9?\xa1\xdfF\xa2R\x9dx\x8c\x88" -> /BYTES/ +InitPut /Table/53/2/"B\xfd\x10\x05\x00\x00\x00\x00\x00\x00\x00?\x91\xdfF\xa2R\x9d8?\x91\xdfF\xa2R\x9c\xb9?\xa1\xdfF\xa2R\x9d9?\xa1\xdfF\xa2R\x9dx\x8c\x88" -> /BYTES/ +InitPut /Table/53/3/"B\xfd\x10\x00\x00\x01D\x05\x14U\x01?\xf0\x00\x00\x00\x00\x00\x00?\xf0\x00\x00\x00\x00\x00\x00\x8c\x88" -> /BYTES/ statement ok CREATE TABLE ltable( diff --git a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index index a16832a51540..0b3583510177 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index +++ b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index @@ -641,26 +641,26 @@ CREATE TABLE geo_table( query TTTTT EXPLAIN (VERBOSE) SELECT k FROM geo_table WHERE ST_Intersects('POINT(3.0 3.0)'::geometry, geom) ---- -· distribution local · · -· vectorized false · · -project · · (k) · - │ estimated row count 333 (missing stats) · · - └── filter · · (k, geom) · - │ estimated row count 333 (missing stats) · · - │ filter st_intersects('010100000000000000000008400000000000000840', geom) · · - └── index join · · (k, geom) · - │ estimated row count 111 (missing stats) · · - │ table geo_table@primary · · - │ key columns k · · - └── project · · (k) · - │ estimated row count 111 (missing stats) · · - └── inverted filter · · (k, geom_inverted_key) · - │ inverted column geom_inverted_key · · - │ num spans 31 · · - └── scan · · (k, geom_inverted_key) · -· estimated row count 111 (missing stats) · · -· table geo_table@geom_index · · -· spans /1152921504606846976-/1152921504606846977 /1152921573326323712-/1152921573326323713 /1152921574400065536-/1152921574400065537 /1152921574668500992-/1152921574668500993 /1152921574735609856-/1152921574735609857 /1152921574739804160-/1152921574739804161 /1152921574740066304-/1152921574740066305 /1152921574740070400-/1152921574740070401 /1152921574740070464-/1152921574740070465 /1152921574740070468-/1152921574740070469 /1152921574740070469-/1152921574740070470 /1152921574740070480-/1152921574740070481 /1152921574740070656-/1152921574740070657 /1152921574740071424-/1152921574740071425 /1152921574740082688-/1152921574740082689 /1152921574740131840-/1152921574740131841 /1152921574740852736-/1152921574740852737 /1152921574752387072-/1152921574752387073 /1152921577621291008-/1152921577621291009 /1152921590506192896-/1152921590506192897 /1152921779484753920-/1152921779484753921 /1152922604118474752-/1152922604118474753 /1152925902653358080-/1152925902653358081 /1152939096792891392-/1152939096792891393 /1152991873351024640-/1152991873351024641 /1153202979583557632-/1153202979583557633 /1154047404513689600-/1154047404513689601 /1157425104234217472-/1157425104234217473 /1170935903116328960-/1170935903116328961 /1224979098644774912-/1224979098644774913 /1441151880758558720-/1441151880758558721 · · +· distribution local · · +· vectorized false · · +project · · (k) · + │ estimated row count 333 (missing stats) · · + └── filter · · (k, geom) · + │ estimated row count 333 (missing stats) · · + │ filter st_intersects('010100000000000000000008400000000000000840', geom) · · + └── index join · · (k, geom) · + │ estimated row count 111 (missing stats) · · + │ table geo_table@primary · · + │ key columns k · · + └── project · · (k) · + │ estimated row count 111 (missing stats) · · + └── inverted filter · · (k, geom_inverted_key) · + │ inverted column geom_inverted_key · · + │ num spans 31 · · + └── scan · · (k, geom_inverted_key) · +· estimated row count 111 (missing stats) · · +· table geo_table@geom_index · · +· spans /"B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"-/"B\xfd\x10\x00\x00\x00\x00\x00\x00\x01" /"B\xfd\x10\x00\x00\x10\x00\x00\x00\x00"-/"B\xfd\x10\x00\x00\x10\x00\x00\x00\x01" /"B\xfd\x10\x00\x00\x10@\x00\x00\x00"-/"B\xfd\x10\x00\x00\x10@\x00\x00\x01" /"B\xfd\x10\x00\x00\x10P\x00\x00\x00"-/"B\xfd\x10\x00\x00\x10P\x00\x00\x01" /"B\xfd\x10\x00\x00\x10T\x00\x00\x00"-/"B\xfd\x10\x00\x00\x10T\x00\x00\x01" /"B\xfd\x10\x00\x00\x10T@\x00\x00"-/"B\xfd\x10\x00\x00\x10T@\x00\x01" /"B\xfd\x10\x00\x00\x10TD\x00\x00"-/"B\xfd\x10\x00\x00\x10TD\x00\x01" /"B\xfd\x10\x00\x00\x10TD\x10\x00"-/"B\xfd\x10\x00\x00\x10TD\x10\x01" /"B\xfd\x10\x00\x00\x10TD\x10@"-/"B\xfd\x10\x00\x00\x10TD\x10A" /"B\xfd\x10\x00\x00\x10TD\x10D"-/"B\xfd\x10\x00\x00\x10TD\x10E" /"B\xfd\x10\x00\x00\x10TD\x10E"-/"B\xfd\x10\x00\x00\x10TD\x10F" /"B\xfd\x10\x00\x00\x10TD\x10P"-/"B\xfd\x10\x00\x00\x10TD\x10Q" /"B\xfd\x10\x00\x00\x10TD\x11\x00"-/"B\xfd\x10\x00\x00\x10TD\x11\x01" /"B\xfd\x10\x00\x00\x10TD\x14\x00"-/"B\xfd\x10\x00\x00\x10TD\x14\x01" /"B\xfd\x10\x00\x00\x10TD@\x00"-/"B\xfd\x10\x00\x00\x10TD@\x01" /"B\xfd\x10\x00\x00\x10TE\x00\x00"-/"B\xfd\x10\x00\x00\x10TE\x00\x01" /"B\xfd\x10\x00\x00\x10TP\x00\x00"-/"B\xfd\x10\x00\x00\x10TP\x00\x01" /"B\xfd\x10\x00\x00\x10U\x00\x00\x00"-/"B\xfd\x10\x00\x00\x10U\x00\x00\x01" /"B\xfd\x10\x00\x00\x11\x00\x00\x00\x00"-/"B\xfd\x10\x00\x00\x11\x00\x00\x00\x01" /"B\xfd\x10\x00\x00\x14\x00\x00\x00\x00"-/"B\xfd\x10\x00\x00\x14\x00\x00\x00\x01" /"B\xfd\x10\x00\x00@\x00\x00\x00\x00"-/"B\xfd\x10\x00\x00@\x00\x00\x00\x01" /"B\xfd\x10\x00\x01\x00\x00\x00\x00\x00"-/"B\xfd\x10\x00\x01\x00\x00\x00\x00\x01" /"B\xfd\x10\x00\x04\x00\x00\x00\x00\x00"-/"B\xfd\x10\x00\x04\x00\x00\x00\x00\x01" /"B\xfd\x10\x00\x10\x00\x00\x00\x00\x00"-/"B\xfd\x10\x00\x10\x00\x00\x00\x00\x01" /"B\xfd\x10\x00@\x00\x00\x00\x00\x00"-/"B\xfd\x10\x00@\x00\x00\x00\x00\x01" /"B\xfd\x10\x01\x00\x00\x00\x00\x00\x00"-/"B\xfd\x10\x01\x00\x00\x00\x00\x00\x01" /"B\xfd\x10\x04\x00\x00\x00\x00\x00\x00"-/"B\xfd\x10\x04\x00\x00\x00\x00\x00\x01" /"B\xfd\x10\x10\x00\x00\x00\x00\x00\x00"-/"B\xfd\x10\x10\x00\x00\x00\x00\x00\x01" /"B\xfd\x10@\x00\x00\x00\x00\x00\x00"-/"B\xfd\x10@\x00\x00\x00\x00\x00\x01" /"B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"-/"B\xfd\x11\x00\x00\x00\x00\x00\x00\x01" /"B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"-/"B\xfd\x14\x00\x00\x00\x00\x00\x00\x01" · · statement ok CREATE TABLE geo_table2( diff --git a/pkg/sql/opt/invertedexpr/expression.go b/pkg/sql/opt/invertedexpr/expression.go index 50494b123e4a..c3c538e962be 100644 --- a/pkg/sql/opt/invertedexpr/expression.go +++ b/pkg/sql/opt/invertedexpr/expression.go @@ -27,9 +27,46 @@ import ( // SpanExpressionProto.SpansToRead to determine which spans to read from the // inverted index. Then it computes a set expression on the scanned rows as // defined by the SpanExpressionProto.Node. +// For a subset of expressions, it can additionally perform pre-filtering, +// which is filtering a row before evaluating the set expression (but after +// the row is retrieved). This pre-filtering looks at additional state encoded +// in the inverted column. type DatumsToInvertedExpr interface { - // Convert uses the lookup column to construct an inverted expression. - Convert(context.Context, rowenc.EncDatumRow) (*SpanExpressionProto, error) + // CanPreFilter returns true iff this DatumsToInvertedExpr can pre-filter. + CanPreFilter() bool + + // Convert uses the lookup column to construct an inverted expression. When + // CanPreFilter() is true, and the returned *SpanExpressionProto is non-nil, + // the interface{} returned represents an opaque pre-filtering state for + // this lookup column. + Convert(context.Context, rowenc.EncDatumRow) (*SpanExpressionProto, interface{}, error) + + // PreFilter is used for pre-filtering a looked up row whose inverted column is + // represented as enc. The caller has determined the candidate + // expressions for whom this row is relevant, and preFilters contains the + // pre-filtering state for those expressions. The result slice must be the + // same length as preFilters and will contain true when the row is relevant + // and false otherwise. It returns true iff there is at least one result + // index that has a true value. PreFilter must only be called when CanPreFilter() + // is true. This batching of pre-filter application for a looked up row allows + // enc to be decoded once. + // + // For example, when doing an invertedJoin for a geospatial column, say the + // left side has a batch of 5 rows. Each of these 5 rows will be used in + // Convert calls above, which will each return an interface{}. The 5 + // interface{} objects contain state for each of these 5 rows that will be + // used in pre-filtering. Specifically, for geospatial, this state includes + // the bounding box of the shape. When an inverted index row is looked up + // and is known to be relevant to left rows 0, 2, 4 (based on the spans in + // the SpanExpressionProtos), then PreFilter will be called with + // len(preFilters) == 3, containing the three interface{} objects returned + // previously for 0, 2, 4. The results will indicate which span expressions + // should actually use this inverted index row. More concretely, the span + // expressions for ST_Intersects work on cell coverings which are different + // from the bounding box, and the pre-filtering works on the bounding box. + // The bounding box pre-filtering complements the span expressions, by + // eliminating some false positives caused by cell coverings. + PreFilter(enc EncInvertedVal, preFilters []interface{}, result []bool) (bool, error) } // EncInvertedVal is the encoded form of a value in the inverted column. diff --git a/pkg/sql/opt/invertedexpr/geo_expression.go b/pkg/sql/opt/invertedexpr/geo_expression.go index d995dc72d13c..aa0e77b77378 100644 --- a/pkg/sql/opt/invertedexpr/geo_expression.go +++ b/pkg/sql/opt/invertedexpr/geo_expression.go @@ -42,6 +42,7 @@ func geoKeyToEncInvertedVal(k geoindex.Key, end bool, b []byte) (EncInvertedVal, } } prev := len(b) + b = encoding.EncodeGeoInvertedAscending(b) b = encoding.EncodeUvarintAscending(b, uint64(k)) // Set capacity so that the caller appending does not corrupt later keys. enc := b[prev:len(b):len(b)] @@ -63,8 +64,9 @@ func GeoUnionKeySpansToSpanExpr(ukSpans geoindex.UnionKeySpans) *SpanExpression if len(ukSpans) == 0 { return nil } - // Avoid per-span heap allocations. - b := make([]byte, 0, len(ukSpans)*(2*encoding.MaxVarintLen)) + // Avoid per-span heap allocations. Each of the 2 keys in a span is the + // geoInvertedIndexMarker (1 byte) followed by a varint. + b := make([]byte, 0, len(ukSpans)*(2*encoding.MaxVarintLen+2)) spans := make([]InvertedSpan, len(ukSpans)) for i, ukSpan := range ukSpans { spans[i], b = geoToSpan(ukSpan, b) diff --git a/pkg/sql/opt/invertedexpr/geo_expression_test.go b/pkg/sql/opt/invertedexpr/geo_expression_test.go index c405ab678502..2347ac479a82 100644 --- a/pkg/sql/opt/invertedexpr/geo_expression_test.go +++ b/pkg/sql/opt/invertedexpr/geo_expression_test.go @@ -28,13 +28,13 @@ func TestUnionKeySpansToSpanExpr(t *testing.T) { cases := []testCase{ { uks: []geoindex.KeySpan{{Start: 5, End: 5}, {Start: 10, End: 10}, {Start: 1, End: 3}}, - expected: "spans_to_read: " + - "spans_to_read: " + - "spans_to_read: " + + expected: "spans_to_read: " + + "spans_to_read: " + + "spans_to_read: " + "node:<" + - "factored_union_spans: " + - "factored_union_spans: " + - "factored_union_spans: > ", + "factored_union_spans: " + + "factored_union_spans: " + + "factored_union_spans: > ", }, { uks: nil, @@ -62,27 +62,27 @@ func TestRPKeyExprToSpanExpr(t *testing.T) { { // Union of two keys. rpx: []geoindex.RPExprElement{geoindex.Key(5), geoindex.Key(10), geoindex.RPSetUnion}, - expected: "spans_to_read: " + - "spans_to_read: " + + expected: "spans_to_read: " + + "spans_to_read: " + "node:<" + - "factored_union_spans: " + - "factored_union_spans: > ", + "factored_union_spans: " + + "factored_union_spans: > ", }, { // Intersection of two keys. rpx: []geoindex.RPExprElement{geoindex.Key(5), geoindex.Key(10), geoindex.RPSetIntersection}, - expected: "spans_to_read: " + - "spans_to_read: " + + expected: "spans_to_read: " + + "spans_to_read: " + "node:<" + "operator:SetIntersection " + - "left: > " + - "right: > > ", + "left: > " + + "right: > > ", }, { // Single key. rpx: []geoindex.RPExprElement{geoindex.Key(5)}, - expected: "spans_to_read: " + - "node: > ", + expected: "spans_to_read: " + + "node: > ", }, { // Malformed. @@ -102,23 +102,23 @@ func TestRPKeyExprToSpanExpr(t *testing.T) { geoindex.Key(7), geoindex.RPSetIntersection, }, - expected: "spans_to_read: " + - "spans_to_read: " + - "spans_to_read: " + - "spans_to_read: " + - "spans_to_read: " + - "spans_to_read: " + - "spans_to_read: " + + expected: "spans_to_read: " + + "spans_to_read: " + + "spans_to_read: " + + "spans_to_read: " + + "spans_to_read: " + + "spans_to_read: " + + "spans_to_read: " + "node: > " + + "left: > " + "right:<" + - "factored_union_spans: " + - "factored_union_spans: " + - "factored_union_spans: " + - "factored_union_spans: " + + "factored_union_spans: " + + "factored_union_spans: " + + "factored_union_spans: " + + "factored_union_spans: " + "operator:SetIntersection " + - "left: > " + - "right: > > > ", + "left: > " + + "right: > > > ", }, } for _, c := range cases { diff --git a/pkg/sql/opt/invertedidx/geo.go b/pkg/sql/opt/invertedidx/geo.go index 0d492552b775..28a793ac3704 100644 --- a/pkg/sql/opt/invertedidx/geo.go +++ b/pkg/sql/opt/invertedidx/geo.go @@ -14,8 +14,11 @@ import ( "context" "fmt" + "github.com/cockroachdb/cockroach/pkg/geo" "github.com/cockroachdb/cockroach/pkg/geo/geogfn" "github.com/cockroachdb/cockroach/pkg/geo/geoindex" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" + "github.com/cockroachdb/cockroach/pkg/geo/geoprojbase" "github.com/cockroachdb/cockroach/pkg/sql/opt" "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" "github.com/cockroachdb/cockroach/pkg/sql/opt/invertedexpr" @@ -25,7 +28,11 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/errors" + "github.com/golang/geo/r1" + "github.com/golang/geo/s1" + "github.com/golang/geo/s2" ) // This file contains functions for building geospatial inverted index scans @@ -641,6 +648,171 @@ type geoInvertedExpr struct { var _ tree.TypedExpr = &geoInvertedExpr{} +// State for pre-filtering, returned by PreFilterer.Bind. +type filterState struct { + geopb.BoundingBox + srid geopb.SRID +} + +// The pre-filtering interface{} returned by Convert refers to *filterState +// which are backed by batch allocated []filterState to reduce the number of +// heap allocations. +const preFilterAllocBatch = 100 + +// PreFilterer captures the pre-filtering state for a function whose +// non-indexed parameter (the lookup column for an inverted join) has not been +// bound to a value. The bound value is captured in the interface{} returned +// by Bind, to allow the caller to hold onto that state for a batch of lookup +// columns. +// +// TODO(sumeer): +// - extend PreFilterer to more general expressions. +// - use PreFilterer for invertedFilterer (where it will be bound once). +type PreFilterer struct { + // The type of the lookup column. + typ *types.T + // The relationship represented by the function. + preFilterRelationship geoindex.RelationshipType + additionalPreFilterParams []tree.Datum + // Batch allocated for reducing heap allocations. + preFilterState []filterState +} + +// NewPreFilterer constructs a PreFilterer +func NewPreFilterer( + typ *types.T, preFilterRelationship geoindex.RelationshipType, additionalParams []tree.Datum, +) *PreFilterer { + return &PreFilterer{ + typ: typ, + preFilterRelationship: preFilterRelationship, + additionalPreFilterParams: additionalParams, + } +} + +// Bind binds the datum and returns the pre-filter state. +func (p *PreFilterer) Bind(d tree.Datum) interface{} { + bbox := geopb.BoundingBox{} + var srid geopb.SRID + // Earlier type-checking ensures we only see these two types. + switch g := d.(type) { + case *tree.DGeometry: + bboxRef := g.BoundingBoxRef() + if bboxRef != nil { + bbox = *bboxRef + } + srid = g.SRID() + case *tree.DGeography: + rect := g.BoundingRect() + bbox = geopb.BoundingBox{ + LoX: rect.Lng.Lo, + HiX: rect.Lng.Hi, + LoY: rect.Lat.Lo, + HiY: rect.Lat.Hi, + } + srid = g.SRID() + default: + panic(errors.AssertionFailedf("datum of unhandled type: %s", d)) + } + if len(p.preFilterState) == 0 { + p.preFilterState = make([]filterState, preFilterAllocBatch) + } + p.preFilterState[0] = filterState{BoundingBox: bbox, srid: srid} + rv := &p.preFilterState[0] + p.preFilterState = p.preFilterState[1:] + return rv +} + +// PreFilter pre-filters a retrieved inverted value against a set of +// pre-filter states. The function signature matches the PreFilter function of +// the DatumsToInvertedExpr interface (PreFilterer does not implment the full +// interface): the result slice indicates which pre-filters matched and the +// single bool in the return value is true iff there is at least one result +// index with a true value. +func (p *PreFilterer) PreFilter( + enc invertedexpr.EncInvertedVal, preFilters []interface{}, result []bool, +) (bool, error) { + loX, loY, hiX, hiY, _, err := encoding.DecodeGeoInvertedKey(enc) + if err != nil { + return false, err + } + switch p.typ { + case types.Geometry: + var bb geo.CartesianBoundingBox + bb.LoX, bb.LoY, bb.HiX, bb.HiY = loX, loY, hiX, hiY + switch p.preFilterRelationship { + case geoindex.DWithin, geoindex.DFullyWithin: + distance := float64(tree.MustBeDFloat(p.additionalPreFilterParams[0])) + bb.LoX -= distance + bb.LoY -= distance + bb.HiX += distance + bb.HiY += distance + } + rv := false + for i := range preFilters { + pbb := geo.CartesianBoundingBox{BoundingBox: preFilters[i].(*filterState).BoundingBox} + switch p.preFilterRelationship { + case geoindex.Intersects, geoindex.DWithin: + result[i] = bb.Intersects(&pbb) + case geoindex.Covers: + result[i] = pbb.Covers(&bb) + case geoindex.CoveredBy, geoindex.DFullyWithin: + result[i] = bb.Covers(&pbb) + default: + return false, errors.Errorf("unhandled relationship %s", p.preFilterRelationship) + } + if result[i] { + rv = true + } + } + return rv, nil + case types.Geography: + bb := s2.Rect{ + Lat: r1.Interval{Lo: loY, Hi: hiY}, + Lng: s1.Interval{Lo: loX, Hi: hiX}, + } + rv := false + for i := range preFilters { + fs := preFilters[i].(*filterState) + pbb := s2.Rect{ + Lat: r1.Interval{Lo: fs.LoY, Hi: fs.HiY}, + Lng: s1.Interval{Lo: fs.LoX, Hi: fs.HiX}, + } + switch p.preFilterRelationship { + case geoindex.Intersects: + result[i] = pbb.Intersects(bb) + case geoindex.Covers: + result[i] = pbb.Contains(bb) + case geoindex.CoveredBy: + result[i] = bb.Contains(pbb) + case geoindex.DWithin: + distance := float64(tree.MustBeDFloat(p.additionalPreFilterParams[0])) + useSphereOrSpheroid := geogfn.UseSpheroid + if len(p.additionalPreFilterParams) == 2 { + useSphereOrSpheroid = + geogfn.UseSphereOrSpheroid(tree.MustBeDBool(p.additionalPreFilterParams[1])) + } + // TODO(sumeer): refactor to share code with geogfn.DWithin. + proj, ok := geoprojbase.Projection(fs.srid) + if !ok { + return false, errors.Errorf("cannot compute DWithin on unknown SRID %d", fs.srid) + } + angleToExpand := s1.Angle(distance / proj.Spheroid.SphereRadius) + if useSphereOrSpheroid == geogfn.UseSpheroid { + angleToExpand *= (1 + geogfn.SpheroidErrorFraction) + } + result[i] = pbb.CapBound().Expanded(angleToExpand).Intersects(bb.CapBound()) + default: + return false, errors.Errorf("unhandled relationship %s", p.preFilterRelationship) + } + if result[i] { + rv = true + } + } + return rv, nil + } + panic(errors.AssertionFailedf("unhandled type %s", p.typ)) +} + // geoDatumsToInvertedExpr implements invertedexpr.DatumsToInvertedExpr for // geospatial columns. type geoDatumsToInvertedExpr struct { @@ -651,6 +823,9 @@ type geoDatumsToInvertedExpr struct { typ *types.T getSpanExpr getSpanExprForGeoIndexFn + // Non-nil only when it can pre-filter. + filterer *PreFilterer + row rowenc.EncDatumRow alloc rowenc.DatumAlloc } @@ -712,6 +887,9 @@ func NewGeoDatumsToInvertedExpr( // computing and caching the SpanExpressions for any functions that have a // constant as the non-indexed argument. var getInvertedExpr func(expr tree.TypedExpr) (tree.TypedExpr, error) + funcExprCount := 0 + var preFilterRelationship geoindex.RelationshipType + var additionalPreFilterParams []tree.Datum getInvertedExpr = func(expr tree.TypedExpr) (tree.TypedExpr, error) { switch t := expr.(type) { case *tree.AndExpr: @@ -737,6 +915,7 @@ func NewGeoDatumsToInvertedExpr( return tree.NewTypedOrExpr(leftExpr, rightExpr), nil case *tree.FuncExpr: + funcExprCount++ name := t.Func.FunctionReference.String() relationship, ok := geoindex.RelationshipMap[name] if !ok { @@ -766,6 +945,10 @@ func NewGeoDatumsToInvertedExpr( var spanExpr *invertedexpr.SpanExpression if d, ok := nonIndexParam.(tree.Datum); ok { spanExpr = g.getSpanExpr(evalCtx.Ctx(), d, additionalParams, relationship, g.indexConfig) + } else if funcExprCount == 1 { + // Currently pre-filtering is limited to a single FuncExpr. + preFilterRelationship = relationship + additionalPreFilterParams = additionalParams } return &geoInvertedExpr{ @@ -786,18 +969,21 @@ func NewGeoDatumsToInvertedExpr( if err != nil { return nil, err } - + if funcExprCount == 1 { + g.filterer = NewPreFilterer(g.typ, preFilterRelationship, additionalPreFilterParams) + } return g, nil } // Convert implements the invertedexpr.DatumsToInvertedExpr interface. func (g *geoDatumsToInvertedExpr) Convert( ctx context.Context, datums rowenc.EncDatumRow, -) (*invertedexpr.SpanExpressionProto, error) { +) (*invertedexpr.SpanExpressionProto, interface{}, error) { g.row = datums g.evalCtx.PushIVarContainer(g) defer g.evalCtx.PopIVarContainer() + var preFilterState interface{} var evalInvertedExpr func(expr tree.TypedExpr) (invertedexpr.InvertedExpression, error) evalInvertedExpr = func(expr tree.TypedExpr) (invertedexpr.InvertedExpression, error) { switch t := expr.(type) { @@ -843,6 +1029,9 @@ func (g *geoDatumsToInvertedExpr) Convert( if d == tree.DNull { return nil, nil } + if g.filterer != nil { + preFilterState = g.filterer.Bind(d) + } return g.getSpanExpr(ctx, d, t.additionalParams, t.relationship, g.indexConfig), nil default: @@ -852,19 +1041,29 @@ func (g *geoDatumsToInvertedExpr) Convert( invertedExpr, err := evalInvertedExpr(g.invertedExpr) if err != nil { - return nil, err + return nil, nil, err } if invertedExpr == nil { - return nil, nil + return nil, nil, nil } spanExpr, ok := invertedExpr.(*invertedexpr.SpanExpression) if !ok { - return nil, fmt.Errorf("unable to construct span expression") + return nil, nil, fmt.Errorf("unable to construct span expression") } - return spanExpr.ToProto(), nil + return spanExpr.ToProto(), preFilterState, nil +} + +func (g *geoDatumsToInvertedExpr) CanPreFilter() bool { + return g.filterer != nil +} + +func (g *geoDatumsToInvertedExpr) PreFilter( + enc invertedexpr.EncInvertedVal, preFilters []interface{}, result []bool, +) (bool, error) { + return g.filterer.PreFilter(enc, preFilters, result) } func (g *geoDatumsToInvertedExpr) String() string { diff --git a/pkg/sql/opt/invertedidx/geo_test.go b/pkg/sql/opt/invertedidx/geo_test.go index c3bb666af335..43ee128b9dc7 100644 --- a/pkg/sql/opt/invertedidx/geo_test.go +++ b/pkg/sql/opt/invertedidx/geo_test.go @@ -12,9 +12,15 @@ package invertedidx_test import ( "context" + "math" + "strconv" "testing" + "github.com/cockroachdb/cockroach/pkg/geo" + "github.com/cockroachdb/cockroach/pkg/geo/geoindex" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" "github.com/cockroachdb/cockroach/pkg/sql/opt" + "github.com/cockroachdb/cockroach/pkg/sql/opt/invertedexpr" "github.com/cockroachdb/cockroach/pkg/sql/opt/invertedidx" "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" "github.com/cockroachdb/cockroach/pkg/sql/opt/norm" @@ -22,7 +28,10 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat" "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/errors" + "github.com/stretchr/testify/require" ) func TestTryJoinGeoIndex(t *testing.T) { @@ -493,3 +502,207 @@ func buildFilters( filters = f.CustomFuncs().ConsolidateFilters(filters) return filters, nil } + +func TestPreFilterer(t *testing.T) { + // Test cases do pre-filtering for (geoShapes[i], geoShapes[j]) for all i, + // j. + geoShapes := []string{ + "SRID=4326;POINT(0 0)", + "SRID=4326;POINT(5 5)", + "SRID=4326;LINESTRING(8 8, 9 9)", + "SRID=4326;POLYGON((0 0, 5 0, 5 5, 0 5, 0 0))", + } + testCases := []struct { + // The typ, relationship and relationshipParams determine how the + // PreFilterer works. + typ *types.T + relationship geoindex.RelationshipType + relationshipParams []tree.Datum + shapes []string + expected [][]bool + // excludeFromPreFilters excludes shapes at the given indexes from being + // used in Bind calls. + excludeFromPreFilters []int + }{ + { + typ: types.Geometry, + relationship: geoindex.Intersects, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, true}, + {false, true, false, true}, + {false, false, true, false}, + {true, true, false, true}, + }, + }, + { + typ: types.Geometry, + relationship: geoindex.Covers, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, true}, + {false, true, false, true}, + {false, false, true, false}, + {false, false, false, true}, + }, + }, + { + typ: types.Geometry, + relationship: geoindex.CoveredBy, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, false}, + {false, true, false, false}, + {false, false, true, false}, + {true, true, false, true}, + }, + }, + { + typ: types.Geometry, + relationship: geoindex.DWithin, + relationshipParams: []tree.Datum{tree.NewDFloat(3)}, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, true}, + {false, true, true, true}, + {false, true, true, true}, + {true, true, true, true}, + }, + }, + { + typ: types.Geometry, + relationship: geoindex.DFullyWithin, + relationshipParams: []tree.Datum{tree.NewDFloat(3)}, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, false}, + {false, true, false, false}, + {false, true, true, false}, + {true, true, false, true}, + }, + }, + { + typ: types.Geography, + relationship: geoindex.Intersects, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, true}, + {false, true, false, true}, + {false, false, true, false}, + {true, true, false, true}, + }, + }, + { + typ: types.Geography, + relationship: geoindex.Covers, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, true}, + {false, true, false, true}, + {false, false, true, false}, + {false, false, false, true}, + }, + }, + { + typ: types.Geography, + relationship: geoindex.CoveredBy, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, false}, + {false, true, false, false}, + {false, false, true, false}, + {true, true, false, true}, + }, + }, + { + typ: types.Geography, + relationship: geoindex.DWithin, + relationshipParams: []tree.Datum{tree.NewDFloat(3)}, + shapes: geoShapes, + expected: [][]bool{ + {true, false, false, true}, + {false, true, false, true}, + {false, false, true, false}, + {true, true, false, true}, + }, + }, + { + typ: types.Geography, + relationship: geoindex.DWithin, + relationshipParams: []tree.Datum{tree.NewDFloat(3)}, + shapes: geoShapes, + excludeFromPreFilters: []int{2}, + expected: [][]bool{ + {true, false, true}, + {false, true, true}, + {false, false, false}, + {true, true, true}, + }, + }, + } + encodeInv := func(bbox geopb.BoundingBox) invertedexpr.EncInvertedVal { + var b []byte + b = encoding.EncodeGeoInvertedAscending(b) + // Arbitrary cellid + b = encoding.EncodeUvarintAscending(b, math.MaxUint32) + b = encoding.EncodeGeoInvertedBBox(b, bbox.LoX, bbox.LoY, bbox.HiX, bbox.HiY) + return b + } + for i, tc := range testCases { + t.Run(strconv.Itoa(i+1), func(t *testing.T) { + filterer := invertedidx.NewPreFilterer(tc.typ, tc.relationship, tc.relationshipParams) + var toBind []tree.Datum + var toPreFilter []invertedexpr.EncInvertedVal + includeBind := func(index int) bool { + for _, exclude := range tc.excludeFromPreFilters { + if exclude == index { + return false + } + } + return true + } + for i, shape := range tc.shapes { + switch tc.typ { + case types.Geometry: + g, err := geo.ParseGeometry(shape) + require.NoError(t, err) + if includeBind(i) { + toBind = append(toBind, tree.NewDGeometry(g)) + } + toPreFilter = append(toPreFilter, encodeInv(*g.BoundingBoxRef())) + case types.Geography: + g, err := geo.ParseGeography(shape) + require.NoError(t, err) + if includeBind(i) { + toBind = append(toBind, tree.NewDGeography(g)) + } + rect := g.BoundingRect() + toPreFilter = append(toPreFilter, + encodeInv(geopb.BoundingBox{ + LoX: rect.Lng.Lo, + HiX: rect.Lng.Hi, + LoY: rect.Lat.Lo, + HiY: rect.Lat.Hi, + })) + } + } + var preFilterState []interface{} + for _, d := range toBind { + preFilterState = append(preFilterState, filterer.Bind(d)) + } + result := make([]bool, len(preFilterState)) + for i, enc := range toPreFilter { + res, err := filterer.PreFilter(enc, preFilterState, result) + require.NoError(t, err) + expectedRes := false + for _, b := range result { + expectedRes = expectedRes || b + } + require.Equal(t, expectedRes, res) + require.Equal(t, tc.expected[i], result) + } + }) + } +} + +// TODO(sumeer): test for NewGeoDatumsToInvertedExpr, geoDatumsToInvertedExpr. diff --git a/pkg/sql/opt/memo/testdata/stats/inverted-geo b/pkg/sql/opt/memo/testdata/stats/inverted-geo index 7795a014f37c..cb2e9b0f6896 100644 --- a/pkg/sql/opt/memo/testdata/stats/inverted-geo +++ b/pkg/sql/opt/memo/testdata/stats/inverted-geo @@ -26,25 +26,25 @@ ALTER TABLE t INJECT STATISTICS '[ "num_eq":1000, "num_range":0, "distinct_range":0, - "upper_bound":"\\xfd0555555555555555" + "upper_bound":"\\x42fd0555555555555555" }, { "num_eq":1000, "num_range":1000, "distinct_range":1, - "upper_bound":"\\xfd0fffffff00000000" + "upper_bound":"\\x42fd0fffffff00000000" }, { "num_eq":1000, "num_range":1000, "distinct_range":1, - "upper_bound":"\\xfd1000000100000000" + "upper_bound":"\\x42fd1000000100000000" }, { "num_eq":1000, "num_range":1000, "distinct_range":1, - "upper_bound":"\\xfd1aaaaaab00000000" + "upper_bound":"\\x42fd1aaaaaab00000000" }] } ]' @@ -174,13 +174,13 @@ project │ │ ├── columns: rowid:3(int!null) │ │ ├── inverted expression: /5 │ │ │ ├── tight: false - │ │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── stats: [rows=7e-07] │ │ ├── key: (3) │ │ └── scan t@secondary │ │ ├── columns: rowid:3(int!null) g_inverted_key:5(geometry!null) │ │ ├── inverted constraint: /5/3 - │ │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── stats: [rows=7e-07, distinct(3)=1.99999931e-07, null(3)=0, distinct(5)=7e-07, null(5)=0] │ │ │ histogram(5)= │ │ ├── key: (3) @@ -259,25 +259,25 @@ ALTER TABLE t INJECT STATISTICS '[ "num_eq":1000, "num_range":0, "distinct_range":0, - "upper_bound":"\\xfd0555555555555555" + "upper_bound":"\\x42fd0555555555555555" }, { "num_eq":1000, "num_range":1000, "distinct_range":1, - "upper_bound":"\\xfd0fffffff00000000" + "upper_bound":"\\x42fd0fffffff00000000" }, { "num_eq":1000, "num_range":1000, "distinct_range":1, - "upper_bound":"\\xfd1000000100000000" + "upper_bound":"\\x42fd1000000100000000" }, { "num_eq":1000, "num_range":1000, "distinct_range":1, - "upper_bound":"\\xfd1aaaaaab00000000" + "upper_bound":"\\x42fd1aaaaaab00000000" }] } ]' @@ -394,13 +394,13 @@ project │ │ ├── columns: rowid:3(int!null) │ │ ├── inverted expression: /5 │ │ │ ├── tight: false - │ │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── stats: [rows=7e-07] │ │ ├── key: (3) │ │ └── scan t@secondary │ │ ├── columns: rowid:3(int!null) g_inverted_key:5(geometry!null) │ │ ├── inverted constraint: /5/3 - │ │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── stats: [rows=7e-07, distinct(3)=1.99999931e-07, null(3)=0, distinct(5)=7e-07, null(5)=0] │ │ │ histogram(5)= │ │ ├── key: (3) @@ -430,25 +430,25 @@ ALTER TABLE t INJECT STATISTICS '[ "num_eq":100, "num_range":0, "distinct_range":0, - "upper_bound":"\\xfd0555555555555555" + "upper_bound":"\\x42fd0555555555555555" }, { "num_eq":100, "num_range":0, "distinct_range":0, - "upper_bound":"\\xfd0fffffff00000000" + "upper_bound":"\\x42fd0fffffff00000000" }, { "num_eq":100, "num_range":0, "distinct_range":0, - "upper_bound":"\\xfd1000000100000000" + "upper_bound":"\\x42fd1000000100000000" }, { "num_eq":100, "num_range":0, "distinct_range":0, - "upper_bound":"\\xfd1aaaaaab00000000" + "upper_bound":"\\x42fd1aaaaaab00000000" }] } ]' @@ -469,21 +469,21 @@ select │ ├── inverted expression: /5 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "\xfd\x12\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x12\x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── stats: [rows=100] │ ├── key: (3) │ └── scan t@secondary │ ├── columns: rowid:3(int!null) g_inverted_key:5(geometry!null) │ ├── inverted constraint: /5/3 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "\xfd\x12\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x12\x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── stats: [rows=100, distinct(3)=100, null(3)=0, distinct(5)=1, null(5)=0] - │ │ histogram(5)= 0 100 0 0 - │ │ <--- '\xfd1000000100000000' --- '\xfd1400000000000001' + │ │ histogram(5)= 0 100 0 0 + │ │ <--- '\x42fd1000000100000000' --- '\x42fd1400000000000001' │ ├── key: (3) │ └── fd: (3)-->(5) └── filters @@ -509,22 +509,22 @@ project │ ├── inverted expression: /5 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "\xfd\x12\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x12\x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── stats: [rows=100] │ ├── key: (3) │ └── scan t@secondary │ ├── columns: rowid:3(int!null) g_inverted_key:5(geometry!null) │ ├── inverted constraint: /5/3 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "\xfd\x12\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x12\x00\x00\x00\x00\x00\x00\x00") + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── flags: force-index=secondary │ ├── stats: [rows=100, distinct(3)=100, null(3)=0, distinct(5)=1, null(5)=0] - │ │ histogram(5)= 0 100 0 0 - │ │ <--- '\xfd1000000100000000' --- '\xfd1400000000000001' + │ │ histogram(5)= 0 100 0 0 + │ │ <--- '\x42fd1000000100000000' --- '\x42fd1400000000000001' │ ├── key: (3) │ └── fd: (3)-->(5) └── filters diff --git a/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx b/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx index cecc97268235..41d9d241ccad 100644 --- a/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx +++ b/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx @@ -39,12 +39,12 @@ project │ ├── columns: gid:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── scan nyc_neighborhoods@nyc_neighborhoods_geom_idx │ ├── columns: gid:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -82,12 +82,12 @@ project │ ├── columns: gid:1!null │ ├── inverted expression: /8 │ │ ├── tight: false - │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── scan nyc_streets@nyc_streets_geom_idx │ ├── columns: gid:1!null geom_inverted_key:8!null │ ├── inverted constraint: /8/1 - │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── fd: (1)-->(8) └── filters @@ -127,12 +127,12 @@ project │ ├── columns: gid:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── scan nyc_neighborhoods@nyc_neighborhoods_geom_idx │ ├── columns: gid:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -173,12 +173,12 @@ project │ ├── columns: gid:1!null │ ├── inverted expression: /8 │ │ ├── tight: false - │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── scan nyc_streets@nyc_streets_geom_idx │ ├── columns: gid:1!null geom_inverted_key:8!null │ ├── inverted constraint: /8/1 - │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ ├── key: (1) │ └── fd: (1)-->(8) └── filters @@ -215,12 +215,12 @@ scalar-group-by │ │ ├── columns: gid:1!null │ │ ├── inverted expression: /12 │ │ │ ├── tight: false - │ │ │ └── union spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── key: (1) │ │ └── scan nyc_census_blocks@nyc_census_blocks_geom_idx │ │ ├── columns: gid:1!null geom_inverted_key:12!null │ │ ├── inverted constraint: /12/1 - │ │ │ └── spans: ["\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── key: (1) │ │ └── fd: (1)-->(12) │ └── filters diff --git a/pkg/sql/opt/xform/testdata/rules/join b/pkg/sql/opt/xform/testdata/rules/join index 89c6b57a68fd..fbbddb2a3931 100644 --- a/pkg/sql/opt/xform/testdata/rules/join +++ b/pkg/sql/opt/xform/testdata/rules/join @@ -3467,12 +3467,12 @@ project │ │ │ ├── columns: n.gid:13!null │ │ │ ├── inverted expression: /18 │ │ │ │ ├── tight: false - │ │ │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ │ │ ├── key: (13) │ │ │ └── scan n@nyc_neighborhoods_geo_idx │ │ │ ├── columns: n.gid:13!null n.geom_inverted_key:18!null │ │ │ ├── inverted constraint: /18/13 - │ │ │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ │ │ ├── flags: force-index=nyc_neighborhoods_geo_idx │ │ │ ├── key: (13) │ │ │ └── fd: (13)-->(18) diff --git a/pkg/sql/opt/xform/testdata/rules/limit b/pkg/sql/opt/xform/testdata/rules/limit index 7e3ae659d482..fd4d0ab9f2db 100644 --- a/pkg/sql/opt/xform/testdata/rules/limit +++ b/pkg/sql/opt/xform/testdata/rules/limit @@ -1213,17 +1213,17 @@ select │ ├── inverted expression: /10 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x01", "\xfd\x16\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x16\x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan index_tab@geomidx │ ├── columns: id:1!null geom_inverted_key:10!null │ ├── inverted constraint: /10/1 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x01", "\xfd\x16\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x16\x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(10) └── filters diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index 9e78812bd285..8519529720cc 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -1548,17 +1548,17 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -1586,17 +1586,17 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -1623,17 +1623,17 @@ project │ ├── inverted expression: /6 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1660,12 +1660,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1691,12 +1691,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1722,12 +1722,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1754,31 +1754,31 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x00", "\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x01", "\xfdF\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdF\x00\x00\x00\x00\x00\x00\x01", "\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdH\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x01", "\xfdR\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdR\x00\x00\x00\x00\x00\x00\x01", "\xfdT\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdT\x00\x00\x00\x00\x00\x00\x00", "\xfdT\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdZ\x00\x00\x00\x00\x00\x00\x01", "\xfd\\\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\\\x00\x00\x00\x00\x00\x00\x00", "\xfd\\\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\xfdD\x00\x00\x00\x00\x00\x00\x01", "B\xfdF\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\xfdP\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\xfdP\x00\x00\x00\x00\x00\x00\x01", "B\xfdR\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdR\x00\x00\x00\x00\x00\x00\x01", "B\xfdT\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdT\x00\x00\x00\x00\x00\x00\x00", "B\xfdT\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdZ\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"] │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x00", "\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x01", "\xfdF\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdF\x00\x00\x00\x00\x00\x00\x01", "\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdH\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x01", "\xfdR\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdR\x00\x00\x00\x00\x00\x00\x01", "\xfdT\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdT\x00\x00\x00\x00\x00\x00\x00", "\xfdT\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdZ\x00\x00\x00\x00\x00\x00\x01", "\xfd\\\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\\\x00\x00\x00\x00\x00\x00\x00", "\xfd\\\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\xfdD\x00\x00\x00\x00\x00\x00\x01", "B\xfdF\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\xfdP\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\xfdP\x00\x00\x00\x00\x00\x00\x01", "B\xfdR\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdR\x00\x00\x00\x00\x00\x00\x01", "B\xfdT\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdT\x00\x00\x00\x00\x00\x00\x00", "B\xfdT\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdZ\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"] │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -1805,31 +1805,31 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x00", "\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x01", "\xfdF\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdF\x00\x00\x00\x00\x00\x00\x01", "\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdH\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x01", "\xfdR\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdR\x00\x00\x00\x00\x00\x00\x01", "\xfdT\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdT\x00\x00\x00\x00\x00\x00\x00", "\xfdT\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdZ\x00\x00\x00\x00\x00\x00\x01", "\xfd\\\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\\\x00\x00\x00\x00\x00\x00\x00", "\xfd\\\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\xfdD\x00\x00\x00\x00\x00\x00\x01", "B\xfdF\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\xfdP\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\xfdP\x00\x00\x00\x00\x00\x00\x01", "B\xfdR\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdR\x00\x00\x00\x00\x00\x00\x01", "B\xfdT\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdT\x00\x00\x00\x00\x00\x00\x00", "B\xfdT\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdZ\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"] │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x00", "\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x01", "\xfdF\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdF\x00\x00\x00\x00\x00\x00\x01", "\xfdH\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdH\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdP\x00\x00\x00\x00\x00\x00\x01", "\xfdR\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdR\x00\x00\x00\x00\x00\x00\x01", "\xfdT\x00\x00\x00\x00\x00\x00\x00") - │ │ ├── ["\xfdT\x00\x00\x00\x00\x00\x00\x00", "\xfdT\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdZ\x00\x00\x00\x00\x00\x00\x01", "\xfd\\\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfd\\\x00\x00\x00\x00\x00\x00\x00", "\xfd\\\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\xfdD\x00\x00\x00\x00\x00\x00\x01", "B\xfdF\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\xfdP\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\xfdP\x00\x00\x00\x00\x00\x00\x01", "B\xfdR\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdR\x00\x00\x00\x00\x00\x00\x01", "B\xfdT\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfdT\x00\x00\x00\x00\x00\x00\x00", "B\xfdT\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfdZ\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"] │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -1856,12 +1856,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1888,12 +1888,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1919,12 +1919,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -1956,16 +1956,16 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdO\x00\x00\x00\x00\x00\x00\x00", "\xfdO\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdO\x00\x00\x00\x00\x00\x00\x00", "B\xfdO\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"] │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\x00\x00\x00\x00\x00\x00\x01") │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -1995,15 +1995,15 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\x00\x00\x00\x00\x00\x00\x01") │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\x00\x00\x00\x00\x00\x00\x01") │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -2033,9 +2033,9 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ ├── union spans - │ │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ │ └── INTERSECTION │ │ ├── span expression │ │ │ ├── tight: false @@ -2044,33 +2044,33 @@ project │ │ │ ├── span expression │ │ │ │ ├── tight: false │ │ │ │ └── union spans - │ │ │ │ ├── ["\xfd\x81\x00\x00\x00\x00\x00\x00\x00", "\xfd\x81\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ │ ├── ["\xfd\x84\x00\x00\x00\x00\x00\x00\x00", "\xfd\x84\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ │ └── ["\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "\xfd\x90\x00\x00\x00\x00\x00\x00\x00"] + │ │ │ │ ├── ["B\xfd\x81\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x81\x00\x00\x00\x00\x00\x00\x00"] + │ │ │ │ ├── ["B\xfd\x84\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x84\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"] │ │ │ └── span expression │ │ │ ├── tight: false │ │ │ └── union spans - │ │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x00", "\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ ├── ["\xfdG\x00\x00\x00\x00\x00\x00\x00", "\xfdG\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdG\x00\x00\x00\x00\x00\x00\x00", "B\xfdG\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"] │ │ └── span expression │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdO\x00\x00\x00\x00\x00\x00\x00", "\xfdO\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdO\x00\x00\x00\x00\x00\x00\x00", "B\xfdO\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"] │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdD\x00\x00\x00\x00\x00\x00\x00", "\xfdD\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdG\x00\x00\x00\x00\x00\x00\x00", "\xfdG\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x01") - │ │ ├── ["\xfd\x81\x00\x00\x00\x00\x00\x00\x00", "\xfd\x81\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x84\x00\x00\x00\x00\x00\x00\x00", "\xfd\x84\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x90\x00\x00\x00\x00\x00\x00\x00", "\xfd\x90\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\xfdG\x00\x00\x00\x00\x00\x00\x00", "B\xfdG\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\x00\x00\x00\x00\x00\x00\x01") + │ │ ├── ["B\xfd\x81\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x81\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x84\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x84\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"] │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -2099,12 +2099,12 @@ project │ ├── columns: k:1!null │ ├── inverted expression: /6 │ │ ├── tight: false - │ │ └── union spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── union spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 - │ │ └── spans: ["\x89", "\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ └── spans: ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -2157,15 +2157,15 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\x00\x00\x00\x00\x00\x00\x01") │ ├── key: (1) │ └── scan g@geog_idx │ ├── columns: k:1!null geog_inverted_key:7!null │ ├── inverted constraint: /7/1 │ │ └── spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\x00\x00\x00\x00\x00\x00\x01") │ ├── key: (1) │ └── fd: (1)-->(7) └── filters @@ -2195,9 +2195,9 @@ project │ ├── inverted expression: /7 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ ├── key: (1) │ └── select │ ├── columns: k:1!null geog_inverted_key:7!null @@ -2207,9 +2207,9 @@ project │ │ ├── columns: k:1!null geog_inverted_key:7!null │ │ ├── inverted constraint: /7/1 │ │ │ └── spans - │ │ │ ├── ["\xfdL\x00\x00\x00\x00\x00\x00\x00", "\xfdL\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ ├── ["\xfdN\x00\x00\x00\x00\x00\x00\x01", "\xfdP\x00\x00\x00\x00\x00\x00\x00") - │ │ │ └── ["\xfdP\x00\x00\x00\x00\x00\x00\x00", "\xfdP\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\xfdN\x00\x00\x00\x00\x00\x00\x01", "B\xfdP\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"] │ │ ├── key: (1) │ │ └── fd: (1)-->(7) │ └── filters @@ -2240,15 +2240,15 @@ project │ ├── inverted expression: /6 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "\xfd\x18\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x18\x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "\xfd\x18\x00\x00\x00\x00\x00\x00\x00") + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x01", "B\xfd\x18\x00\x00\x00\x00\x00\x00\x00") │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -2275,15 +2275,15 @@ project │ ├── inverted expression: /6 │ │ ├── tight: false │ │ └── union spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ ├── key: (1) │ └── fd: (1)-->(6) └── filters @@ -2310,24 +2310,24 @@ project │ ├── inverted expression: /6 │ │ ├── tight: false │ │ ├── union spans - │ │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ │ └── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] │ │ └── INTERSECTION │ │ ├── span expression │ │ │ ├── tight: false - │ │ │ └── union spans: ["\xfd\x15\x00\x00\x00\x00\x00\x00\x00", "\xfd\x15\x00\x00\x00\x00\x00\x00\x00"] + │ │ │ └── union spans: ["B\xfd\x15\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x15\x00\x00\x00\x00\x00\x00\x00"] │ │ └── span expression │ │ ├── tight: false - │ │ └── union spans: ["\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── union spans: ["B\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] │ ├── key: (1) │ └── scan g@geom_idx │ ├── columns: k:1!null geom_inverted_key:6!null │ ├── inverted constraint: /6/1 │ │ └── spans - │ │ ├── ["\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] - │ │ ├── ["\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] - │ │ └── ["\xfd\x15\x00\x00\x00\x00\x00\x00\x00", "\xfd\x15\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x11\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x11\x00\x00\x00\x00\x00\x00\x00"] + │ │ ├── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"] + │ │ └── ["B\xfd\x15\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x15\x00\x00\x00\x00\x00\x00\x00"] │ ├── key: (1) │ └── fd: (1)-->(6) └── filters diff --git a/pkg/sql/rowenc/index_encoding.go b/pkg/sql/rowenc/index_encoding.go index cfa7c7b1a549..bf3ccb3d23c1 100644 --- a/pkg/sql/rowenc/index_encoding.go +++ b/pkg/sql/rowenc/index_encoding.go @@ -16,6 +16,7 @@ import ( "sort" "github.com/cockroachdb/cockroach/pkg/geo/geoindex" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog" @@ -830,31 +831,36 @@ func EncodeGeoInvertedIndexTableKeys( switch val.ResolvedType().Family() { case types.GeographyFamily: index := geoindex.NewS2GeographyIndex(*index.GeoConfig.S2Geography) - intKeys, err := index.InvertedIndexKeys(context.TODO(), val.(*tree.DGeography).Geography) + intKeys, bbox, err := index.InvertedIndexKeys(context.TODO(), val.(*tree.DGeography).Geography) if err != nil { return nil, err } - return encodeGeoKeys(inKey, intKeys) + return encodeGeoKeys(encoding.EncodeGeoInvertedAscending(inKey), intKeys, bbox) case types.GeometryFamily: index := geoindex.NewS2GeometryIndex(*index.GeoConfig.S2Geometry) - intKeys, err := index.InvertedIndexKeys(context.TODO(), val.(*tree.DGeometry).Geometry) + intKeys, bbox, err := index.InvertedIndexKeys(context.TODO(), val.(*tree.DGeometry).Geometry) if err != nil { return nil, err } - return encodeGeoKeys(inKey, intKeys) + return encodeGeoKeys(encoding.EncodeGeoInvertedAscending(inKey), intKeys, bbox) default: return nil, errors.Errorf("internal error: unexpected type: %s", val.ResolvedType().Family()) } } -func encodeGeoKeys(inKey []byte, geoKeys []geoindex.Key) (keys [][]byte, err error) { +func encodeGeoKeys( + inKey []byte, geoKeys []geoindex.Key, bbox geopb.BoundingBox, +) (keys [][]byte, err error) { + encodedBBox := make([]byte, 0, encoding.MaxGeoInvertedBBoxLen) + encodedBBox = encoding.EncodeGeoInvertedBBox(encodedBBox, bbox.LoX, bbox.LoY, bbox.HiX, bbox.HiY) // Avoid per-key heap allocations. - b := make([]byte, 0, len(geoKeys)*(len(inKey)+encoding.MaxVarintLen)) + b := make([]byte, 0, len(geoKeys)*(len(inKey)+encoding.MaxVarintLen+len(encodedBBox))) keys = make([][]byte, len(geoKeys)) for i, k := range geoKeys { prev := len(b) b = append(b, inKey...) b = encoding.EncodeUvarintAscending(b, uint64(k)) + b = append(b, encodedBBox...) // Set capacity so that the caller appending does not corrupt later keys. newKey := b[prev:len(b):len(b)] keys[i] = newKey diff --git a/pkg/sql/rowexec/inverted_expr_evaluator.go b/pkg/sql/rowexec/inverted_expr_evaluator.go index 8620ea34fa4d..0120baf5cb11 100644 --- a/pkg/sql/rowexec/inverted_expr_evaluator.go +++ b/pkg/sql/rowexec/inverted_expr_evaluator.go @@ -15,6 +15,7 @@ import ( "sort" "github.com/cockroachdb/cockroach/pkg/sql/opt/invertedexpr" + "github.com/cockroachdb/errors" ) // The abstractions in this file help with evaluating (batches of) @@ -218,13 +219,27 @@ type exprAndSetIndex struct { setIndex int } +type exprAndSetIndexSorter []exprAndSetIndex + +// Implement sort.Interface. Sorts in increasing order of exprIndex. +func (esis exprAndSetIndexSorter) Len() int { return len(esis) } +func (esis exprAndSetIndexSorter) Swap(i, j int) { esis[i], esis[j] = esis[j], esis[i] } +func (esis exprAndSetIndexSorter) Less(i, j int) bool { + return esis[i].exprIndex < esis[j].exprIndex +} + // invertedSpanRoutingInfo contains the list of exprAndSetIndex pairs that // need rows from the inverted index span. A []invertedSpanRoutingInfo with // spans that are sorted and non-overlapping is used to route an added row to // all the expressions and sets that need that row. type invertedSpanRoutingInfo struct { - span invertedSpan + span invertedSpan + // Sorted in increasing order of exprIndex. exprAndSetIndexList []exprAndSetIndex + // A de-duped and sorted list of exprIndex values from exprAndSetIndexList. + // Used for pre-filtering, since the pre-filter is applied on a per + // exprIndex basis. + exprIndexList []int } // invertedSpanRoutingInfosByEndKey is a slice of invertedSpanRoutingInfo that @@ -240,18 +255,35 @@ func (s invertedSpanRoutingInfosByEndKey) Less(i, j int) bool { return bytes.Compare(s[i].span.End, s[j].span.End) < 0 } +// preFilterer is the single method from DatumsToInvertedExpr that is relevant here. +type preFilterer interface { + PreFilter(enc invertedexpr.EncInvertedVal, preFilters []interface{}, result []bool) (bool, error) +} + // batchedInvertedExprEvaluator is for evaluating one or more expressions. The // batched evaluator can be reused by calling reset(). In the build phase, // append expressions directly to exprs. A nil expression is permitted, and is // just a placeholder that will result in a nil []KeyIndex in evaluate(). -// init() must be called before calls to addIndexRow() -- it builds the +// init() must be called before calls to {prepare}addIndexRow() -- it builds the // fragmentedSpans used for routing the added rows. type batchedInvertedExprEvaluator struct { - exprs []*invertedexpr.SpanExpressionProto + filterer preFilterer + exprs []*invertedexpr.SpanExpressionProto + + // The pre-filtering state for each expression. When pre-filtering, this + // is the same length as exprs. + preFilterState []interface{} + // The parameters and result of pre-filtering for an inverted row are + // kept in this temporary state. + tempPreFilters []interface{} + tempPreFilterResult []bool + // The evaluators for all the exprs. exprEvals []*invertedExprEvaluator // Spans here are in sorted order and non-overlapping. fragmentedSpans []invertedSpanRoutingInfo + // The routing index computed by prepareAddIndexRow + routingIndex int // Temporary state used during initialization. routingSpans []invertedSpanRoutingInfo @@ -338,6 +370,17 @@ func (b *batchedInvertedExprEvaluator) fragmentPendingSpans( nextSpan.exprAndSetIndexList = append(nextSpan.exprAndSetIndexList, pendingSpans[i].exprAndSetIndexList...) } + // Sort the exprAndSetIndexList, since we need to use it to initialize the + // exprIndexList before we push nextSpan onto b.fragmentedSpans. + sort.Sort(exprAndSetIndexSorter(nextSpan.exprAndSetIndexList)) + nextSpan.exprIndexList = make([]int, 0, len(nextSpan.exprAndSetIndexList)) + for i := range nextSpan.exprAndSetIndexList { + length := len(nextSpan.exprIndexList) + exprIndex := nextSpan.exprAndSetIndexList[i].exprIndex + if length == 0 || nextSpan.exprIndexList[length-1] != exprIndex { + nextSpan.exprIndexList = append(nextSpan.exprIndexList, exprIndex) + } + } b.fragmentedSpans = append(b.fragmentedSpans, nextSpan) pendingSpans = pendingSpans[removeSize:] if removeSize == 0 { @@ -436,18 +479,65 @@ func (b *batchedInvertedExprEvaluator) init() []invertedSpan { return b.coveringSpans } +// prepareAddIndexRow must be called prior to addIndexRow to do any +// pre-filtering. The return value indicates whether addIndexRow should be +// called. // TODO(sumeer): if this will be called in non-decreasing order of enc, // use that to optimize the binary search. -func (b *batchedInvertedExprEvaluator) addIndexRow( - enc invertedexpr.EncInvertedVal, keyIndex KeyIndex, -) { +func (b *batchedInvertedExprEvaluator) prepareAddIndexRow( + enc invertedexpr.EncInvertedVal, +) (bool, error) { i := sort.Search(len(b.fragmentedSpans), func(i int) bool { return bytes.Compare(b.fragmentedSpans[i].span.Start, enc) > 0 }) i-- - for _, elem := range b.fragmentedSpans[i].exprAndSetIndexList { - b.exprEvals[elem.exprIndex].addIndexRow(elem.setIndex, keyIndex) + b.routingIndex = i + if b.filterer != nil { + exprIndexList := b.fragmentedSpans[i].exprIndexList + if len(exprIndexList) > cap(b.tempPreFilters) { + b.tempPreFilters = make([]interface{}, len(exprIndexList)) + b.tempPreFilterResult = make([]bool, len(exprIndexList)) + } else { + b.tempPreFilters = b.tempPreFilters[:len(exprIndexList)] + b.tempPreFilterResult = b.tempPreFilterResult[:len(exprIndexList)] + } + for j := range exprIndexList { + b.tempPreFilters[j] = b.preFilterState[exprIndexList[j]] + } + return b.filterer.PreFilter(enc, b.tempPreFilters, b.tempPreFilterResult) + } + return true, nil +} + +// addIndexRow must be called iff prepareAddIndexRow returned true. +func (b *batchedInvertedExprEvaluator) addIndexRow(keyIndex KeyIndex) error { + i := b.routingIndex + if b.filterer != nil { + exprIndexes := b.fragmentedSpans[i].exprIndexList + exprSetIndexes := b.fragmentedSpans[i].exprAndSetIndexList + if len(exprIndexes) != len(b.tempPreFilterResult) { + return errors.Errorf("non-matching lengths of tempPreFilterResult and exprIndexes") + } + // Coordinated iteration over exprIndexes and exprSetIndexes. + j := 0 + for k := range exprSetIndexes { + elem := exprSetIndexes[k] + if elem.exprIndex > exprIndexes[j] { + j++ + if exprIndexes[j] != elem.exprIndex { + return errors.Errorf("non-matching expr indexes") + } + } + if b.tempPreFilterResult[j] { + b.exprEvals[elem.exprIndex].addIndexRow(elem.setIndex, keyIndex) + } + } + } else { + for _, elem := range b.fragmentedSpans[i].exprAndSetIndexList { + b.exprEvals[elem.exprIndex].addIndexRow(elem.setIndex, keyIndex) + } } + return nil } func (b *batchedInvertedExprEvaluator) evaluate() [][]KeyIndex { @@ -463,6 +553,7 @@ func (b *batchedInvertedExprEvaluator) evaluate() [][]KeyIndex { func (b *batchedInvertedExprEvaluator) reset() { b.exprs = b.exprs[:0] + b.preFilterState = b.preFilterState[:0] b.exprEvals = b.exprEvals[:0] b.fragmentedSpans = b.fragmentedSpans[:0] b.routingSpans = b.routingSpans[:0] diff --git a/pkg/sql/rowexec/inverted_expr_evaluator_test.go b/pkg/sql/rowexec/inverted_expr_evaluator_test.go index df5f1cf5d50e..8b976c44042f 100644 --- a/pkg/sql/rowexec/inverted_expr_evaluator_test.go +++ b/pkg/sql/rowexec/inverted_expr_evaluator_test.go @@ -66,6 +66,10 @@ func fragmentedSpansToString(spans []invertedSpanRoutingInfo) string { for _, indexes := range elem.exprAndSetIndexList { fmt.Fprintf(&b, "(%d, %d) ", indexes.exprIndex, indexes.setIndex) } + b.WriteString("(expr): ") + for _, indexes := range elem.exprIndexList { + fmt.Fprintf(&b, "%d ", indexes) + } b.WriteString("\n") } return b.String() @@ -209,17 +213,17 @@ func TestInvertedExpressionEvaluator(t *testing.T) { } expectedSpans := "[a, n) " expectedFragmentedSpans := - "span: [a, c) indexes (expr, set): (0, 6) (0, 2) \n" + - "span: [c, d) indexes (expr, set): (0, 2) \n" + - "span: [d, e) indexes (expr, set): (0, 5) \n" + - "span: [e, f) indexes (expr, set): (0, 5) (0, 3) \n" + - "span: [f, g) indexes (expr, set): (0, 3) \n" + - "span: [g, h) indexes (expr, set): (0, 3) (0, 4) \n" + - "span: [h, i) indexes (expr, set): (0, 4) \n" + - "span: [i, j) indexes (expr, set): (0, 1) (0, 4) \n" + - "span: [j, k) indexes (expr, set): (0, 4) \n" + - "span: [k, m) indexes (expr, set): (0, 4) (0, 1) \n" + - "span: [m, n) indexes (expr, set): (0, 1) \n" + "span: [a, c) indexes (expr, set): (0, 6) (0, 2) (expr): 0 \n" + + "span: [c, d) indexes (expr, set): (0, 2) (expr): 0 \n" + + "span: [d, e) indexes (expr, set): (0, 5) (expr): 0 \n" + + "span: [e, f) indexes (expr, set): (0, 5) (0, 3) (expr): 0 \n" + + "span: [f, g) indexes (expr, set): (0, 3) (expr): 0 \n" + + "span: [g, h) indexes (expr, set): (0, 3) (0, 4) (expr): 0 \n" + + "span: [h, i) indexes (expr, set): (0, 4) (expr): 0 \n" + + "span: [i, j) indexes (expr, set): (0, 1) (0, 4) (expr): 0 \n" + + "span: [j, k) indexes (expr, set): (0, 4) (expr): 0 \n" + + "span: [k, m) indexes (expr, set): (0, 4) (0, 1) (expr): 0 \n" + + "span: [m, n) indexes (expr, set): (0, 1) (expr): 0 \n" require.Equal(t, expectedSpans, spansToString(batchEvalUnion.init())) require.Equal(t, expectedFragmentedSpans, @@ -248,8 +252,16 @@ func TestInvertedExpressionEvaluator(t *testing.T) { indexRows[i], indexRows[j] = indexRows[j], indexRows[i] }) for _, elem := range indexRows { - batchEvalUnion.addIndexRow(invertedexpr.EncInvertedVal(elem.key), elem.index) - batchEvalIntersection.addIndexRow(invertedexpr.EncInvertedVal(elem.key), elem.index) + add, err := batchEvalUnion.prepareAddIndexRow(invertedexpr.EncInvertedVal(elem.key)) + require.NoError(t, err) + require.Equal(t, true, add) + err = batchEvalUnion.addIndexRow(elem.index) + require.NoError(t, err) + add, err = batchEvalIntersection.prepareAddIndexRow(invertedexpr.EncInvertedVal(elem.key)) + require.NoError(t, err) + require.Equal(t, true, add) + err = batchEvalIntersection.addIndexRow(elem.index) + require.NoError(t, err) } require.Equal(t, expectedUnion, keyIndexesToString(batchEvalUnion.evaluate())) require.Equal(t, expectedIntersection, keyIndexesToString(batchEvalIntersection.evaluate())) @@ -260,7 +272,11 @@ func TestInvertedExpressionEvaluator(t *testing.T) { batchBoth.exprs = append(batchBoth.exprs, &protoUnion, &protoIntersection) batchBoth.init() for _, elem := range indexRows { - batchBoth.addIndexRow(invertedexpr.EncInvertedVal(elem.key), elem.index) + add, err := batchBoth.prepareAddIndexRow(invertedexpr.EncInvertedVal(elem.key)) + require.NoError(t, err) + require.Equal(t, true, add) + err = batchBoth.addIndexRow(elem.index) + require.NoError(t, err) } require.Equal(t, "0: 0 3 4 5 6 7 8 \n1: 0 4 6 8 \n", keyIndexesToString(batchBoth.evaluate())) @@ -302,15 +318,120 @@ func TestFragmentedSpans(t *testing.T) { } require.Equal(t, "[a, l) [o, p) ", spansToString(batchEval.init())) require.Equal(t, - "span: [a, d) indexes (expr, set): (0, 0) \n"+ - "span: [d, e) indexes (expr, set): (0, 0) (1, 0) \n"+ - "span: [e, f) indexes (expr, set): (2, 0) (0, 0) (1, 0) \n"+ - "span: [f, g) indexes (expr, set): (0, 0) (1, 0) \n"+ - "span: [g, i) indexes (expr, set): (1, 0) \n"+ - "span: [i, j) indexes (expr, set): (1, 0) (2, 0) \n"+ - "span: [j, l) indexes (expr, set): (2, 0) \n"+ - "span: [o, p) indexes (expr, set): (2, 0) \n", + "span: [a, d) indexes (expr, set): (0, 0) (expr): 0 \n"+ + "span: [d, e) indexes (expr, set): (0, 0) (1, 0) (expr): 0 1 \n"+ + "span: [e, f) indexes (expr, set): (0, 0) (1, 0) (2, 0) (expr): 0 1 2 \n"+ + "span: [f, g) indexes (expr, set): (0, 0) (1, 0) (expr): 0 1 \n"+ + "span: [g, i) indexes (expr, set): (1, 0) (expr): 1 \n"+ + "span: [i, j) indexes (expr, set): (1, 0) (2, 0) (expr): 1 2 \n"+ + "span: [j, l) indexes (expr, set): (2, 0) (expr): 2 \n"+ + "span: [o, p) indexes (expr, set): (2, 0) (expr): 2 \n", fragmentedSpansToString(batchEval.fragmentedSpans)) } +type testPreFilterer struct { + t *testing.T + expectedPreFilters []interface{} + result []bool +} + +func (t *testPreFilterer) PreFilter( + enc invertedexpr.EncInvertedVal, preFilters []interface{}, result []bool, +) (bool, error) { + require.Equal(t.t, t.expectedPreFilters, preFilters) + rv := false + require.Equal(t.t, len(t.result), len(result)) + for i := range result { + result[i] = t.result[i] + rv = rv || result[i] + } + return rv, nil +} + +func TestInvertedExpressionEvaluatorPreFilter(t *testing.T) { + defer leaktest.AfterTest(t)() + // Setup expressions such that the same expression appears multiple times + // in a span. + leaf1 := &spanExpression{ + FactoredUnionSpans: []invertedSpan{{Start: []byte("a"), End: []byte("d")}}, + Operator: invertedexpr.None, + } + leaf2 := &spanExpression{ + FactoredUnionSpans: []invertedSpan{{Start: []byte("e"), End: []byte("h")}}, + Operator: invertedexpr.None, + } + expr1 := &spanExpression{ + Operator: invertedexpr.SetIntersection, + Left: &spanExpression{ + Operator: invertedexpr.SetIntersection, + Left: leaf1, + Right: leaf2, + }, + Right: leaf1, + } + expr1Proto := invertedexpr.SpanExpressionProto{Node: *expr1} + expr2 := &spanExpression{ + Operator: invertedexpr.SetIntersection, + Left: &spanExpression{ + Operator: invertedexpr.SetIntersection, + Left: leaf2, + Right: leaf1, + }, + Right: leaf2, + } + expr2Proto := invertedexpr.SpanExpressionProto{Node: *expr2} + preFilters := []interface{}{"pf1", "pf2"} + batchEval := &batchedInvertedExprEvaluator{ + exprs: []*invertedexpr.SpanExpressionProto{&expr1Proto, &expr2Proto}, + preFilterState: preFilters, + } + require.Equal(t, "[a, d) [e, h) ", spansToString(batchEval.init())) + require.Equal(t, + "span: [a, d) indexes (expr, set): (0, 2) (0, 4) (1, 3) (expr): 0 1 \n"+ + "span: [e, h) indexes (expr, set): (0, 3) (1, 2) (1, 4) (expr): 0 1 \n", + fragmentedSpansToString(batchEval.fragmentedSpans)) + feedIndexRows := func(indexRows []keyAndIndex, expectedAdd bool) { + for _, elem := range indexRows { + add, err := batchEval.prepareAddIndexRow(invertedexpr.EncInvertedVal(elem.key)) + require.NoError(t, err) + require.Equal(t, expectedAdd, add) + if add { + err = batchEval.addIndexRow(elem.index) + } + require.NoError(t, err) + } + + } + filterer := testPreFilterer{ + t: t, + expectedPreFilters: preFilters, + } + batchEval.filterer = &filterer + // Neither row is pre-filtered, so 0 will appear in output. + filterer.result = []bool{true, true} + feedIndexRows([]keyAndIndex{{"a", 0}, {"e", 0}}, true) + + // Row is excluded from second expression + filterer.result = []bool{true, false} + feedIndexRows([]keyAndIndex{{"a", 1}}, true) + filterer.result = []bool{true, true} + // Row is not excluded, but the previous exclusion means 1 will not appear + // in output of second expression. + feedIndexRows([]keyAndIndex{{"e", 1}}, true) + + // Row is excluded from first expression + filterer.result = []bool{false, true} + feedIndexRows([]keyAndIndex{{"a", 2}}, true) + filterer.result = []bool{true, true} + // Row is not excluded, but the previous exclusion means 2 will not appear + // in output of first expression. + feedIndexRows([]keyAndIndex{{"e", 2}}, true) + + // Rows are excluded from both expressions. + filterer.result = []bool{false, false} + feedIndexRows([]keyAndIndex{{"a", 3}, {"e", 3}}, false) + + require.Equal(t, "0: 0 1 \n1: 0 2 \n", keyIndexesToString(batchEval.evaluate())) +} + // TODO(sumeer): randomized inputs for union, intersection and expression evaluation. diff --git a/pkg/sql/rowexec/inverted_filterer.go b/pkg/sql/rowexec/inverted_filterer.go index ca35524bfb1d..be9a8bbe0499 100644 --- a/pkg/sql/rowexec/inverted_filterer.go +++ b/pkg/sql/rowexec/inverted_filterer.go @@ -41,6 +41,9 @@ const ( ifrEmittingRows ) +// TODO(sumeer): support pre-filtering, akin to the invertedJoiner, by passing +// relationship info and parameters in the spec and using it to construct a +// preFilterer. type invertedFilterer struct { execinfra.ProcessorBase runningState invertedFiltererState @@ -205,7 +208,14 @@ func (ifr *invertedFilterer) readInput() (invertedFiltererState, *execinfrapb.Pr return ifrStateUnknown, ifr.DrainHelper() } // Add to the evaluator. - ifr.invertedEval.addIndexRow(row[ifr.invertedColIdx].EncodedBytes(), keyIndex) + if _, err = ifr.invertedEval.prepareAddIndexRow(row[ifr.invertedColIdx].EncodedBytes()); err != nil { + ifr.MoveToDraining(err) + return ifrStateUnknown, ifr.DrainHelper() + } + if err = ifr.invertedEval.addIndexRow(keyIndex); err != nil { + ifr.MoveToDraining(err) + return ifrStateUnknown, ifr.DrainHelper() + } return ifrReadingInput, nil } diff --git a/pkg/sql/rowexec/inverted_filterer_test.go b/pkg/sql/rowexec/inverted_filterer_test.go index 99b0994c4719..6fc608b73e8d 100644 --- a/pkg/sql/rowexec/inverted_filterer_test.go +++ b/pkg/sql/rowexec/inverted_filterer_test.go @@ -20,19 +20,13 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" "github.com/cockroachdb/cockroach/pkg/sql/opt/invertedexpr" "github.com/cockroachdb/cockroach/pkg/sql/rowenc" - "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/cockroach/pkg/util/leaktest" ) func intToEncodedInvertedVal(v int64) []byte { - dint := tree.DInt(v) - encoded, err := rowenc.EncodeTableKey(nil, &dint, encoding.Ascending) - if err != nil { - panic("unable to encode int") - } - return encoded + return encoding.EncodeVarintAscending(nil, v) } func intSpanToEncodedSpan(start, end int64) invertedexpr.SpanExpressionProto_Span { diff --git a/pkg/sql/rowexec/inverted_joiner.go b/pkg/sql/rowexec/inverted_joiner.go index fee09ff1da4f..65f1a60267c4 100644 --- a/pkg/sql/rowexec/inverted_joiner.go +++ b/pkg/sql/rowexec/inverted_joiner.go @@ -105,6 +105,7 @@ type invertedJoiner struct { input execinfra.RowSource inputTypes []*types.T datumsToInvertedExpr invertedexpr.DatumsToInvertedExpr + canPreFilter bool // Batch size for fetches. Not a constant so we can lower for testing. batchSize int @@ -240,6 +241,10 @@ func newInvertedJoiner( return nil, err } } + ij.canPreFilter = ij.datumsToInvertedExpr.CanPreFilter() + if ij.canPreFilter { + ij.batchedExprEval.filterer = ij.datumsToInvertedExpr + } var fetcher row.Fetcher // In general we need all the columns in the index to compute the set @@ -384,7 +389,7 @@ func (ij *invertedJoiner) readInput() (invertedJoinerState, *execinfrapb.Produce break } - expr, err := ij.datumsToInvertedExpr.Convert(ij.Ctx, row) + expr, preFilterState, err := ij.datumsToInvertedExpr.Convert(ij.Ctx, row) if err != nil { ij.MoveToDraining(err) return ijStateUnknown, ij.DrainHelper() @@ -403,8 +408,14 @@ func (ij *invertedJoiner) readInput() (invertedJoinerState, *execinfrapb.Produce // The nil serves as a marker that will result in an empty set as the // evaluation result. ij.batchedExprEval.exprs = append(ij.batchedExprEval.exprs, nil) + if ij.canPreFilter { + ij.batchedExprEval.preFilterState = append(ij.batchedExprEval.preFilterState, nil) + } } else { ij.batchedExprEval.exprs = append(ij.batchedExprEval.exprs, expr) + if ij.canPreFilter { + ij.batchedExprEval.preFilterState = append(ij.batchedExprEval.preFilterState, preFilterState) + } } } @@ -460,13 +471,23 @@ func (ij *invertedJoiner) performScan() (invertedJoinerState, *execinfrapb.Produ break } encInvertedVal := scannedRow[ij.colIdxMap[ij.invertedColID]].EncodedBytes() - ij.transformToKeyRow(scannedRow) - rowIdx, err := ij.keyRows.AddRow(ij.Ctx, ij.keyRow) + shouldAdd, err := ij.batchedExprEval.prepareAddIndexRow(encInvertedVal) if err != nil { ij.MoveToDraining(err) return ijStateUnknown, ij.DrainHelper() } - ij.batchedExprEval.addIndexRow(encInvertedVal, rowIdx) + if shouldAdd { + ij.transformToKeyRow(scannedRow) + rowIdx, err := ij.keyRows.AddRow(ij.Ctx, ij.keyRow) + if err != nil { + ij.MoveToDraining(err) + return ijStateUnknown, ij.DrainHelper() + } + if err = ij.batchedExprEval.addIndexRow(rowIdx); err != nil { + ij.MoveToDraining(err) + return ijStateUnknown, ij.DrainHelper() + } + } } ij.joinedRowIdx = ij.batchedExprEval.evaluate() ij.keyRows.SetupForRead(ij.Ctx, ij.joinedRowIdx) diff --git a/pkg/sql/rowexec/inverted_joiner_test.go b/pkg/sql/rowexec/inverted_joiner_test.go index e6aab7afc55e..4f0ade108cc6 100644 --- a/pkg/sql/rowexec/inverted_joiner_test.go +++ b/pkg/sql/rowexec/inverted_joiner_test.go @@ -31,6 +31,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/testutils/distsqlutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/cockroach/pkg/util/json" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/errors" @@ -60,20 +61,54 @@ const numRows = 99 // index 5 (obviously), since the test used the same transformation to // generate the array for each row. And this is also the set for row index // 50, since 50%10 = 0, 50/10 = 5. -type arrayIntersectionExpr struct{} +type arrayIntersectionExpr struct { + t *testing.T + toExclude *struct { + left, right int64 + } +} var _ invertedexpr.DatumsToInvertedExpr = &arrayIntersectionExpr{} -func (arrayIntersectionExpr) Convert( - ctx context.Context, datums rowenc.EncDatumRow, -) (*invertedexpr.SpanExpressionProto, error) { +func decodeInvertedValToInt(b []byte) int64 { + b, val, err := encoding.DecodeVarintAscending(b) + if err != nil { + panic(err) + } + if len(b) > 0 { + panic("leftover bytes") + } + return val +} + +func (a arrayIntersectionExpr) Convert( + _ context.Context, datums rowenc.EncDatumRow, +) (*invertedexpr.SpanExpressionProto, interface{}, error) { d := int64(*(datums[0].Datum.(*tree.DInt))) d1Span := invertedexpr.MakeSingleInvertedValSpan(intToEncodedInvertedVal(d / 10)) d2Span := invertedexpr.MakeSingleInvertedValSpan(intToEncodedInvertedVal(d % 10)) // The tightness only affects the optimizer, so arbitrarily use true. expr := invertedexpr.And(invertedexpr.ExprForInvertedSpan(d1Span, true), invertedexpr.ExprForInvertedSpan(d2Span, true)) - return expr.(*invertedexpr.SpanExpression).ToProto(), nil + return expr.(*invertedexpr.SpanExpression).ToProto(), d, nil +} + +func (a arrayIntersectionExpr) CanPreFilter() bool { + return a.toExclude != nil +} + +func (a arrayIntersectionExpr) PreFilter( + enc invertedexpr.EncInvertedVal, preFilters []interface{}, result []bool, +) (bool, error) { + require.True(a.t, a.CanPreFilter()) + right := decodeInvertedValToInt(enc) + rv := false + for i := range preFilters { + left := preFilters[i].(int64) + result[i] = !(a.toExclude != nil && a.toExclude.left == left && a.toExclude.right == right) + rv = rv || result[i] + } + return rv, nil } // This expression converter is similar to the arrayIntersectionExpr, but for @@ -85,8 +120,8 @@ type jsonIntersectionExpr struct{} var _ invertedexpr.DatumsToInvertedExpr = &jsonIntersectionExpr{} func (jsonIntersectionExpr) Convert( - ctx context.Context, datums rowenc.EncDatumRow, -) (*invertedexpr.SpanExpressionProto, error) { + _ context.Context, datums rowenc.EncDatumRow, +) (*invertedexpr.SpanExpressionProto, interface{}, error) { d := int64(*(datums[0].Datum.(*tree.DInt))) d1 := d / 10 d2 := d % 10 @@ -106,7 +141,16 @@ func (jsonIntersectionExpr) Convert( // The tightness only affects the optimizer, so arbitrarily use false. expr := invertedexpr.And(invertedexpr.ExprForInvertedSpan(d1Span, false), invertedexpr.ExprForInvertedSpan(d2Span, false)) - return expr.(*invertedexpr.SpanExpression).ToProto(), nil + return expr.(*invertedexpr.SpanExpression).ToProto(), nil, nil +} + +func (jsonIntersectionExpr) CanPreFilter() bool { + return false +} +func (jsonIntersectionExpr) PreFilter( + _ invertedexpr.EncInvertedVal, _ []interface{}, _ []bool, +) (bool, error) { + return false, errors.Errorf("unsupported") } // For each integer d provided by the left side, this expression converter @@ -118,8 +162,8 @@ type jsonUnionExpr struct{} var _ invertedexpr.DatumsToInvertedExpr = &jsonUnionExpr{} func (jsonUnionExpr) Convert( - ctx context.Context, datums rowenc.EncDatumRow, -) (*invertedexpr.SpanExpressionProto, error) { + _ context.Context, datums rowenc.EncDatumRow, +) (*invertedexpr.SpanExpressionProto, interface{}, error) { d := int64(*(datums[0].Datum.(*tree.DInt))) d1 := d / 10 d2 := d % 10 @@ -139,7 +183,16 @@ func (jsonUnionExpr) Convert( // The tightness only affects the optimizer, so arbitrarily use true. expr := invertedexpr.Or(invertedexpr.ExprForInvertedSpan(d1Span, true), invertedexpr.ExprForInvertedSpan(d2Span, true)) - return expr.(*invertedexpr.SpanExpression).ToProto(), nil + return expr.(*invertedexpr.SpanExpression).ToProto(), nil, nil +} + +func (jsonUnionExpr) CanPreFilter() bool { + return false +} +func (jsonUnionExpr) PreFilter( + _ invertedexpr.EncInvertedVal, _ []interface{}, _ []bool, +) (bool, error) { + return false, errors.Errorf("unsupported") } func TestInvertedJoiner(t *testing.T) { @@ -204,11 +257,31 @@ func TestInvertedJoiner(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(tree.DInt(5))}, {tree.NewDInt(tree.DInt(20))}, {tree.NewDInt(tree.DInt(42))}, }, - datumsToExpr: arrayIntersectionExpr{}, + datumsToExpr: arrayIntersectionExpr{t: t}, joinType: descpb.InnerJoin, outputTypes: rowenc.TwoIntCols, expected: "[[5 5] [5 50] [20 2] [20 20] [42 24] [42 42]]", }, + { + description: "array intersection with pre-filter", + indexIdx: biIndex, + post: execinfrapb.PostProcessSpec{ + OutputColumns: []uint32{0, 1}, + }, + // Similar to above, but with a pre-filter that prevents a left row 5 + // from matching right rows with inverted key 0. Note that a right row 0 + // is also used for the left row 20, due to 20 % 10 = 0, but that won't + // be excluded. This causes left row 5 to not match anything. + input: [][]tree.Datum{ + {tree.NewDInt(tree.DInt(5))}, {tree.NewDInt(tree.DInt(20))}, {tree.NewDInt(tree.DInt(42))}, + }, + datumsToExpr: arrayIntersectionExpr{ + t: t, toExclude: &struct{ left, right int64 }{left: 5, right: 0}, + }, + joinType: descpb.InnerJoin, + outputTypes: rowenc.TwoIntCols, + expected: "[[20 2] [20 20] [42 24] [42 42]]", + }, { // This case is similar to the "array intersection" case, and uses the // same input, but additionally filters to only include right rows with @@ -224,7 +297,7 @@ func TestInvertedJoiner(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(tree.DInt(5))}, {tree.NewDInt(tree.DInt(20))}, {tree.NewDInt(tree.DInt(42))}, }, - datumsToExpr: arrayIntersectionExpr{}, + datumsToExpr: arrayIntersectionExpr{t: t}, joinType: descpb.InnerJoin, outputTypes: rowenc.TwoIntCols, expected: "[[5 50] [42 24] [42 42]]", @@ -242,7 +315,7 @@ func TestInvertedJoiner(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(tree.DInt(5))}, {tree.NewDInt(tree.DInt(20))}, {tree.NewDInt(tree.DInt(42))}, }, - datumsToExpr: arrayIntersectionExpr{}, + datumsToExpr: arrayIntersectionExpr{t: t}, joinType: descpb.LeftOuterJoin, outputTypes: rowenc.TwoIntCols, // Similar to previous, but the left side input failing the onExpr is emitted. @@ -260,7 +333,7 @@ func TestInvertedJoiner(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(tree.DInt(5))}, {tree.NewDInt(tree.DInt(20))}, {tree.NewDInt(tree.DInt(42))}, }, - datumsToExpr: arrayIntersectionExpr{}, + datumsToExpr: arrayIntersectionExpr{t: t}, joinType: descpb.LeftSemiJoin, outputTypes: rowenc.OneIntCol, expected: "[[5] [42]]", @@ -277,7 +350,7 @@ func TestInvertedJoiner(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(tree.DInt(5))}, {tree.NewDInt(tree.DInt(20))}, {tree.NewDInt(tree.DInt(42))}, }, - datumsToExpr: arrayIntersectionExpr{}, + datumsToExpr: arrayIntersectionExpr{t: t}, joinType: descpb.LeftAntiJoin, outputTypes: rowenc.OneIntCol, expected: "[[20]]", diff --git a/pkg/util/encoding/encoding.go b/pkg/util/encoding/encoding.go index 8b1ee97d0291..56a36d43cf5a 100644 --- a/pkg/util/encoding/encoding.go +++ b/pkg/util/encoding/encoding.go @@ -97,7 +97,8 @@ const ( arrayKeyMarker = geoDescMarker + 1 arrayKeyDescendingMarker = arrayKeyMarker + 1 - box2DMarker = arrayKeyDescendingMarker + 1 + box2DMarker = arrayKeyDescendingMarker + 1 + geoInvertedIndexMarker = box2DMarker + 1 arrayKeyTerminator byte = 0x00 arrayKeyDescendingTerminator byte = 0xFF @@ -829,6 +830,98 @@ func EncodeJSONAscending(b []byte) []byte { return append(b, jsonInvertedIndex) } +// Geo inverted keys are formatted as: +// geoInvertedIndexMarker + EncodeUvarintAscending(cellid) + encoded-bbox +// We don't have a single function to do the whole encoding since a shape is typically +// indexed under multiple cellids, but has a single bbox. So the caller can more +// efficiently +// - append geoInvertedIndex to construct the prefix. +// - encode the bbox once +// - iterate over the cellids and append the encoded cellid to the prefix and then the +// previously encoded bbox. + +// EncodeGeoInvertedAscending appends the geoInvertedIndexMarker. +func EncodeGeoInvertedAscending(b []byte) []byte { + return append(b, geoInvertedIndexMarker) +} + +// Currently only the lowest bit is used to define the encoding kind and the +// remaining 7 bits are unused. +type geoInvertedBBoxEncodingKind byte + +const ( + geoInvertedFourFloats geoInvertedBBoxEncodingKind = iota + geoInvertedTwoFloats +) + +// MaxGeoInvertedBBoxLen is the maximum length of the encoded bounding box for +// geo inverted keys. +const MaxGeoInvertedBBoxLen = 1 + 4*uint64AscendingEncodedLength + +// EncodeGeoInvertedBBox encodes the bounding box for the geo inverted index. +func EncodeGeoInvertedBBox(b []byte, loX, loY, hiX, hiY float64) []byte { + encodeTwoFloats := loX == hiX && loY == hiY + if encodeTwoFloats { + b = append(b, byte(geoInvertedTwoFloats)) + b = EncodeUntaggedFloatValue(b, loX) + b = EncodeUntaggedFloatValue(b, loY) + } else { + b = append(b, byte(geoInvertedFourFloats)) + b = EncodeUntaggedFloatValue(b, loX) + b = EncodeUntaggedFloatValue(b, loY) + b = EncodeUntaggedFloatValue(b, hiX) + b = EncodeUntaggedFloatValue(b, hiY) + } + return b +} + +// DecodeGeoInvertedKey decodes the bounding box from the geo inverted key. +// The cellid is skipped in the decoding. +func DecodeGeoInvertedKey(b []byte) (loX, loY, hiX, hiY float64, remaining []byte, err error) { + // Minimum: 1 byte marker + 1 byte cell length + + // 1 byte bbox encoding kind + 16 bytes for 2 floats + if len(b) < 3+2*uint64AscendingEncodedLength { + return 0, 0, 0, 0, b, + errors.Errorf("inverted key length %d too small", len(b)) + } + if b[0] != geoInvertedIndexMarker { + return 0, 0, 0, 0, b, errors.Errorf("marker is not geoInvertedIndexMarker") + } + b = b[1:] + var cellLen int + if cellLen, err = getVarintLen(b); err != nil { + return 0, 0, 0, 0, b, err + } + if len(b) < cellLen+17 { + return 0, 0, 0, 0, b, + errors.Errorf("insufficient length for encoded bbox in inverted key: %d", len(b)-cellLen) + } + encodingKind := geoInvertedBBoxEncodingKind(b[cellLen]) + if encodingKind != geoInvertedTwoFloats && encodingKind != geoInvertedFourFloats { + return 0, 0, 0, 0, b, + errors.Errorf("unknown encoding kind for bbox in inverted key: %d", encodingKind) + } + b = b[cellLen+1:] + if b, loX, err = DecodeUntaggedFloatValue(b); err != nil { + return 0, 0, 0, 0, b, err + } + if b, loY, err = DecodeUntaggedFloatValue(b); err != nil { + return 0, 0, 0, 0, b, err + } + if encodingKind == geoInvertedFourFloats { + if b, hiX, err = DecodeUntaggedFloatValue(b); err != nil { + return 0, 0, 0, 0, b, err + } + if b, hiY, err = DecodeUntaggedFloatValue(b); err != nil { + return 0, 0, 0, 0, b, err + } + } else { + hiX = loX + hiY = loY + } + return loX, loY, hiX, hiY, b, nil +} + // EncodeNullDescending is the descending equivalent of EncodeNullAscending. func EncodeNullDescending(b []byte) []byte { return append(b, encodedNullDesc) @@ -1672,6 +1765,8 @@ func PeekLength(b []byte) (int, error) { return 0, err } return 1 + length, nil + case geoInvertedIndexMarker: + return getGeoInvertedIndexKeyLength(b) case geoMarker: // Expect to reserve at least 8 bytes for int64. if len(b) < 8 { @@ -2994,6 +3089,25 @@ func getJSONInvertedIndexKeyLength(buf []byte) (int, error) { } } +func getGeoInvertedIndexKeyLength(buf []byte) (int, error) { + // Minimum: 1 byte marker + 1 byte cell length + + // 1 byte bbox encoding kind + 16 bytes for 2 floats + if len(buf) < 3+2*uint64AscendingEncodedLength { + return 0, errors.Errorf("buf length %d too small", len(buf)) + } + var cellLen int + var err error + if cellLen, err = getVarintLen(buf[1:]); err != nil { + return 0, err + } + encodingKind := geoInvertedBBoxEncodingKind(buf[1+cellLen]) + floatsLen := 4 * uint64AscendingEncodedLength + if encodingKind == geoInvertedTwoFloats { + floatsLen = 2 * uint64AscendingEncodedLength + } + return 1 + cellLen + 1 + floatsLen, nil +} + // EncodeArrayKeyMarker adds the array key encoding marker to buf and // returns the new buffer. func EncodeArrayKeyMarker(buf []byte, dir Direction) []byte { diff --git a/pkg/util/encoding/encoding_test.go b/pkg/util/encoding/encoding_test.go index aa7ef84a0ae0..dfc4284c50ab 100644 --- a/pkg/util/encoding/encoding_test.go +++ b/pkg/util/encoding/encoding_test.go @@ -1317,6 +1317,63 @@ func TestEncodeDecodeGeography(t *testing.T) { } } +func TestEncodeDecodeGeoInvertedIndex(t *testing.T) { + testCases := []struct { + shape string + cellID uint64 + expectedLength int + }{ + { + shape: "SRID=4326;LINESTRING(0 0, -90 -80)", + cellID: 0, + expectedLength: 35, + }, + { + shape: "SRID=4326;LINESTRING(0 0, -90 -80)", + cellID: math.MaxUint64, + expectedLength: 43, + }, + { + shape: "SRID=4326;POINT(-80 80)", + cellID: 0, + expectedLength: 19, + }, + { + shape: "SRID=4326;POINT(-80 80)", + cellID: math.MaxUint64, + expectedLength: 27, + }, + { + shape: "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))", + cellID: 10000, + expectedLength: 37, + }, + { + shape: "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))", + cellID: math.MaxUint32, + expectedLength: 39, + }, + } + for i, tc := range testCases { + t.Run(strconv.Itoa(i+1), func(t *testing.T) { + parsed, err := geo.ParseGeometry(tc.shape) + require.NoError(t, err) + var b []byte + b = EncodeGeoInvertedAscending(b) + b = EncodeUvarintAscending(b, tc.cellID) + bbox := parsed.BoundingBoxRef() + require.NotNil(t, bbox) + b = EncodeGeoInvertedBBox(b, bbox.LoX, bbox.LoY, bbox.HiX, bbox.HiY) + require.Equal(t, tc.expectedLength, len(b)) + var dBBox geopb.BoundingBox + dBBox.LoX, dBBox.LoY, dBBox.HiX, dBBox.HiY, b, err = DecodeGeoInvertedKey(b) + require.NoError(t, err) + require.Equal(t, *bbox, dBBox) + require.Equal(t, 0, len(b)) + }) + } +} + type testCaseDuration struct { value duration.Duration expEnc []byte