diff --git a/pkg/geo/errors.go b/pkg/geo/errors.go index cac23023005a..4c09c9d86302 100644 --- a/pkg/geo/errors.go +++ b/pkg/geo/errors.go @@ -10,7 +10,11 @@ package geo -import "github.com/cockroachdb/errors" +import ( + "fmt" + + "github.com/cockroachdb/errors" +) // NewMismatchingSRIDsError returns the error message for SRIDs of GeospatialTypes // a and b being a mismatch. @@ -23,3 +27,42 @@ func NewMismatchingSRIDsError(a GeospatialType, b GeospatialType) error { b.SRID(), ) } + +// EmptyGeometryError is an error that is returned when the Geometry or any +// parts of its subgeometries are empty. +type EmptyGeometryError struct { + cause error +} + +var _ error = (*EmptyGeometryError)(nil) +var _ errors.SafeDetailer = (*EmptyGeometryError)(nil) +var _ fmt.Formatter = (*EmptyGeometryError)(nil) +var _ errors.Formatter = (*EmptyGeometryError)(nil) + +// Error implements the error interface. +func (w *EmptyGeometryError) Error() string { return w.cause.Error() } + +// Cause implements the errors.SafeDetailer interface. +func (w *EmptyGeometryError) Cause() error { return w.cause } + +// Unwrap implements the SafeDetailer interface. +func (w *EmptyGeometryError) Unwrap() error { return w.cause } + +// SafeDetails implements the SafeDetailer interface. +func (w *EmptyGeometryError) SafeDetails() []string { return []string{w.cause.Error()} } + +// Format implements the errors.Formatter interface. +func (w *EmptyGeometryError) Format(s fmt.State, verb rune) { errors.FormatError(w, s, verb) } + +// FormatError implements the errors.Formatter interface. +func (w *EmptyGeometryError) FormatError(p errors.Printer) (next error) { return w.cause } + +// IsEmptyGeometryError returns true if the error is of type EmptyGeometryError. +func IsEmptyGeometryError(err error) bool { + return errors.HasType(err, &EmptyGeometryError{}) +} + +// NewEmptyGeometryError returns an error indicating an empty geometry has been found. +func NewEmptyGeometryError() *EmptyGeometryError { + return &EmptyGeometryError{cause: errors.Newf("empty shape found")} +} diff --git a/pkg/geo/geo.go b/pkg/geo/geo.go index 7dc4d799c55b..1b2584f2a642 100644 --- a/pkg/geo/geo.go +++ b/pkg/geo/geo.go @@ -24,6 +24,17 @@ import ( // DefaultEWKBEncodingFormat is the default encoding format for EWKB. var DefaultEWKBEncodingFormat = binary.LittleEndian +// EmptyBehavior is the behavior to adopt when an empty Geometry is encountered. +type EmptyBehavior uint8 + +const ( + // EmptyBehaviorError will error with EmptyGeometryError when an empty geometry + // is encountered. + EmptyBehaviorError EmptyBehavior = 0 + // EmptyBehaviorOmit will omit an entry when an empty geometry is encountered. + EmptyBehaviorOmit EmptyBehavior = 1 +) + // // Geospatial Type // @@ -353,14 +364,14 @@ func (g *Geography) Shape() geopb.Shape { return g.SpatialObject.Shape } -// AsS2 converts a given Geography into its S2 form. -func (g *Geography) AsS2() ([]s2.Region, error) { +// AsS2 converts a given Geography into it's S2 form. +func (g *Geography) AsS2(emptyBehavior EmptyBehavior) ([]s2.Region, error) { geomRepr, err := g.AsGeomT() if err != nil { return nil, err } // TODO(otan): convert by reading from EWKB to S2 directly. - return S2RegionsFromGeom(geomRepr), nil + return S2RegionsFromGeom(geomRepr, emptyBehavior) } // isLinearRingCCW returns whether a given linear ring is counter clock wise. @@ -407,8 +418,20 @@ func isLinearRingCCW(linearRing *geom.LinearRing) bool { // S2RegionsFromGeom converts an geom representation of an object // to s2 regions. -func S2RegionsFromGeom(geomRepr geom.T) []s2.Region { +// As S2 does not really handle empty geometries well, we need to ingest emptyBehavior and +// react appropriately. +func S2RegionsFromGeom(geomRepr geom.T, emptyBehavior EmptyBehavior) ([]s2.Region, error) { var regions []s2.Region + if geomRepr.Empty() { + switch emptyBehavior { + case EmptyBehaviorOmit: + return nil, nil + case EmptyBehaviorError: + return nil, NewEmptyGeometryError() + default: + return nil, errors.Newf("programmer error: unknown behavior") + } + } switch repr := geomRepr.(type) { case *geom.Point: regions = []s2.Region{ @@ -446,22 +469,38 @@ func S2RegionsFromGeom(geomRepr geom.T) []s2.Region { } case *geom.GeometryCollection: for _, geom := range repr.Geoms() { - regions = append(regions, S2RegionsFromGeom(geom)...) + subRegions, err := S2RegionsFromGeom(geom, emptyBehavior) + if err != nil { + return nil, err + } + regions = append(regions, subRegions...) } case *geom.MultiPoint: for i := 0; i < repr.NumPoints(); i++ { - regions = append(regions, S2RegionsFromGeom(repr.Point(i))...) + subRegions, err := S2RegionsFromGeom(repr.Point(i), emptyBehavior) + if err != nil { + return nil, err + } + regions = append(regions, subRegions...) } case *geom.MultiLineString: for i := 0; i < repr.NumLineStrings(); i++ { - regions = append(regions, S2RegionsFromGeom(repr.LineString(i))...) + subRegions, err := S2RegionsFromGeom(repr.LineString(i), emptyBehavior) + if err != nil { + return nil, err + } + regions = append(regions, subRegions...) } case *geom.MultiPolygon: for i := 0; i < repr.NumPolygons(); i++ { - regions = append(regions, S2RegionsFromGeom(repr.Polygon(i))...) + subRegions, err := S2RegionsFromGeom(repr.Polygon(i), emptyBehavior) + if err != nil { + return nil, err + } + regions = append(regions, subRegions...) } } - return regions + return regions, nil } // diff --git a/pkg/geo/geo_test.go b/pkg/geo/geo_test.go index e3e4d0d51908..4cf21e09577d 100644 --- a/pkg/geo/geo_test.go +++ b/pkg/geo/geo_test.go @@ -421,12 +421,58 @@ func TestGeographyAsS2(t *testing.T) { g, err := ParseGeography(tc.wkt) require.NoError(t, err) - shapes, err := g.AsS2() + shapes, err := g.AsS2(EmptyBehaviorError) require.NoError(t, err) require.Equal(t, tc.expected, shapes) }) } + + // Test when things are empty. + emptyTestCases := []struct { + wkt string + expectedOmit []s2.Region + }{ + { + "GEOMETRYCOLLECTION ( LINESTRING EMPTY, MULTIPOINT((1.0 5.0), (3.0 4.0)) )", + []s2.Region{ + s2.PointFromLatLng(s2.LatLngFromDegrees(5.0, 1.0)), + s2.PointFromLatLng(s2.LatLngFromDegrees(4.0, 3.0)), + }, + }, + { + "GEOMETRYCOLLECTION EMPTY", + nil, + }, + { + "MULTILINESTRING (EMPTY, (1.0 2.0, 3.0 4.0))", + []s2.Region{ + s2.PolylineFromLatLngs([]s2.LatLng{ + s2.LatLngFromDegrees(2.0, 1.0), + s2.LatLngFromDegrees(4.0, 3.0), + }), + }, + }, + { + "MULTILINESTRING (EMPTY, EMPTY)", + nil, + }, + } + + for _, tc := range emptyTestCases { + t.Run(tc.wkt, func(t *testing.T) { + g, err := ParseGeography(tc.wkt) + require.NoError(t, err) + + _, err = g.AsS2(EmptyBehaviorError) + require.Error(t, err) + require.True(t, IsEmptyGeometryError(err)) + + shapes, err := g.AsS2(EmptyBehaviorOmit) + require.NoError(t, err) + require.Equal(t, tc.expectedOmit, shapes) + }) + } } func TestClipEWKBByRect(t *testing.T) { diff --git a/pkg/geo/geogfn/covers.go b/pkg/geo/geogfn/covers.go index 3bed52d8b801..8989ea0a9a54 100644 --- a/pkg/geo/geogfn/covers.go +++ b/pkg/geo/geogfn/covers.go @@ -48,12 +48,17 @@ func Covers(a *geo.Geography, b *geo.Geography) (bool, error) { // covers is the internal calculation for Covers. func covers(a *geo.Geography, b *geo.Geography) (bool, error) { - aRegions, err := a.AsS2() + // Ignore EMPTY regions in a. + aRegions, err := a.AsS2(geo.EmptyBehaviorOmit) if err != nil { return false, err } - bRegions, err := b.AsS2() + // If any of b is empty, we cannot cover it. Error and catch to return false. + bRegions, err := b.AsS2(geo.EmptyBehaviorError) if err != nil { + if geo.IsEmptyGeometryError(err) { + return false, nil + } return false, err } diff --git a/pkg/geo/geogfn/covers_test.go b/pkg/geo/geogfn/covers_test.go index a3e6237aa0cb..4a0470dea339 100644 --- a/pkg/geo/geogfn/covers_test.go +++ b/pkg/geo/geogfn/covers_test.go @@ -354,6 +354,30 @@ func TestCovers(t *testing.T) { "MULTIPOINT((0.5 0.5), (1.5 0.5))'", true, }, + { + "EMPTY GEOMETRYCOLLECTION does not cover itself", + "GEOMETRYCOLLECTION EMPTY", + "GEOMETRYCOLLECTION EMPTY", + false, + }, + { + "nothing covers an empty GEOMETRYCOLLECTION", + "POINT(1.0 1.0)", + "GEOMETRYCOLLECTION EMPTY", + false, + }, + { + "nothing covers a GEOMETRYCOLLECTION with an EMPTY element", + "POINT(1.0 1.0)", + "GEOMETRYCOLLECTION EMPTY", + false, + }, + { + "empty collection contains point which covers another", + "GEOMETRYCOLLECTION(LINESTRING EMPTY, POINT(1.0 2.0))", + "POINT(1.0 2.0)", + true, + }, } for _, tc := range testCases { diff --git a/pkg/geo/geogfn/distance.go b/pkg/geo/geogfn/distance.go index 0370d6ca2227..23a3ca1dadc7 100644 --- a/pkg/geo/geogfn/distance.go +++ b/pkg/geo/geogfn/distance.go @@ -22,6 +22,7 @@ import ( ) // Distance returns the distance between geographies a and b on a sphere or spheroid. +// Returns a geo.EmptyGeometryError if any of the Geographies are EMPTY. func Distance( a *geo.Geography, b *geo.Geography, useSphereOrSpheroid UseSphereOrSpheroid, ) (float64, error) { @@ -29,11 +30,11 @@ func Distance( return 0, geo.NewMismatchingSRIDsError(a, b) } - aRegions, err := a.AsS2() + aRegions, err := a.AsS2(geo.EmptyBehaviorError) if err != nil { return 0, err } - bRegions, err := b.AsS2() + bRegions, err := b.AsS2(geo.EmptyBehaviorError) if err != nil { return 0, err } diff --git a/pkg/geo/geogfn/distance_test.go b/pkg/geo/geogfn/distance_test.go index 986a8ae6f17e..f8ee0270dc5b 100644 --- a/pkg/geo/geogfn/distance_test.go +++ b/pkg/geo/geogfn/distance_test.go @@ -11,6 +11,7 @@ package geogfn import ( + "fmt" "math" "testing" @@ -270,4 +271,30 @@ func TestDistance(t *testing.T) { _, err := Distance(mismatchingSRIDGeographyA, mismatchingSRIDGeographyB, UseSpheroid) requireMismatchingSRIDError(t, err) }) + + t.Run("empty geographies always error", func(t *testing.T) { + for _, tc := range []struct { + a string + b string + }{ + {"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION EMPTY"}, + {"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION (POINT(1.0 1.0), LINESTRING EMPTY)"}, + {"POINT(1.0 1.0)", "GEOMETRYCOLLECTION (POINT(1.0 1.0), LINESTRING EMPTY)"}, + } { + for _, useSphereOrSpheroid := range []UseSphereOrSpheroid{ + UseSphere, + UseSpheroid, + } { + t.Run(fmt.Sprintf("Distance(%s,%s),spheroid=%t", tc.a, tc.b, useSphereOrSpheroid), func(t *testing.T) { + a, err := geo.ParseGeography(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeography(tc.b) + require.NoError(t, err) + _, err = Distance(a, b, useSphereOrSpheroid) + require.Error(t, err) + require.True(t, geo.IsEmptyGeometryError(err)) + }) + } + } + }) } diff --git a/pkg/geo/geogfn/dwithin.go b/pkg/geo/geogfn/dwithin.go index 9d70dd094910..74611da37110 100644 --- a/pkg/geo/geogfn/dwithin.go +++ b/pkg/geo/geogfn/dwithin.go @@ -19,6 +19,7 @@ import ( ) // DWithin returns whether a is within distance d of b, i.e. Distance(a, b) <= d. +// If A or B contains empty Geography objects, this will return false. func DWithin( a *geo.Geography, b *geo.Geography, distance float64, useSphereOrSpheroid UseSphereOrSpheroid, ) (bool, error) { @@ -29,12 +30,18 @@ func DWithin( return false, errors.Newf("dwithin distance cannot be less than zero") } - aRegions, err := a.AsS2() + aRegions, err := a.AsS2(geo.EmptyBehaviorError) if err != nil { + if geo.IsEmptyGeometryError(err) { + return false, nil + } return false, err } - bRegions, err := b.AsS2() + bRegions, err := b.AsS2(geo.EmptyBehaviorError) if err != nil { + if geo.IsEmptyGeometryError(err) { + return false, nil + } return false, err } spheroid := geographiclib.WGS84Spheroid diff --git a/pkg/geo/geogfn/dwithin_test.go b/pkg/geo/geogfn/dwithin_test.go index a6f95813cbc7..fecd9766fb48 100644 --- a/pkg/geo/geogfn/dwithin_test.go +++ b/pkg/geo/geogfn/dwithin_test.go @@ -106,4 +106,30 @@ func TestDWithin(t *testing.T) { _, err := DWithin(geo.MustParseGeography("POINT(1.0 2.0)"), geo.MustParseGeography("POINT(3.0 4.0)"), -0.01, UseSpheroid) require.Error(t, err) }) + + t.Run("empty geographies are never dwithin each other", func(t *testing.T) { + for _, tc := range []struct { + a string + b string + }{ + {"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION EMPTY"}, + {"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION (POINT(1.0 1.0), LINESTRING EMPTY)"}, + {"POINT(1.0 1.0)", "GEOMETRYCOLLECTION (POINT(1.0 1.0), LINESTRING EMPTY)"}, // This case errors (in a bad way) in PostGIS. + } { + for _, useSphereOrSpheroid := range []UseSphereOrSpheroid{ + UseSphere, + UseSpheroid, + } { + t.Run(fmt.Sprintf("DWithin(%s,%s),spheroid=%t", tc.a, tc.b, useSphereOrSpheroid), func(t *testing.T) { + a, err := geo.ParseGeography(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeography(tc.b) + require.NoError(t, err) + dwithin, err := DWithin(a, b, 0, useSphereOrSpheroid) + require.NoError(t, err) + require.False(t, dwithin) + }) + } + } + }) } diff --git a/pkg/geo/geogfn/intersects.go b/pkg/geo/geogfn/intersects.go index 11cd9dc12c19..a9f8216b69ba 100644 --- a/pkg/geo/geogfn/intersects.go +++ b/pkg/geo/geogfn/intersects.go @@ -25,11 +25,11 @@ func Intersects(a *geo.Geography, b *geo.Geography) (bool, error) { return false, geo.NewMismatchingSRIDsError(a, b) } - aRegions, err := a.AsS2() + aRegions, err := a.AsS2(geo.EmptyBehaviorOmit) if err != nil { return false, err } - bRegions, err := b.AsS2() + bRegions, err := b.AsS2(geo.EmptyBehaviorOmit) if err != nil { return false, err } diff --git a/pkg/geo/geogfn/intersects_test.go b/pkg/geo/geogfn/intersects_test.go index 016c13618026..072e6ad3e42a 100644 --- a/pkg/geo/geogfn/intersects_test.go +++ b/pkg/geo/geogfn/intersects_test.go @@ -258,6 +258,24 @@ func TestIntersects(t *testing.T) { "MULTIPOLYGON (((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0)))", false, }, + { + "GEOMETRYCOLLECTION EMPTY do not intersect with each other", + "GEOMETRYCOLLECTION EMPTY", + "GEOMETRYCOLLECTION EMPTY", + false, + }, + { + "GEOMETRYCOLLECTION EMPTY do not intersect with a point", + "POINT(1.0 2.0)", + "GEOMETRYCOLLECTION EMPTY", + false, + }, + { + "GEOMETRYCOLLECTION EMPTY and POINT intersect", + "POINT(1.0 2.0)", + "GEOMETRYCOLLECTION (LINESTRING EMPTY, POINT(1.0 2.0))", + true, + }, } for _, tc := range testCases { diff --git a/pkg/geo/geogfn/unary_operators.go b/pkg/geo/geogfn/unary_operators.go index 86b14a46ceac..e5ce3ccf7c05 100644 --- a/pkg/geo/geogfn/unary_operators.go +++ b/pkg/geo/geogfn/unary_operators.go @@ -20,7 +20,7 @@ import ( // Area returns the area of a given Geography. func Area(g *geo.Geography, useSphereOrSpheroid UseSphereOrSpheroid) (float64, error) { - regions, err := g.AsS2() + regions, err := g.AsS2(geo.EmptyBehaviorOmit) if err != nil { return 0, err } @@ -63,7 +63,11 @@ func Perimeter(g *geo.Geography, useSphereOrSpheroid UseSphereOrSpheroid) (float default: return 0, nil } - return length(geo.S2RegionsFromGeom(gt), useSphereOrSpheroid) + regions, err := geo.S2RegionsFromGeom(gt, geo.EmptyBehaviorOmit) + if err != nil { + return 0, err + } + return length(regions, useSphereOrSpheroid) } // Length returns length of a given Geography. @@ -79,7 +83,11 @@ func Length(g *geo.Geography, useSphereOrSpheroid UseSphereOrSpheroid) (float64, default: return 0, nil } - return length(geo.S2RegionsFromGeom(gt), useSphereOrSpheroid) + regions, err := geo.S2RegionsFromGeom(gt, geo.EmptyBehaviorOmit) + if err != nil { + return 0, err + } + return length(regions, useSphereOrSpheroid) } // length returns the sum of the lengtsh and perimeters in the shapes of the Geography. diff --git a/pkg/geo/geogfn/unary_operators_test.go b/pkg/geo/geogfn/unary_operators_test.go index 9090c790c3ae..6149ad6762cb 100644 --- a/pkg/geo/geogfn/unary_operators_test.go +++ b/pkg/geo/geogfn/unary_operators_test.go @@ -99,6 +99,32 @@ var unaryOperatorTestCases = []struct { expectedPerimeter: 9632838.874863794, }, }, + { + wkt: "GEOMETRYCOLLECTION (MULTIPOINT EMPTY, POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))", + sphere: unaryOperatorExpectedResult{ + expectedArea: 691570576619.521, + expectedLength: 9637039.459995955, + expectedPerimeter: 9637039.459995955, + }, + spheroid: unaryOperatorExpectedResult{ + expectedArea: 691638769184.1753, + expectedLength: 9632838.874863794, + expectedPerimeter: 9632838.874863794, + }, + }, + { + wkt: "GEOMETRYCOLLECTION EMPTY", + sphere: unaryOperatorExpectedResult{ + expectedArea: 0, + expectedLength: 0, + expectedPerimeter: 0, + }, + spheroid: unaryOperatorExpectedResult{ + expectedArea: 0, + expectedLength: 0, + expectedPerimeter: 0, + }, + }, } func TestArea(t *testing.T) { diff --git a/pkg/geo/geoindex/s2_geography_index.go b/pkg/geo/geoindex/s2_geography_index.go index 67f88ed77874..77d67181e190 100644 --- a/pkg/geo/geoindex/s2_geography_index.go +++ b/pkg/geo/geoindex/s2_geography_index.go @@ -51,7 +51,7 @@ func DefaultGeographyIndexConfig() *Config { // InvertedIndexKeys implements the GeographyIndex interface. func (i *s2GeographyIndex) InvertedIndexKeys(c context.Context, g *geo.Geography) ([]Key, error) { - r, err := g.AsS2() + r, err := g.AsS2(geo.EmptyBehaviorOmit) if err != nil { return nil, err } @@ -60,7 +60,7 @@ func (i *s2GeographyIndex) InvertedIndexKeys(c context.Context, g *geo.Geography // Covers implements the GeographyIndex interface. func (i *s2GeographyIndex) Covers(c context.Context, g *geo.Geography) (UnionKeySpans, error) { - r, err := g.AsS2() + r, err := g.AsS2(geo.EmptyBehaviorOmit) if err != nil { return nil, err } @@ -69,7 +69,7 @@ func (i *s2GeographyIndex) Covers(c context.Context, g *geo.Geography) (UnionKey // CoveredBy implements the GeographyIndex interface. func (i *s2GeographyIndex) CoveredBy(c context.Context, g *geo.Geography) (RPKeyExpr, error) { - r, err := g.AsS2() + r, err := g.AsS2(geo.EmptyBehaviorOmit) if err != nil { return nil, err } @@ -78,7 +78,7 @@ func (i *s2GeographyIndex) CoveredBy(c context.Context, g *geo.Geography) (RPKey // Intersects implements the GeographyIndex interface. func (i *s2GeographyIndex) Intersects(c context.Context, g *geo.Geography) (UnionKeySpans, error) { - r, err := g.AsS2() + r, err := g.AsS2(geo.EmptyBehaviorOmit) if err != nil { return nil, err } @@ -86,7 +86,7 @@ func (i *s2GeographyIndex) Intersects(c context.Context, g *geo.Geography) (Unio } func (i *s2GeographyIndex) testingInnerCovering(g *geo.Geography) s2.CellUnion { - r, _ := g.AsS2() + r, _ := g.AsS2(geo.EmptyBehaviorOmit) if r == nil { return nil } diff --git a/pkg/geo/geomfn/distance.go b/pkg/geo/geomfn/distance.go index 226b75236a5b..81faa604810e 100644 --- a/pkg/geo/geomfn/distance.go +++ b/pkg/geo/geomfn/distance.go @@ -21,11 +21,12 @@ import ( ) // MinDistance returns the minimum distance between geometries A and B. +// This returns a geo.EmptyGeometryError if either A or B is EMPTY. func MinDistance(a *geo.Geometry, b *geo.Geometry) (float64, error) { if a.SRID() != b.SRID() { return 0, geo.NewMismatchingSRIDsError(a, b) } - return minDistanceInternal(a, b, 0) + return minDistanceInternal(a, b, 0, geo.EmptyBehaviorOmit) } // MaxDistance returns the maximum distance across every pair of points comprising @@ -34,7 +35,7 @@ func MaxDistance(a *geo.Geometry, b *geo.Geometry) (float64, error) { if a.SRID() != b.SRID() { return 0, geo.NewMismatchingSRIDsError(a, b) } - return maxDistanceInternal(a, b, math.MaxFloat64) + return maxDistanceInternal(a, b, math.MaxFloat64, geo.EmptyBehaviorOmit) } // DWithin determines if any part of geometry A is within D units of geometry B. @@ -45,8 +46,12 @@ func DWithin(a *geo.Geometry, b *geo.Geometry, d float64) (bool, error) { if d < 0 { return false, errors.Newf("dwithin distance cannot be less than zero") } - dist, err := minDistanceInternal(a, b, d) + dist, err := minDistanceInternal(a, b, d, geo.EmptyBehaviorError) if err != nil { + // In case of any empty geometries return false. + if geo.IsEmptyGeometryError(err) { + return false, nil + } return false, err } return dist <= d, nil @@ -61,8 +66,12 @@ func DFullyWithin(a *geo.Geometry, b *geo.Geometry, d float64) (bool, error) { if d < 0 { return false, errors.Newf("dwithin distance cannot be less than zero") } - dist, err := maxDistanceInternal(a, b, d) + dist, err := maxDistanceInternal(a, b, d, geo.EmptyBehaviorError) if err != nil { + // In case of any empty geometries return false. + if geo.IsEmptyGeometryError(err) { + return false, nil + } return false, err } return dist <= d, nil @@ -71,33 +80,45 @@ func DFullyWithin(a *geo.Geometry, b *geo.Geometry, d float64) (bool, error) { // maxDistanceInternal finds the maximum distance between two geometries. // We can re-use the same algorithm as min-distance, allowing skips of checks that involve // the interiors or intersections as those will always be less then the maximum min-distance. -func maxDistanceInternal(a *geo.Geometry, b *geo.Geometry, stopAfterGT float64) (float64, error) { +func maxDistanceInternal( + a *geo.Geometry, b *geo.Geometry, stopAfterGT float64, emptyBehavior geo.EmptyBehavior, +) (float64, error) { u := newGeomMaxDistanceUpdater(stopAfterGT) c := &geomDistanceCalculator{updater: u} - return distanceInternal(a, b, c) + return distanceInternal(a, b, c, emptyBehavior) } // minDistanceInternal finds the minimum distance between two geometries. // This implementation is done in-house, as compared to using GEOS. -func minDistanceInternal(a *geo.Geometry, b *geo.Geometry, stopAfterLE float64) (float64, error) { +func minDistanceInternal( + a *geo.Geometry, b *geo.Geometry, stopAfterLE float64, emptyBehavior geo.EmptyBehavior, +) (float64, error) { u := newGeomMinDistanceUpdater(stopAfterLE) c := &geomDistanceCalculator{updater: u} - return distanceInternal(a, b, c) + return distanceInternal(a, b, c, emptyBehavior) } // distanceInternal calculates the distance between two geometries using // the DistanceCalculator operator. +// If there are any EMPTY Geometry objects, they will be ignored. It will return an +// EmptyGeometryError if A or B contains only EMPTY geometries, even if emptyBehavior +// is set to EmptyBehaviorOmit. func distanceInternal( - a *geo.Geometry, b *geo.Geometry, c geodist.DistanceCalculator, + a *geo.Geometry, b *geo.Geometry, c geodist.DistanceCalculator, emptyBehavior geo.EmptyBehavior, ) (float64, error) { - aGeoms, err := flattenGeometry(a) + aGeoms, err := flattenGeometry(a, emptyBehavior) if err != nil { return 0, err } - bGeoms, err := flattenGeometry(b) + bGeoms, err := flattenGeometry(b, emptyBehavior) if err != nil { return 0, err } + // If either side has no geoms, then we error out. + if len(aGeoms) == 0 || len(bGeoms) == 0 { + return 0, geo.NewEmptyGeometryError() + } + for _, aGeom := range aGeoms { aGeodist, err := geomToGeodist(aGeom) if err != nil { diff --git a/pkg/geo/geomfn/distance_test.go b/pkg/geo/geomfn/distance_test.go index 72a1fcc92ed5..abc595ddafb6 100644 --- a/pkg/geo/geomfn/distance_test.go +++ b/pkg/geo/geomfn/distance_test.go @@ -195,6 +195,41 @@ var distanceTestCases = []struct { 0, 50, }, + { + "GEOMETRYCOLLECTION (POINT, EMPTY) with POINT", + "GEOMETRYCOLLECTION ( POINT(1.0 2.0), LINESTRING EMPTY )", + "POINT(1.0 2.0)", + 0, + 0, + }, + { + "GEOMETRYCOLLECTION (POINT, EMPTY) with DIFFERENT POINT", + "GEOMETRYCOLLECTION ( POINT(1.0 2.0), LINESTRING EMPTY )", + "POINT(1.0 3.0)", + 1, + 1, + }, +} + +// TODO(otan): delete after https://github.com/cockroachdb/cockroach/issues/49209 +var knownGEOSPanics = map[string]struct{}{ + "GEOMETRYCOLLECTION (POINT, EMPTY) with POINT": {}, + "GEOMETRYCOLLECTION (POINT, EMPTY) with DIFFERENT POINT": {}, +} + +var falseDWithinTestCases = map[string]struct{}{ + "GEOMETRYCOLLECTION (POINT, EMPTY) with POINT": {}, + "GEOMETRYCOLLECTION (POINT, EMPTY) with DIFFERENT POINT": {}, +} + +var emptyDistanceTestCases = []struct { + a string + b string +}{ + {"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION EMPTY"}, + {"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION (LINESTRING EMPTY)"}, + {"GEOMETRYCOLLECTION EMPTY", "POINT(1.0 1.0)"}, + {"POINT(1.0 1.0)", "GEOMETRYCOLLECTION EMPTY"}, } func TestMinDistance(t *testing.T) { @@ -215,19 +250,35 @@ func TestMinDistance(t *testing.T) { require.Equal(t, tc.expectedMinDistance, ret) // Check distance roughly the same as GEOS. - ret, err = geos.MinDistance(a.EWKB(), b.EWKB()) - require.NoError(t, err) - require.LessOrEqualf( - t, - math.Abs(tc.expectedMinDistance-ret), - 0.0000001, // GEOS and PostGIS/CRDB can return results close by. - "expected distance within %f, GEOS returns %f", - tc.expectedMinDistance, - ret, - ) + if _, panicsInGEOS := knownGEOSPanics[tc.desc]; !panicsInGEOS { + ret, err = geos.MinDistance(a.EWKB(), b.EWKB()) + require.NoError(t, err) + require.LessOrEqualf( + t, + math.Abs(tc.expectedMinDistance-ret), + 0.0000001, // GEOS and PostGIS/CRDB can return results close by. + "expected distance within %f, GEOS returns %f", + tc.expectedMinDistance, + ret, + ) + } }) } + t.Run("errors for EMPTY geometries", func(t *testing.T) { + for _, tc := range emptyDistanceTestCases { + t.Run(fmt.Sprintf("%s to %s", tc.a, tc.b), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + _, err = MinDistance(a, b) + require.Error(t, err) + require.True(t, geo.IsEmptyGeometryError(err)) + }) + } + }) + t.Run("errors if SRIDs mismatch", func(t *testing.T) { _, err := MinDistance(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB) requireMismatchingSRIDError(t, err) @@ -267,6 +318,12 @@ func TestDWithin(t *testing.T) { b, err := geo.ParseGeometry(tc.b) require.NoError(t, err) + // empty geometries should always return false. + expected := true + if _, ok := falseDWithinTestCases[tc.desc]; ok { + expected = false + } + for _, val := range []float64{ tc.expectedMinDistance, tc.expectedMinDistance + 0.1, @@ -276,11 +333,11 @@ func TestDWithin(t *testing.T) { t.Run(fmt.Sprintf("dwithin:%f", val), func(t *testing.T) { dwithin, err := DWithin(a, b, val) require.NoError(t, err) - require.True(t, dwithin) + require.Equal(t, expected, dwithin) dwithin, err = DWithin(a, b, val) require.NoError(t, err) - require.True(t, dwithin) + require.Equal(t, expected, dwithin) }) } @@ -304,6 +361,20 @@ func TestDWithin(t *testing.T) { }) } + t.Run("returns false for EMPTY geometries", func(t *testing.T) { + for _, tc := range emptyDistanceTestCases { + t.Run(fmt.Sprintf("%s to %s", tc.a, tc.b), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + dwithin, err := DWithin(a, b, 0) + require.NoError(t, err) + require.False(t, dwithin) + }) + } + }) + t.Run("errors if SRIDs mismatch", func(t *testing.T) { _, err := MinDistance(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB) requireMismatchingSRIDError(t, err) @@ -323,6 +394,12 @@ func TestDFullyWithin(t *testing.T) { b, err := geo.ParseGeometry(tc.b) require.NoError(t, err) + // empty geometries should always return false. + expected := true + if _, ok := falseDWithinTestCases[tc.desc]; ok { + expected = false + } + for _, val := range []float64{ tc.expectedMaxDistance, tc.expectedMaxDistance + 0.1, @@ -330,13 +407,13 @@ func TestDFullyWithin(t *testing.T) { tc.expectedMaxDistance * 2, } { t.Run(fmt.Sprintf("dfullywithin:%f", val), func(t *testing.T) { - dwithax, err := DFullyWithin(a, b, val) + dfullywithin, err := DFullyWithin(a, b, val) require.NoError(t, err) - require.True(t, dwithax) + require.Equal(t, expected, dfullywithin) - dwithax, err = DFullyWithin(a, b, val) + dfullywithin, err = DFullyWithin(a, b, val) require.NoError(t, err) - require.True(t, dwithax) + require.Equal(t, expected, dfullywithin) }) } @@ -347,13 +424,13 @@ func TestDFullyWithin(t *testing.T) { } { if val > 0 { t.Run(fmt.Sprintf("dfullywithin:%f", val), func(t *testing.T) { - dwithin, err := DFullyWithin(a, b, val) + dfullywithin, err := DFullyWithin(a, b, val) require.NoError(t, err) - require.False(t, dwithin) + require.False(t, dfullywithin) - dwithin, err = DFullyWithin(a, b, val) + dfullywithin, err = DFullyWithin(a, b, val) require.NoError(t, err) - require.False(t, dwithin) + require.False(t, dfullywithin) }) } } diff --git a/pkg/geo/geomfn/geomfn.go b/pkg/geo/geomfn/geomfn.go index dc3f2833efd0..8c7257bcd012 100644 --- a/pkg/geo/geomfn/geomfn.go +++ b/pkg/geo/geomfn/geomfn.go @@ -18,16 +18,26 @@ import ( ) // flattenGeometry flattens a geo.Geometry object. -func flattenGeometry(g *geo.Geometry) ([]geom.T, error) { +func flattenGeometry(g *geo.Geometry, emptyBehavior geo.EmptyBehavior) ([]geom.T, error) { f, err := g.AsGeomT() if err != nil { return nil, err } - return flattenGeomT(f) + return flattenGeomT(f, emptyBehavior) } // flattenGeomT decomposes geom.T collections to individual geom.T components. -func flattenGeomT(g geom.T) ([]geom.T, error) { +func flattenGeomT(g geom.T, emptyBehavior geo.EmptyBehavior) ([]geom.T, error) { + if g.Empty() { + switch emptyBehavior { + case geo.EmptyBehaviorOmit: + return nil, nil + case geo.EmptyBehaviorError: + return nil, geo.NewEmptyGeometryError() + default: + return nil, errors.Newf("programmer error: unknown behavior") + } + } switch g := g.(type) { case *geom.Point: return []geom.T{g}, nil @@ -54,9 +64,19 @@ func flattenGeomT(g geom.T) ([]geom.T, error) { } return ret, nil case *geom.GeometryCollection: - ret := make([]geom.T, g.NumGeoms()) - for i := 0; i < g.NumGeoms(); i++ { - ret[i] = g.Geom(i) + ret := make([]geom.T, 0, g.NumGeoms()) + for _, subG := range g.Geoms() { + if subG.Empty() { + switch emptyBehavior { + case geo.EmptyBehaviorOmit: + continue + case geo.EmptyBehaviorError: + return nil, geo.NewEmptyGeometryError() + default: + return nil, errors.Newf("programmer error: unknown behavior") + } + } + ret = append(ret, subG) } return ret, nil } diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index e86cafbdbb10..3edcce86f3f9 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -299,7 +299,10 @@ INSERT INTO geom_operators_test VALUES ('Point middle of Right Square', 'POINT(0.5 0.5)'), ('Square overlapping left and right square', 'POLYGON((-0.1 0.0, 1.0 0.0, 1.0 1.0, -0.1 1.0, -0.1 0.0))'), ('Line going through left and right square', 'LINESTRING(-0.5 0.5, 0.5 0.5)'), - ('Faraway point', 'POINT(5.0 5.0)') + ('Faraway point', 'POINT(5.0 5.0)'), + ('Empty LineString', 'LINESTRING EMPTY'), + ('Empty GeometryCollection', 'GEOMETRYCOLLECTION EMPTY') + -- ('Partially Empty GeometryCollection', 'GEOMETRYCOLLECTION ( LINESTRING EMPTY, POINT (0.0 0.0) )'): omitted, see: #49209 # Unary operators query TRRR @@ -311,6 +314,8 @@ SELECT FROM geom_operators_test a ORDER BY a.dsc ---- +Empty GeometryCollection 0 0 0 +Empty LineString 0 0 0 Faraway point 0 0 0 Line going through left and right square 0 1 0 NULL NULL NULL NULL @@ -331,6 +336,28 @@ FROM geom_operators_test a JOIN geom_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection NULL NULL +Empty GeometryCollection Empty LineString NULL NULL +Empty GeometryCollection Faraway point NULL NULL +Empty GeometryCollection Line going through left and right square NULL NULL +Empty GeometryCollection NULL NULL NULL +Empty GeometryCollection Point middle of Left Square NULL NULL +Empty GeometryCollection Point middle of Right Square NULL NULL +Empty GeometryCollection Square (left) NULL NULL +Empty GeometryCollection Square (right) NULL NULL +Empty GeometryCollection Square overlapping left and right square NULL NULL +Empty LineString Empty GeometryCollection NULL NULL +Empty LineString Empty LineString NULL NULL +Empty LineString Faraway point NULL NULL +Empty LineString Line going through left and right square NULL NULL +Empty LineString NULL NULL NULL +Empty LineString Point middle of Left Square NULL NULL +Empty LineString Point middle of Right Square NULL NULL +Empty LineString Square (left) NULL NULL +Empty LineString Square (right) NULL NULL +Empty LineString Square overlapping left and right square NULL NULL +Faraway point Empty GeometryCollection NULL NULL +Faraway point Empty LineString NULL NULL Faraway point Faraway point 0 0 Faraway point Line going through left and right square 6.36396103067893 7.10633520177595 Faraway point NULL NULL NULL @@ -339,6 +366,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) 6.40312423743285 7.81024967590665 Faraway point Square (right) 5.65685424949238 7.07106781186548 Faraway point Square overlapping left and right square 5.65685424949238 7.14212853426764 +Line going through left and right square Empty GeometryCollection NULL NULL +Line going through left and right square Empty LineString NULL NULL Line going through left and right square Faraway point 6.36396103067893 7.10633520177595 Line going through left and right square Line going through left and right square 0 1 Line going through left and right square NULL NULL NULL @@ -347,6 +376,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) 0 1.58113883008419 Line going through left and right square Square (right) 0 1.58113883008419 Line going through left and right square Square overlapping left and right square 0 1.58113883008419 +NULL Empty GeometryCollection NULL NULL +NULL Empty LineString NULL NULL NULL Faraway point NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL @@ -355,6 +386,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL Square (right) NULL NULL NULL Square overlapping left and right square NULL NULL +Point middle of Left Square Empty GeometryCollection NULL NULL +Point middle of Left Square Empty LineString NULL NULL Point middle of Left Square Faraway point 7.10633520177595 7.10633520177595 Point middle of Left Square Line going through left and right square 0 1 Point middle of Left Square NULL NULL NULL @@ -363,6 +396,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) 0 0.707106781186548 Point middle of Left Square Square (right) 0.5 1.58113883008419 Point middle of Left Square Square overlapping left and right square 0.4 1.58113883008419 +Point middle of Right Square Empty GeometryCollection NULL NULL +Point middle of Right Square Empty LineString NULL NULL Point middle of Right Square Faraway point 6.36396103067893 6.36396103067893 Point middle of Right Square Line going through left and right square 0 1 Point middle of Right Square NULL NULL NULL @@ -371,6 +406,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) 0.5 1.58113883008419 Point middle of Right Square Square (right) 0 0.707106781186548 Point middle of Right Square Square overlapping left and right square 0 0.781024967590665 +Square (left) Empty GeometryCollection NULL NULL +Square (left) Empty LineString NULL NULL Square (left) Faraway point 6.40312423743285 7.81024967590665 Square (left) Line going through left and right square 0 1.58113883008419 Square (left) NULL NULL NULL @@ -379,6 +416,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) 0 1.4142135623731 Square (left) Square (right) 0 2.23606797749979 Square (left) Square overlapping left and right square 0 2.23606797749979 +Square (right) Empty GeometryCollection NULL NULL +Square (right) Empty LineString NULL NULL Square (right) Faraway point 5.65685424949238 7.07106781186548 Square (right) Line going through left and right square 0 1.58113883008419 Square (right) NULL NULL NULL @@ -387,6 +426,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) 0 2.23606797749979 Square (right) Square (right) 0 1.4142135623731 Square (right) Square overlapping left and right square 0 1.48660687473185 +Square overlapping left and right square Empty GeometryCollection NULL NULL +Square overlapping left and right square Empty LineString NULL NULL Square overlapping left and right square Faraway point 5.65685424949238 7.14212853426764 Square overlapping left and right square Line going through left and right square 0 1.58113883008419 Square overlapping left and right square NULL NULL NULL @@ -415,6 +456,28 @@ FROM geom_operators_test a JOIN geom_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection false false false false false true false false false false +Empty GeometryCollection Empty LineString false false false false false true false false false false +Empty GeometryCollection Faraway point false false false false false false false false false false +Empty GeometryCollection Line going through left and right square false false false false false false false false false false +Empty GeometryCollection NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL +Empty GeometryCollection Point middle of Left Square false false false false false false false false false false +Empty GeometryCollection Point middle of Right Square false false false false false false false false false false +Empty GeometryCollection Square (left) false false false false false false false false false false +Empty GeometryCollection Square (right) false false false false false false false false false false +Empty GeometryCollection Square overlapping left and right square false false false false false false false false false false +Empty LineString Empty GeometryCollection false false false false false true false false false false +Empty LineString Empty LineString false false false false false true false false false false +Empty LineString Faraway point false false false false false false false false false false +Empty LineString Line going through left and right square false false false false false false false false false false +Empty LineString NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL +Empty LineString Point middle of Left Square false false false false false false false false false false +Empty LineString Point middle of Right Square false false false false false false false false false false +Empty LineString Square (left) false false false false false false false false false false +Empty LineString Square (right) false false false false false false false false false false +Empty LineString Square overlapping left and right square false false false false false false false false false false +Faraway point Empty GeometryCollection false false false false false false false false false false +Faraway point Empty LineString false false false false false false false false false false Faraway point Faraway point true true true true false true true false false true Faraway point Line going through left and right square false false false false false false false false false false Faraway point NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -423,6 +486,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) false false false false false false false false false false Faraway point Square (right) false false false false false false false false false false Faraway point Square overlapping left and right square false false false false false false false false false false +Line going through left and right square Empty GeometryCollection false false false false false false false false false false +Line going through left and right square Empty LineString false false false false false false false false false false Line going through left and right square Faraway point false false false false false false false false false false Line going through left and right square Line going through left and right square true true true false false true true false false true Line going through left and right square NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -431,6 +496,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) false false false false true false true false false false Line going through left and right square Square (right) false false false false true false true false false false Line going through left and right square Square overlapping left and right square false false false false true false true false false false +NULL Empty GeometryCollection NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL +NULL Empty LineString NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Faraway point NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -439,6 +506,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Square (right) NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Square overlapping left and right square NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL +Point middle of Left Square Empty GeometryCollection false false false false false false false false false false +Point middle of Left Square Empty LineString false false false false false false false false false false Point middle of Left Square Faraway point false false false false false false false false false false Point middle of Left Square Line going through left and right square false true false false false false true false true false Point middle of Left Square NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -447,6 +516,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) false true false false false false true false false true Point middle of Left Square Square (right) false false false false false false false false false false Point middle of Left Square Square overlapping left and right square false false false false false false false false false false +Point middle of Right Square Empty GeometryCollection false false false false false false false false false false +Point middle of Right Square Empty LineString false false false false false false false false false false Point middle of Right Square Faraway point false false false false false false false false false false Point middle of Right Square Line going through left and right square false true false false false false true false true false Point middle of Right Square NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -455,6 +526,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) false false false false false false false false false false Point middle of Right Square Square (right) false true false false false false true false false true Point middle of Right Square Square overlapping left and right square false true false false false false true false false true +Square (left) Empty GeometryCollection false false false false false false false false false false +Square (left) Empty LineString false false false false false false false false false false Square (left) Faraway point false false false false false false false false false false Square (left) Line going through left and right square false false false false true false true false false false Square (left) NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -463,6 +536,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) true true true false false true true false false true Square (left) Square (right) false false false false false false true false true false Square (left) Square overlapping left and right square false false false false false false true true false false +Square (right) Empty GeometryCollection false false false false false false false false false false +Square (right) Empty LineString false false false false false false false false false false Square (right) Faraway point false false false false false false false false false false Square (right) Line going through left and right square false false false false true false true false false false Square (right) NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -471,6 +546,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) false false false false false false true false true false Square (right) Square (right) true true true false false true true false false true Square (right) Square overlapping left and right square false true false false false false true false false true +Square overlapping left and right square Empty GeometryCollection false false false false false false false false false false +Square overlapping left and right square Empty LineString false false false false false false false false false false Square overlapping left and right square Faraway point false false false false false false false false false false Square overlapping left and right square Line going through left and right square false false false false true false true false false false Square overlapping left and right square NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -491,6 +568,28 @@ FROM geom_operators_test a JOIN geom_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection false false +Empty GeometryCollection Empty LineString false false +Empty GeometryCollection Faraway point false false +Empty GeometryCollection Line going through left and right square false false +Empty GeometryCollection NULL NULL NULL +Empty GeometryCollection Point middle of Left Square false false +Empty GeometryCollection Point middle of Right Square false false +Empty GeometryCollection Square (left) false false +Empty GeometryCollection Square (right) false false +Empty GeometryCollection Square overlapping left and right square false false +Empty LineString Empty GeometryCollection false false +Empty LineString Empty LineString false false +Empty LineString Faraway point false false +Empty LineString Line going through left and right square false false +Empty LineString NULL NULL NULL +Empty LineString Point middle of Left Square false false +Empty LineString Point middle of Right Square false false +Empty LineString Square (left) false false +Empty LineString Square (right) false false +Empty LineString Square overlapping left and right square false false +Faraway point Empty GeometryCollection false false +Faraway point Empty LineString false false Faraway point Faraway point true true Faraway point Line going through left and right square false false Faraway point NULL NULL NULL @@ -499,6 +598,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) false false Faraway point Square (right) false false Faraway point Square overlapping left and right square false false +Line going through left and right square Empty GeometryCollection false false +Line going through left and right square Empty LineString false false Line going through left and right square Faraway point false false Line going through left and right square Line going through left and right square true true Line going through left and right square NULL NULL NULL @@ -507,6 +608,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) true false Line going through left and right square Square (right) true false Line going through left and right square Square overlapping left and right square true false +NULL Empty GeometryCollection NULL NULL +NULL Empty LineString NULL NULL NULL Faraway point NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL @@ -515,6 +618,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL Square (right) NULL NULL NULL Square overlapping left and right square NULL NULL +Point middle of Left Square Empty GeometryCollection false false +Point middle of Left Square Empty LineString false false Point middle of Left Square Faraway point false false Point middle of Left Square Line going through left and right square true true Point middle of Left Square NULL NULL NULL @@ -523,6 +628,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) true true Point middle of Left Square Square (right) true false Point middle of Left Square Square overlapping left and right square true false +Point middle of Right Square Empty GeometryCollection false false +Point middle of Right Square Empty LineString false false Point middle of Right Square Faraway point false false Point middle of Right Square Line going through left and right square true true Point middle of Right Square NULL NULL NULL @@ -531,6 +638,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) true false Point middle of Right Square Square (right) true true Point middle of Right Square Square overlapping left and right square true true +Square (left) Empty GeometryCollection false false +Square (left) Empty LineString false false Square (left) Faraway point false false Square (left) Line going through left and right square true false Square (left) NULL NULL NULL @@ -539,6 +648,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) true false Square (left) Square (right) true false Square (left) Square overlapping left and right square true false +Square (right) Empty GeometryCollection false false +Square (right) Empty LineString false false Square (right) Faraway point false false Square (right) Line going through left and right square true false Square (right) NULL NULL NULL @@ -547,6 +658,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) true false Square (right) Square (right) true false Square (right) Square overlapping left and right square true false +Square overlapping left and right square Empty GeometryCollection false false +Square overlapping left and right square Empty LineString false false Square overlapping left and right square Faraway point false false Square overlapping left and right square Line going through left and right square true false Square overlapping left and right square NULL NULL NULL @@ -567,6 +680,28 @@ FROM geom_operators_test a JOIN geom_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection FFFFFFFF2 false +Empty GeometryCollection Empty LineString FFFFFFFF2 false +Empty GeometryCollection Faraway point FFFFFF0F2 false +Empty GeometryCollection Line going through left and right square FFFFFF102 false +Empty GeometryCollection NULL NULL NULL +Empty GeometryCollection Point middle of Left Square FFFFFF0F2 false +Empty GeometryCollection Point middle of Right Square FFFFFF0F2 false +Empty GeometryCollection Square (left) FFFFFF212 false +Empty GeometryCollection Square (right) FFFFFF212 false +Empty GeometryCollection Square overlapping left and right square FFFFFF212 false +Empty LineString Empty GeometryCollection FFFFFFFF2 false +Empty LineString Empty LineString FFFFFFFF2 false +Empty LineString Faraway point FFFFFF0F2 false +Empty LineString Line going through left and right square FFFFFF102 false +Empty LineString NULL NULL NULL +Empty LineString Point middle of Left Square FFFFFF0F2 false +Empty LineString Point middle of Right Square FFFFFF0F2 false +Empty LineString Square (left) FFFFFF212 false +Empty LineString Square (right) FFFFFF212 false +Empty LineString Square overlapping left and right square FFFFFF212 false +Faraway point Empty GeometryCollection FF0FFFFF2 false +Faraway point Empty LineString FF0FFFFF2 false Faraway point Faraway point 0FFFFFFF2 true Faraway point Line going through left and right square FF0FFF102 false Faraway point NULL NULL NULL @@ -575,6 +710,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) FF0FFF212 false Faraway point Square (right) FF0FFF212 false Faraway point Square overlapping left and right square FF0FFF212 false +Line going through left and right square Empty GeometryCollection FF1FF0FF2 false +Line going through left and right square Empty LineString FF1FF0FF2 false Line going through left and right square Faraway point FF1FF00F2 false Line going through left and right square Line going through left and right square 1FFF0FFF2 false Line going through left and right square NULL NULL NULL @@ -583,6 +720,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) 1010F0212 false Line going through left and right square Square (right) 1010F0212 false Line going through left and right square Square overlapping left and right square 1010F0212 false +NULL Empty GeometryCollection NULL NULL +NULL Empty LineString NULL NULL NULL Faraway point NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL @@ -591,6 +730,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL Square (right) NULL NULL NULL Square overlapping left and right square NULL NULL +Point middle of Left Square Empty GeometryCollection FF0FFFFF2 false +Point middle of Left Square Empty LineString FF0FFFFF2 false Point middle of Left Square Faraway point FF0FFF0F2 false Point middle of Left Square Line going through left and right square F0FFFF102 false Point middle of Left Square NULL NULL NULL @@ -599,6 +740,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) 0FFFFF212 false Point middle of Left Square Square (right) FF0FFF212 false Point middle of Left Square Square overlapping left and right square FF0FFF212 false +Point middle of Right Square Empty GeometryCollection FF0FFFFF2 false +Point middle of Right Square Empty LineString FF0FFFFF2 false Point middle of Right Square Faraway point FF0FFF0F2 false Point middle of Right Square Line going through left and right square F0FFFF102 false Point middle of Right Square NULL NULL NULL @@ -607,6 +750,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) FF0FFF212 false Point middle of Right Square Square (right) 0FFFFF212 false Point middle of Right Square Square overlapping left and right square 0FFFFF212 false +Square (left) Empty GeometryCollection FF2FF1FF2 false +Square (left) Empty LineString FF2FF1FF2 false Square (left) Faraway point FF2FF10F2 false Square (left) Line going through left and right square 1020F1102 false Square (left) NULL NULL NULL @@ -615,6 +760,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) 2FFF1FFF2 false Square (left) Square (right) FF2F11212 false Square (left) Square overlapping left and right square 212111212 false +Square (right) Empty GeometryCollection FF2FF1FF2 false +Square (right) Empty LineString FF2FF1FF2 false Square (right) Faraway point FF2FF10F2 false Square (right) Line going through left and right square 1020F1102 false Square (right) NULL NULL NULL @@ -623,6 +770,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) FF2F11212 false Square (right) Square (right) 2FFF1FFF2 false Square (right) Square overlapping left and right square 2FF11F212 false +Square overlapping left and right square Empty GeometryCollection FF2FF1FF2 false +Square overlapping left and right square Empty LineString FF2FF1FF2 false Square overlapping left and right square Faraway point FF2FF10F2 false Square overlapping left and right square Line going through left and right square 1020F1102 false Square overlapping left and right square NULL NULL NULL @@ -645,6 +794,8 @@ SELECT FROM geom_operators_test a ORDER BY a.dsc ---- +Empty GeometryCollection 0 0 0 NULL NULL NULL +Empty LineString 2 0 0 NULL 010200000000000000 NULL Faraway point 2 1 1 NULL 010100000000000000000014400000000000001440 NULL Line going through left and right square 2 2 1 NULL 010200000002000000000000000000E0BF000000000000E03F000000000000E03F000000000000E03F NULL NULL NULL NULL NULL NULL NULL NULL @@ -763,6 +914,8 @@ SELECT FROM geog_operators_test a ORDER BY a.dsc ---- +Empty GeometryCollection 0 0 0 0 0 0 0 0 0 +Empty LineString 0 0 0 0 0 0 0 0 0 Faraway point 0 0 0 0 0 0 0 0 0 Line going through left and right square 0 0 0 111315.280354463 111190.845659241 111315.280354463 0 0 0 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL @@ -782,6 +935,28 @@ FROM geog_operators_test a JOIN geog_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection NULL NULL NULL +Empty GeometryCollection Empty LineString NULL NULL NULL +Empty GeometryCollection Faraway point NULL NULL NULL +Empty GeometryCollection Line going through left and right square NULL NULL NULL +Empty GeometryCollection NULL NULL NULL NULL +Empty GeometryCollection Point middle of Left Square NULL NULL NULL +Empty GeometryCollection Point middle of Right Square NULL NULL NULL +Empty GeometryCollection Square (left) NULL NULL NULL +Empty GeometryCollection Square (right) NULL NULL NULL +Empty GeometryCollection Square overlapping left and right square NULL NULL NULL +Empty LineString Empty GeometryCollection NULL NULL NULL +Empty LineString Empty LineString NULL NULL NULL +Empty LineString Faraway point NULL NULL NULL +Empty LineString Line going through left and right square NULL NULL NULL +Empty LineString NULL NULL NULL NULL +Empty LineString Point middle of Left Square NULL NULL NULL +Empty LineString Point middle of Right Square NULL NULL NULL +Empty LineString Square (left) NULL NULL NULL +Empty LineString Square (right) NULL NULL NULL +Empty LineString Square overlapping left and right square NULL NULL NULL +Faraway point Empty GeometryCollection NULL NULL NULL +Faraway point Empty LineString NULL NULL NULL Faraway point Faraway point 0 0 0 Faraway point Line going through left and right square 705577.648328755 707142.162247235 705577.648328755 Faraway point NULL NULL NULL NULL @@ -790,6 +965,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) 710260.975395327 711311.966664979 710260.975395327 Faraway point Square (right) 627129.502610754 628519.033787529 627129.502610754 Faraway point Square overlapping left and right square 627129.502610754 628519.033787529 627129.502610754 +Line going through left and right square Empty GeometryCollection NULL NULL NULL +Line going through left and right square Empty LineString NULL NULL NULL Line going through left and right square Faraway point 705577.648328755 707142.162247235 705577.648328755 Line going through left and right square Line going through left and right square 0 0 0 Line going through left and right square NULL NULL NULL NULL @@ -798,6 +975,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) 0 0 0 Line going through left and right square Square (right) 0 0 0 Line going through left and right square Square overlapping left and right square 0 0 0 +NULL Empty GeometryCollection NULL NULL NULL +NULL Empty LineString NULL NULL NULL NULL Faraway point NULL NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL NULL NULL @@ -806,6 +985,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL NULL Square (right) NULL NULL NULL NULL Square overlapping left and right square NULL NULL NULL +Point middle of Left Square Empty GeometryCollection NULL NULL NULL +Point middle of Left Square Empty LineString NULL NULL NULL Point middle of Left Square Faraway point 788297.405265007 789521.780520966 788297.405265007 Point middle of Left Square Line going through left and right square 0 0 0 Point middle of Left Square NULL NULL NULL NULL @@ -814,6 +995,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) 0 0 0 Point middle of Left Square Square (right) 55657.6401772334 55595.4228296203 55657.6401772334 Point middle of Left Square Square overlapping left and right square 44526.1121572803 44476.338279173 44526.1121572803 +Point middle of Right Square Empty GeometryCollection NULL NULL NULL +Point middle of Right Square Empty LineString NULL NULL NULL Point middle of Right Square Faraway point 705577.648328755 707142.162247235 705577.648328755 Point middle of Right Square Line going through left and right square 0 0 0 Point middle of Right Square NULL NULL NULL NULL @@ -822,6 +1005,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) 55657.6401772334 55595.4228296203 55657.6401772334 Point middle of Right Square Square (right) 0 0 0 Point middle of Right Square Square overlapping left and right square 0 0 0 +Square (left) Empty GeometryCollection NULL NULL NULL +Square (left) Empty LineString NULL NULL NULL Square (left) Faraway point 710260.975395327 711311.966664979 710260.975395327 Square (left) Line going through left and right square 0 0 0 Square (left) NULL NULL NULL NULL @@ -830,6 +1015,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) 0 0 0 Square (left) Square (right) 0 0 0 Square (left) Square overlapping left and right square 0 0 0 +Square (right) Empty GeometryCollection NULL NULL NULL +Square (right) Empty LineString NULL NULL NULL Square (right) Faraway point 627129.502610754 628519.033787529 627129.502610754 Square (right) Line going through left and right square 0 0 0 Square (right) NULL NULL NULL NULL @@ -838,6 +1025,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) 0 0 0 Square (right) Square (right) 0 0 0 Square (right) Square overlapping left and right square 0 0 0 +Square overlapping left and right square Empty GeometryCollection NULL NULL NULL +Square overlapping left and right square Empty LineString NULL NULL NULL Square overlapping left and right square Faraway point 627129.502610754 628519.033787529 627129.502610754 Square overlapping left and right square Line going through left and right square 0 0 0 Square overlapping left and right square NULL NULL NULL NULL @@ -859,6 +1048,28 @@ FROM geog_operators_test a JOIN geog_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection false false false +Empty GeometryCollection Empty LineString false false false +Empty GeometryCollection Faraway point false false false +Empty GeometryCollection Line going through left and right square false false false +Empty GeometryCollection NULL NULL NULL NULL +Empty GeometryCollection Point middle of Left Square false false false +Empty GeometryCollection Point middle of Right Square false false false +Empty GeometryCollection Square (left) false false false +Empty GeometryCollection Square (right) false false false +Empty GeometryCollection Square overlapping left and right square false false false +Empty LineString Empty GeometryCollection false false false +Empty LineString Empty LineString false false false +Empty LineString Faraway point false false false +Empty LineString Line going through left and right square false false false +Empty LineString NULL NULL NULL NULL +Empty LineString Point middle of Left Square false false false +Empty LineString Point middle of Right Square false false false +Empty LineString Square (left) false false false +Empty LineString Square (right) false false false +Empty LineString Square overlapping left and right square false false false +Faraway point Empty GeometryCollection false false false +Faraway point Empty LineString false false false Faraway point Faraway point true true true Faraway point Line going through left and right square false false false Faraway point NULL NULL NULL NULL @@ -867,6 +1078,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) false false false Faraway point Square (right) false false false Faraway point Square overlapping left and right square false false false +Line going through left and right square Empty GeometryCollection false false false +Line going through left and right square Empty LineString false false false Line going through left and right square Faraway point false false false Line going through left and right square Line going through left and right square true true true Line going through left and right square NULL NULL NULL NULL @@ -875,6 +1088,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) false false true Line going through left and right square Square (right) false false true Line going through left and right square Square overlapping left and right square false false true +NULL Empty GeometryCollection NULL NULL NULL +NULL Empty LineString NULL NULL NULL NULL Faraway point NULL NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL NULL NULL @@ -883,6 +1098,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL NULL Square (right) NULL NULL NULL NULL Square overlapping left and right square NULL NULL NULL +Point middle of Left Square Empty GeometryCollection false false false +Point middle of Left Square Empty LineString false false false Point middle of Left Square Faraway point false false false Point middle of Left Square Line going through left and right square false true true Point middle of Left Square NULL NULL NULL NULL @@ -891,6 +1108,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) false true true Point middle of Left Square Square (right) false false false Point middle of Left Square Square overlapping left and right square false false false +Point middle of Right Square Empty GeometryCollection false false false +Point middle of Right Square Empty LineString false false false Point middle of Right Square Faraway point false false false Point middle of Right Square Line going through left and right square false true true Point middle of Right Square NULL NULL NULL NULL @@ -899,6 +1118,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) false false false Point middle of Right Square Square (right) false true true Point middle of Right Square Square overlapping left and right square false true true +Square (left) Empty GeometryCollection false false false +Square (left) Empty LineString false false false Square (left) Faraway point false false false Square (left) Line going through left and right square false false true Square (left) NULL NULL NULL NULL @@ -907,6 +1128,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) true true true Square (left) Square (right) false false true Square (left) Square overlapping left and right square false false true +Square (right) Empty GeometryCollection false false false +Square (right) Empty LineString false false false Square (right) Faraway point false false false Square (right) Line going through left and right square false false true Square (right) NULL NULL NULL NULL @@ -915,6 +1138,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) false false true Square (right) Square (right) true true true Square (right) Square overlapping left and right square false false true +Square overlapping left and right square Empty GeometryCollection false false false +Square overlapping left and right square Empty LineString false false false Square overlapping left and right square Faraway point false false false Square overlapping left and right square Line going through left and right square false false true Square overlapping left and right square NULL NULL NULL NULL @@ -936,6 +1161,28 @@ FROM geog_operators_test a JOIN geog_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- +Empty GeometryCollection Empty GeometryCollection false false false +Empty GeometryCollection Empty LineString false false false +Empty GeometryCollection Faraway point false false false +Empty GeometryCollection Line going through left and right square false false false +Empty GeometryCollection NULL NULL NULL NULL +Empty GeometryCollection Point middle of Left Square false false false +Empty GeometryCollection Point middle of Right Square false false false +Empty GeometryCollection Square (left) false false false +Empty GeometryCollection Square (right) false false false +Empty GeometryCollection Square overlapping left and right square false false false +Empty LineString Empty GeometryCollection false false false +Empty LineString Empty LineString false false false +Empty LineString Faraway point false false false +Empty LineString Line going through left and right square false false false +Empty LineString NULL NULL NULL NULL +Empty LineString Point middle of Left Square false false false +Empty LineString Point middle of Right Square false false false +Empty LineString Square (left) false false false +Empty LineString Square (right) false false false +Empty LineString Square overlapping left and right square false false false +Faraway point Empty GeometryCollection false false false +Faraway point Empty LineString false false false Faraway point Faraway point true true true Faraway point Line going through left and right square false false false Faraway point NULL NULL NULL NULL @@ -944,6 +1191,8 @@ Faraway point Point middle of Right Square Faraway point Square (left) false false false Faraway point Square (right) false false false Faraway point Square overlapping left and right square false false false +Line going through left and right square Empty GeometryCollection false false false +Line going through left and right square Empty LineString false false false Line going through left and right square Faraway point false false false Line going through left and right square Line going through left and right square true true true Line going through left and right square NULL NULL NULL NULL @@ -952,6 +1201,8 @@ Line going through left and right square Point middle of Right Square Line going through left and right square Square (left) true true true Line going through left and right square Square (right) true true true Line going through left and right square Square overlapping left and right square true true true +NULL Empty GeometryCollection NULL NULL NULL +NULL Empty LineString NULL NULL NULL NULL Faraway point NULL NULL NULL NULL Line going through left and right square NULL NULL NULL NULL NULL NULL NULL NULL @@ -960,6 +1211,8 @@ NULL Point middle of Right Square NULL Square (left) NULL NULL NULL NULL Square (right) NULL NULL NULL NULL Square overlapping left and right square NULL NULL NULL +Point middle of Left Square Empty GeometryCollection false false false +Point middle of Left Square Empty LineString false false false Point middle of Left Square Faraway point false false false Point middle of Left Square Line going through left and right square true true true Point middle of Left Square NULL NULL NULL NULL @@ -968,6 +1221,8 @@ Point middle of Left Square Point middle of Right Square Point middle of Left Square Square (left) true true true Point middle of Left Square Square (right) true true true Point middle of Left Square Square overlapping left and right square true true true +Point middle of Right Square Empty GeometryCollection false false false +Point middle of Right Square Empty LineString false false false Point middle of Right Square Faraway point false false false Point middle of Right Square Line going through left and right square true true true Point middle of Right Square NULL NULL NULL NULL @@ -976,6 +1231,8 @@ Point middle of Right Square Point middle of Right Square Point middle of Right Square Square (left) true true true Point middle of Right Square Square (right) true true true Point middle of Right Square Square overlapping left and right square true true true +Square (left) Empty GeometryCollection false false false +Square (left) Empty LineString false false false Square (left) Faraway point false false false Square (left) Line going through left and right square true true true Square (left) NULL NULL NULL NULL @@ -984,6 +1241,8 @@ Square (left) Point middle of Right Square Square (left) Square (left) true true true Square (left) Square (right) true true true Square (left) Square overlapping left and right square true true true +Square (right) Empty GeometryCollection false false false +Square (right) Empty LineString false false false Square (right) Faraway point false false false Square (right) Line going through left and right square true true true Square (right) NULL NULL NULL NULL @@ -992,6 +1251,8 @@ Square (right) Point middle of Right Square Square (right) Square (left) true true true Square (right) Square (right) true true true Square (right) Square overlapping left and right square true true true +Square overlapping left and right square Empty GeometryCollection false false false +Square overlapping left and right square Empty LineString false false false Square overlapping left and right square Faraway point false false false Square overlapping left and right square Line going through left and right square true true true Square overlapping left and right square NULL NULL NULL NULL @@ -1010,6 +1271,8 @@ SELECT FROM geog_operators_test ORDER BY dsc ---- +Empty GeometryCollection GEOMETRYCOLLECTION EMPTY GEOMETRYCOLLECTION EMPTY +Empty LineString LINESTRING EMPTY LINESTRING EMPTY Faraway point POINT (5 5) POINT (5 5) Line going through left and right square LINESTRING (-0.5 0.5, -0.00000000000000009939611878359099 0.5000190382262164, 0.5 0.5) LINESTRING (-0.5 0.5, -0.25000000036247944 0.500014278647005, -0.00000000000000009939611878359099 0.5000190382262164, 0.2500000003624792 0.5000142786470051, 0.5 0.5) NULL NULL NULL diff --git a/pkg/sql/sem/builtins/geo_builtins.go b/pkg/sql/sem/builtins/geo_builtins.go index dc932f183174..6139d95774c8 100644 --- a/pkg/sql/sem/builtins/geo_builtins.go +++ b/pkg/sql/sem/builtins/geo_builtins.go @@ -851,7 +851,17 @@ var geoBuiltins = map[string]builtinDefinition{ if err != nil { return nil, err } - return tree.NewDInt(tree.DInt(len(t.FlatCoords()) / t.Stride())), nil + switch t := t.(type) { + case *geom.GeometryCollection: + // FlatCoords() does not work on GeometryCollection. + numPoints := 0 + for _, g := range t.Geoms() { + numPoints += len(g.FlatCoords()) / g.Stride() + } + return tree.NewDInt(tree.DInt(numPoints)), nil + default: + return tree.NewDInt(tree.DInt(len(t.FlatCoords()) / t.Stride())), nil + } }, types.Int, infoBuilder{ @@ -1241,6 +1251,9 @@ Note ST_Perimeter is only valid for Polygon - use ST_Length for LineString.`, func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { ret, err := geomfn.MinDistance(a.Geometry, b.Geometry) if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } return nil, err } return tree.NewDFloat(tree.DFloat(ret)), nil @@ -1255,6 +1268,9 @@ Note ST_Perimeter is only valid for Polygon - use ST_Length for LineString.`, func(ctx *tree.EvalContext, a *tree.DGeography, b *tree.DGeography) (tree.Datum, error) { ret, err := geogfn.Distance(a.Geography, b.Geography, geogfn.UseSpheroid) if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } return nil, err } return tree.NewDFloat(tree.DFloat(ret)), nil @@ -1281,6 +1297,9 @@ Note ST_Perimeter is only valid for Polygon - use ST_Length for LineString.`, ret, err := geogfn.Distance(a.Geography, b.Geography, toUseSphereOrSpheroid(useSpheroid)) if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } return nil, err } return tree.NewDFloat(tree.DFloat(ret)), nil @@ -1299,6 +1318,9 @@ Note ST_Perimeter is only valid for Polygon - use ST_Length for LineString.`, func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { ret, err := geomfn.MaxDistance(a.Geometry, b.Geometry) if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } return nil, err } return tree.NewDFloat(tree.DFloat(ret)), nil