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 fee3587a18d5..f1cc94a94495 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..2beecafb8995 100644 --- a/pkg/sql/opt/invertedexpr/expression.go +++ b/pkg/sql/opt/invertedexpr/expression.go @@ -27,9 +27,44 @@ 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 return an interface{}. 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..a9ed7e917575 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,170 @@ 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.Errorf("datum of unhandled type: ", 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 values against a set of +// pre-filter states. The function signature matches the PreFilter function of +// the DatumsToInvertedExpr 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 +822,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 +886,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 +914,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 +944,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 +968,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 +1028,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 +1040,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/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 d29133158d23..08b86f7cb83d 100644 --- a/pkg/sql/opt/xform/testdata/rules/join +++ b/pkg/sql/opt/xform/testdata/rules/join @@ -3441,12 +3441,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 6a2dac012179..531e9140a41e 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 7bc024f82c0d..db10512d1fe5 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