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 }