github.com/dolthub/go-mysql-server@v0.18.0/sql/types/geometry.go (about)

     1  // Copyright 2022 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package types
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"math"
    21  	"reflect"
    22  
    23  	"github.com/dolthub/vitess/go/sqltypes"
    24  	"github.com/dolthub/vitess/go/vt/proto/query"
    25  	"gopkg.in/src-d/go-errors.v1"
    26  
    27  	"github.com/dolthub/go-mysql-server/sql"
    28  )
    29  
    30  // GeometryType represents the GEOMETRY type.
    31  // https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometry.html
    32  // The type of the returned value is one of the following (each implements GeometryValue): Point, Polygon, LineString.
    33  type GeometryType struct {
    34  	SRID        uint32
    35  	DefinedSRID bool
    36  }
    37  
    38  // GeometryValue is the value type returned from GeometryType, which is an interface over the following types:
    39  // Point, Polygon, LineString, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection.
    40  type GeometryValue interface {
    41  	implementsGeometryValue()
    42  	GetSRID() uint32
    43  	SetSRID(srid uint32) GeometryValue
    44  	Serialize() []byte
    45  	WriteData(buf []byte) int
    46  	Swap() GeometryValue
    47  	BBox() (float64, float64, float64, float64)
    48  }
    49  
    50  var _ sql.Type = GeometryType{}
    51  var _ sql.SpatialColumnType = GeometryType{}
    52  var _ sql.CollationCoercible = GeometryType{}
    53  
    54  var (
    55  	ErrNotGeometry = errors.NewKind("Value of type %T is not a geometry")
    56  
    57  	geometryValueType = reflect.TypeOf((*GeometryValue)(nil)).Elem()
    58  )
    59  
    60  const (
    61  	CartesianSRID  = uint32(0)
    62  	GeoSpatialSRID = uint32(4326)
    63  )
    64  
    65  const (
    66  	SRIDSize       = 4
    67  	EndianSize     = 1
    68  	TypeSize       = 4
    69  	EWKBHeaderSize = SRIDSize + EndianSize + TypeSize
    70  	WKBHeaderSize  = EndianSize + TypeSize
    71  
    72  	PointSize             = 16
    73  	CountSize             = 4
    74  	GeometryMaxByteLength = 4*(1024*1024*1024) - 1
    75  )
    76  
    77  // Type IDs
    78  const (
    79  	WKBUnknown = iota
    80  	WKBPointID
    81  	WKBLineID
    82  	WKBPolyID
    83  	WKBMultiPointID
    84  	WKBMultiLineID
    85  	WKBMultiPolyID
    86  	WKBGeomCollID
    87  )
    88  
    89  // isLinearRing checks if a LineString is a linear ring
    90  func isLinearRing(line LineString) bool {
    91  	// Get number of points
    92  	numPoints := len(line.Points)
    93  	// Check length of LineString (must be 0 or 4+) points
    94  	if numPoints != 0 && numPoints < 4 {
    95  		return false
    96  	}
    97  	// Check if it is closed (first and last point are the same)
    98  	if line.Points[0] != line.Points[numPoints-1] {
    99  		return false
   100  	}
   101  	return true
   102  }
   103  
   104  // DeserializeEWKBHeader parses the header portion of a byte array in EWKB format to extract endianness and type
   105  func DeserializeEWKBHeader(buf []byte) (srid uint32, bigEndian bool, typ uint32, err error) {
   106  	// Must be right length
   107  	if len(buf) < EWKBHeaderSize {
   108  		return 0, false, 0, sql.ErrInvalidGISData.New("DeserializeEWKBHeader")
   109  	}
   110  	srid = binary.LittleEndian.Uint32(buf) // First 4 bytes is SRID always in little endian
   111  	buf = buf[SRIDSize:]                   // Shift pointer over
   112  	bigEndian = buf[0] == 0                // Next byte is endianness
   113  	buf = buf[EndianSize:]                 // Shift pointer over
   114  	if bigEndian {                         // Next 4 bytes is type
   115  		typ = binary.BigEndian.Uint32(buf)
   116  	} else {
   117  		typ = binary.LittleEndian.Uint32(buf)
   118  	}
   119  
   120  	return
   121  }
   122  
   123  // DeserializeWKBHeader parses the header potion of a byte array in WKB format
   124  // There is no SRID
   125  func DeserializeWKBHeader(buf []byte) (bigEndian bool, typ uint32, err error) {
   126  	// Must be right length
   127  	if len(buf) < (EndianSize + TypeSize) {
   128  		return false, 0, sql.ErrInvalidGISData.New("DeserializeWKBHeader")
   129  	}
   130  
   131  	bigEndian = buf[0] == 0 // First byte is byte order
   132  	buf = buf[EndianSize:]  // Shift pointer over
   133  	if bigEndian {          // Next 4 bytes is geometry type
   134  		typ = binary.BigEndian.Uint32(buf)
   135  	} else {
   136  		typ = binary.LittleEndian.Uint32(buf)
   137  	}
   138  
   139  	return
   140  }
   141  
   142  // DeserializePoint parses the data portion of a byte array in WKB format to a Point object
   143  func DeserializePoint(buf []byte, isBig bool, srid uint32) (Point, int, error) {
   144  	// Must be 16 bytes (2 floats)
   145  	if len(buf) != PointSize {
   146  		return Point{}, 0, sql.ErrInvalidGISData.New("DeserializePoint")
   147  	}
   148  
   149  	// Read floats x and y
   150  	var x, y float64
   151  	if isBig {
   152  		x = math.Float64frombits(binary.BigEndian.Uint64(buf[:8]))
   153  		y = math.Float64frombits(binary.BigEndian.Uint64(buf[8:]))
   154  	} else {
   155  		x = math.Float64frombits(binary.LittleEndian.Uint64(buf[:8]))
   156  		y = math.Float64frombits(binary.LittleEndian.Uint64(buf[8:]))
   157  	}
   158  
   159  	return Point{SRID: srid, X: x, Y: y}, PointSize, nil
   160  }
   161  
   162  // DeserializeLine parses the data portion of a byte array in WKB format to a LineString object
   163  func DeserializeLine(buf []byte, isBig bool, srid uint32) (LineString, int, error) {
   164  	// Must be at least CountSize and two points
   165  	if len(buf) < (CountSize + PointSize + PointSize) {
   166  		return LineString{}, 0, sql.ErrInvalidGISData.New("DeserializeLine")
   167  	}
   168  
   169  	// Read number of points
   170  	points := make([]Point, readCount(buf, isBig))
   171  	buf = buf[CountSize:]
   172  
   173  	// Read points
   174  	var err error
   175  	for i := range points {
   176  		points[i], _, err = DeserializePoint(buf[:PointSize], isBig, srid)
   177  		if err != nil {
   178  			return LineString{}, 0, sql.ErrInvalidGISData.New("DeserializeLine")
   179  		}
   180  		buf = buf[PointSize:]
   181  	}
   182  
   183  	return LineString{SRID: srid, Points: points}, CountSize + PointSize*len(points), nil
   184  }
   185  
   186  // DeserializePoly parses the data portion of a byte array in WKB format to a Polygon object
   187  func DeserializePoly(buf []byte, isBig bool, srid uint32) (Polygon, int, error) {
   188  	// Must be at least count, count, and four points
   189  	if len(buf) < (CountSize + CountSize + 4*PointSize) {
   190  		return Polygon{}, 0, sql.ErrInvalidGISData.New("DeserializePoly")
   191  	}
   192  
   193  	// Read number of lines
   194  	lines := make([]LineString, readCount(buf, isBig))
   195  	buf = buf[CountSize:]
   196  	count := CountSize
   197  
   198  	// Read lines
   199  	var err error
   200  	var c int
   201  	for i := range lines {
   202  		lines[i], c, err = DeserializeLine(buf, isBig, srid)
   203  		if err != nil {
   204  			return Polygon{}, 0, sql.ErrInvalidGISData.New("DeserializePoly")
   205  		}
   206  		buf = buf[c:]
   207  		count += c
   208  	}
   209  
   210  	return Polygon{SRID: srid, Lines: lines}, count, nil
   211  }
   212  
   213  func readCount(buf []byte, isBig bool) uint32 {
   214  	if isBig {
   215  		return binary.BigEndian.Uint32(buf)
   216  	}
   217  	return binary.LittleEndian.Uint32(buf)
   218  }
   219  
   220  // DeserializeMPoint parses the data portion of a byte array in WKB format to a MultiPoint object
   221  func DeserializeMPoint(buf []byte, isBig bool, srid uint32) (MultiPoint, int, error) {
   222  	// Must contain at count, wkb header, and one point
   223  	if len(buf) < (CountSize + WKBHeaderSize + PointSize) {
   224  		return MultiPoint{}, 0, sql.ErrInvalidGISData.New("DeserializeMPoint")
   225  	}
   226  
   227  	// Read number of points in MultiPoint
   228  	points := make([]Point, readCount(buf, isBig))
   229  	buf = buf[CountSize:]
   230  	for i := range points {
   231  		// WKBHeaders are inside MultiGeometry Types
   232  		isBig, typ, err := DeserializeWKBHeader(buf)
   233  		if err != nil {
   234  			return MultiPoint{}, 0, err
   235  		}
   236  		if typ != WKBPointID {
   237  			return MultiPoint{}, 0, sql.ErrInvalidGISData.New("DeserializeMPoint")
   238  		}
   239  		buf = buf[WKBHeaderSize:]
   240  		// Read point data
   241  		points[i], _, err = DeserializePoint(buf[:PointSize], isBig, srid)
   242  		if err != nil {
   243  			return MultiPoint{}, 0, err
   244  		}
   245  		buf = buf[PointSize:]
   246  	}
   247  
   248  	return MultiPoint{SRID: srid, Points: points}, CountSize + (WKBHeaderSize+PointSize)*len(points), nil
   249  }
   250  
   251  // DeserializeMLine parses the data portion of a byte array in WKB format to a MultiLineString object
   252  func DeserializeMLine(buf []byte, isBig bool, srid uint32) (MultiLineString, int, error) {
   253  	// Must contain at least length, wkb header, length, and two points
   254  	if len(buf) < (CountSize + WKBHeaderSize + CountSize + 2*PointSize) {
   255  		return MultiLineString{}, 0, sql.ErrInvalidGISData.New("MultiLineString")
   256  	}
   257  
   258  	// Read number of lines
   259  	lines := make([]LineString, readCount(buf, isBig))
   260  	buf = buf[CountSize:]
   261  	count := CountSize
   262  	var c int
   263  	for i := range lines {
   264  		isBig, typ, err := DeserializeWKBHeader(buf)
   265  		if typ != WKBLineID {
   266  			return MultiLineString{}, 0, sql.ErrInvalidGISData.New("DeserializeMLine")
   267  		}
   268  		buf = buf[WKBHeaderSize:]
   269  
   270  		lines[i], c, err = DeserializeLine(buf, isBig, srid)
   271  		if err != nil {
   272  			return MultiLineString{}, 0, sql.ErrInvalidGISData.New("DeserializeMLine")
   273  		}
   274  
   275  		buf = buf[c:]
   276  		count += WKBHeaderSize + c
   277  	}
   278  
   279  	return MultiLineString{SRID: srid, Lines: lines}, count, nil
   280  }
   281  
   282  // DeserializeMPoly parses the data portion of a byte array in WKB format to a MultiPolygon object
   283  func DeserializeMPoly(buf []byte, isBig bool, srid uint32) (MultiPolygon, int, error) {
   284  	// Must contain at least num polys, wkb header, num lines, num lines, and four points
   285  	if len(buf) < (CountSize + WKBHeaderSize + 2*CountSize + 4*PointSize) {
   286  		return MultiPolygon{}, 0, sql.ErrInvalidGISData.New("MultiPolygon")
   287  	}
   288  
   289  	// Read number of polygons
   290  	polys := make([]Polygon, readCount(buf, isBig))
   291  	buf = buf[CountSize:]
   292  	count := CountSize
   293  	var c int
   294  	for i := range polys {
   295  		isBig, typ, err := DeserializeWKBHeader(buf)
   296  		if typ != WKBPolyID {
   297  			return MultiPolygon{}, 0, sql.ErrInvalidGISData.New("DeserializeMPoly")
   298  		}
   299  
   300  		buf = buf[WKBHeaderSize:]
   301  		polys[i], c, err = DeserializePoly(buf, isBig, srid)
   302  		if err != nil {
   303  			return MultiPolygon{}, 0, sql.ErrInvalidGISData.New("DeserializeMPoly")
   304  		}
   305  
   306  		buf = buf[c:]
   307  		count += WKBHeaderSize + c
   308  	}
   309  
   310  	return MultiPolygon{SRID: srid, Polygons: polys}, count, nil
   311  }
   312  
   313  // DeserializeGeomColl parses the data portion of a byte array in WKB format to a GeometryCollection object
   314  func DeserializeGeomColl(buf []byte, isBig bool, srid uint32) (GeomColl, int, error) {
   315  	// Must be at least CountSize
   316  	if len(buf) < CountSize {
   317  		return GeomColl{}, 0, sql.ErrInvalidGISData.New("DeserializeLine")
   318  	}
   319  
   320  	// Read number of geometry objects
   321  	geoms := make([]GeometryValue, readCount(buf, isBig))
   322  	buf = buf[CountSize:]
   323  	count := CountSize
   324  
   325  	// Read geometries
   326  	var c int
   327  	for i := range geoms {
   328  		isBig, typ, err := DeserializeWKBHeader(buf)
   329  		if err != nil {
   330  			return GeomColl{}, 0, sql.ErrInvalidGISData.New("GeometryType.Convert")
   331  		}
   332  		buf = buf[WKBHeaderSize:]
   333  
   334  		switch typ {
   335  		case WKBPointID:
   336  			geoms[i], c, err = DeserializePoint(buf[:PointSize], isBig, srid)
   337  		case WKBLineID:
   338  			geoms[i], c, err = DeserializeLine(buf, isBig, srid)
   339  		case WKBPolyID:
   340  			geoms[i], c, err = DeserializePoly(buf, isBig, srid)
   341  		case WKBMultiPointID:
   342  			geoms[i], c, err = DeserializeMPoint(buf, isBig, srid)
   343  		case WKBMultiLineID:
   344  			geoms[i], c, err = DeserializeMLine(buf, isBig, srid)
   345  		case WKBMultiPolyID:
   346  			geoms[i], c, err = DeserializeMPoly(buf, isBig, srid)
   347  		case WKBGeomCollID:
   348  			geoms[i], c, err = DeserializeGeomColl(buf, isBig, srid)
   349  		default:
   350  			return GeomColl{}, 0, sql.ErrInvalidGISData.New("GeometryType.Convert")
   351  		}
   352  		if err != nil {
   353  			return GeomColl{}, 0, sql.ErrInvalidGISData.New("GeometryType.Convert")
   354  		}
   355  
   356  		buf = buf[c:]
   357  		count += WKBHeaderSize + c
   358  	}
   359  
   360  	return GeomColl{SRID: srid, Geoms: geoms}, count, nil
   361  }
   362  
   363  // TODO: unexport
   364  func AllocateGeoTypeBuffer(numPoints, numCounts, numWKBHeaders int) []byte {
   365  	return make([]byte, EWKBHeaderSize+PointSize*numPoints+CountSize*numCounts+numWKBHeaders*WKBHeaderSize)
   366  }
   367  
   368  // WriteEWKBHeader will write EWKB header to the given buffer
   369  func WriteEWKBHeader(buf []byte, srid, typ uint32) {
   370  	binary.LittleEndian.PutUint32(buf, srid) // always write SRID in little endian
   371  	buf = buf[SRIDSize:]                     // shift
   372  	buf[0] = 1                               // always write in little endian
   373  	buf = buf[EndianSize:]                   // shift
   374  	binary.LittleEndian.PutUint32(buf, typ)  // write geometry type
   375  }
   376  
   377  // WriteWKBHeader will write WKB header to the given buffer
   378  func WriteWKBHeader(buf []byte, typ uint32) {
   379  	buf[0] = 1                              // always write in little endian
   380  	buf = buf[EndianSize:]                  // shift
   381  	binary.LittleEndian.PutUint32(buf, typ) // write geometry type
   382  }
   383  
   384  // TODO: rename me, unexport
   385  func WriteCount(buf []byte, count uint32) {
   386  	binary.LittleEndian.PutUint32(buf, count)
   387  }
   388  
   389  // Compare implements Type interface.
   390  func (t GeometryType) Compare(a any, b any) (int, error) {
   391  	if hasNulls, res := CompareNulls(a, b); hasNulls {
   392  		return res, nil
   393  	}
   394  
   395  	aa, ok := a.(GeometryValue)
   396  	if !ok {
   397  		return 0, ErrNotGeometry.New(a)
   398  	}
   399  
   400  	bb, ok := b.(GeometryValue)
   401  	if !ok {
   402  		return 0, ErrNotGeometry.New(b)
   403  	}
   404  
   405  	return bytes.Compare(aa.Serialize(), bb.Serialize()), nil
   406  }
   407  
   408  // Convert implements Type interface.
   409  func (t GeometryType) Convert(v interface{}) (interface{}, sql.ConvertInRange, error) {
   410  	if v == nil {
   411  		return nil, sql.InRange, nil
   412  	}
   413  	switch val := v.(type) {
   414  	case []byte:
   415  		srid, isBig, geomType, err := DeserializeEWKBHeader(val)
   416  		if err != nil {
   417  			return nil, sql.OutOfRange, err
   418  		}
   419  		val = val[EWKBHeaderSize:]
   420  
   421  		var geom interface{}
   422  		switch geomType {
   423  		case WKBPointID:
   424  			geom, _, err = DeserializePoint(val, isBig, srid)
   425  		case WKBLineID:
   426  			geom, _, err = DeserializeLine(val, isBig, srid)
   427  		case WKBPolyID:
   428  			geom, _, err = DeserializePoly(val, isBig, srid)
   429  		case WKBMultiPointID:
   430  			geom, _, err = DeserializeMPoint(val, isBig, srid)
   431  		case WKBMultiLineID:
   432  			geom, _, err = DeserializeMLine(val, isBig, srid)
   433  		case WKBMultiPolyID:
   434  			geom, _, err = DeserializeMPoly(val, isBig, srid)
   435  		case WKBGeomCollID:
   436  			geom, _, err = DeserializeGeomColl(val, isBig, srid)
   437  		default:
   438  			return nil, sql.OutOfRange, sql.ErrInvalidGISData.New("GeometryType.Convert")
   439  		}
   440  		if err != nil {
   441  			return nil, sql.OutOfRange, err
   442  		}
   443  		return geom, sql.InRange, nil
   444  	case string:
   445  		return t.Convert([]byte(val))
   446  	case GeometryValue:
   447  		if err := t.MatchSRID(val); err != nil {
   448  			return nil, sql.OutOfRange, err
   449  		}
   450  		return val, sql.InRange, nil
   451  	default:
   452  		return nil, sql.OutOfRange, sql.ErrSpatialTypeConversion.New()
   453  	}
   454  }
   455  
   456  // Equals implements the Type interface.
   457  func (t GeometryType) Equals(otherType sql.Type) (ok bool) {
   458  	_, ok = otherType.(GeometryType)
   459  	return
   460  }
   461  
   462  // MaxTextResponseByteLength implements the Type interface
   463  func (t GeometryType) MaxTextResponseByteLength(_ *sql.Context) uint32 {
   464  	return GeometryMaxByteLength
   465  }
   466  
   467  // Promote implements the Type interface.
   468  func (t GeometryType) Promote() sql.Type {
   469  	return t
   470  }
   471  
   472  // SQL implements Type interface.
   473  func (t GeometryType) SQL(ctx *sql.Context, dest []byte, v interface{}) (sqltypes.Value, error) {
   474  	if v == nil {
   475  		return sqltypes.NULL, nil
   476  	}
   477  
   478  	v, _, err := t.Convert(v)
   479  	if err != nil {
   480  		return sqltypes.Value{}, nil
   481  	}
   482  
   483  	buf := v.(GeometryValue).Serialize()
   484  
   485  	return sqltypes.MakeTrusted(sqltypes.Geometry, buf), nil
   486  }
   487  
   488  // String implements Type interface.
   489  func (t GeometryType) String() string {
   490  	return "geometry"
   491  }
   492  
   493  // Type implements Type interface.
   494  func (t GeometryType) Type() query.Type {
   495  	return sqltypes.Geometry
   496  }
   497  
   498  // ValueType implements Type interface.
   499  func (t GeometryType) ValueType() reflect.Type {
   500  	return geometryValueType
   501  }
   502  
   503  // Zero implements Type interface.
   504  func (t GeometryType) Zero() interface{} {
   505  	// MySQL throws an error for INSERT IGNORE, UPDATE IGNORE, etc. if the geometry type cannot be parsed:
   506  	// ERROR 1416 (22003): Cannot get geometry object from data you send to the GEOMETRY field
   507  	// So, we don't implement a zero type for this function.
   508  	return nil
   509  }
   510  
   511  // CollationCoercibility implements sql.CollationCoercible interface.
   512  func (GeometryType) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   513  	return sql.Collation_binary, 5
   514  }
   515  
   516  // GetSpatialTypeSRID implements SpatialColumnType interface.
   517  func (t GeometryType) GetSpatialTypeSRID() (uint32, bool) {
   518  	return t.SRID, t.DefinedSRID
   519  }
   520  
   521  // SetSRID implements SpatialColumnType interface.
   522  func (t GeometryType) SetSRID(v uint32) sql.Type {
   523  	t.SRID = v
   524  	t.DefinedSRID = true
   525  	return t
   526  }
   527  
   528  // MatchSRID implements SpatialColumnType interface
   529  func (t GeometryType) MatchSRID(v interface{}) error {
   530  	if !t.DefinedSRID {
   531  		return nil
   532  	}
   533  	// if matched with SRID value of row value
   534  	var srid uint32
   535  	switch val := v.(type) {
   536  	case GeometryValue:
   537  		srid = val.GetSRID()
   538  	default:
   539  		return ErrNotGeometry.New(v)
   540  	}
   541  	if t.SRID == srid {
   542  		return nil
   543  	}
   544  	return sql.ErrNotMatchingSRID.New(srid, t.SRID)
   545  }
   546  
   547  func ValidateSRID(srid int, funcName string) error {
   548  	if srid < 0 || srid > math.MaxUint32 {
   549  		return sql.ErrInvalidSRID.New(funcName)
   550  	}
   551  	if _, ok := SupportedSRIDs[uint32(srid)]; !ok {
   552  		return sql.ErrNoSRID.New(srid)
   553  	}
   554  
   555  	return nil
   556  }