github.com/dolthub/go-mysql-server@v0.18.0/sql/types/point.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  	"encoding/binary"
    19  	"math"
    20  	"reflect"
    21  
    22  	"github.com/dolthub/vitess/go/sqltypes"
    23  	"github.com/dolthub/vitess/go/vt/proto/query"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  )
    27  
    28  // PointType represents the POINT type.
    29  // https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html
    30  // The type of the returned value is Point.
    31  type PointType struct {
    32  	SRID        uint32
    33  	DefinedSRID bool
    34  }
    35  
    36  // Point is the value type returned from PointType. Implements GeometryValue.
    37  type Point struct {
    38  	SRID uint32
    39  	X    float64
    40  	Y    float64
    41  }
    42  
    43  var _ sql.Type = PointType{}
    44  var _ sql.SpatialColumnType = PointType{}
    45  var _ sql.CollationCoercible = PointType{}
    46  var _ GeometryValue = Point{}
    47  
    48  var (
    49  	pointValueType = reflect.TypeOf(Point{})
    50  )
    51  
    52  // Compare implements Type interface.
    53  func (t PointType) Compare(a interface{}, b interface{}) (int, error) {
    54  	return GeometryType{}.Compare(a, b)
    55  }
    56  
    57  // Convert implements Type interface.
    58  func (t PointType) Convert(v interface{}) (interface{}, sql.ConvertInRange, error) {
    59  	// Allow null
    60  	if v == nil {
    61  		return nil, sql.InRange, nil
    62  	}
    63  	// Handle conversions
    64  	switch val := v.(type) {
    65  	case []byte:
    66  		// Parse header
    67  		srid, isBig, geomType, err := DeserializeEWKBHeader(val)
    68  		if err != nil {
    69  			return nil, sql.OutOfRange, err
    70  		}
    71  		// Throw error if not marked as point
    72  		if geomType != WKBPointID {
    73  			return nil, sql.OutOfRange, sql.ErrInvalidGISData.New("PointType.Convert")
    74  		}
    75  		// Parse data section
    76  		point, _, err := DeserializePoint(val[EWKBHeaderSize:], isBig, srid)
    77  		if err != nil {
    78  			return nil, sql.OutOfRange, err
    79  		}
    80  		return point, sql.InRange, nil
    81  	case string:
    82  		return t.Convert([]byte(val))
    83  	case Point:
    84  		if err := t.MatchSRID(val); err != nil {
    85  			return nil, sql.OutOfRange, err
    86  		}
    87  		return val, sql.InRange, nil
    88  	default:
    89  		return nil, sql.OutOfRange, sql.ErrSpatialTypeConversion.New()
    90  	}
    91  }
    92  
    93  // Equals implements the Type interface.
    94  func (t PointType) Equals(otherType sql.Type) bool {
    95  	_, ok := otherType.(PointType)
    96  	return ok
    97  }
    98  
    99  // MaxTextResponseByteLength implements the Type interface
   100  func (t PointType) MaxTextResponseByteLength(_ *sql.Context) uint32 {
   101  	return GeometryMaxByteLength
   102  }
   103  
   104  // Promote implements the Type interface.
   105  func (t PointType) Promote() sql.Type {
   106  	return t
   107  }
   108  
   109  // SQL implements Type interface.
   110  func (t PointType) SQL(ctx *sql.Context, dest []byte, v interface{}) (sqltypes.Value, error) {
   111  	if v == nil {
   112  		return sqltypes.NULL, nil
   113  	}
   114  
   115  	v, _, err := t.Convert(v)
   116  	if err != nil {
   117  		return sqltypes.Value{}, nil
   118  	}
   119  
   120  	buf := v.(Point).Serialize()
   121  
   122  	return sqltypes.MakeTrusted(sqltypes.Geometry, buf), nil
   123  }
   124  
   125  // String implements Type interface.
   126  func (t PointType) String() string {
   127  	return "point"
   128  }
   129  
   130  // Type implements Type interface.
   131  func (t PointType) Type() query.Type {
   132  	return sqltypes.Geometry
   133  }
   134  
   135  // Zero implements Type interface.
   136  func (t PointType) Zero() interface{} {
   137  	return Point{X: 0.0, Y: 0.0}
   138  }
   139  
   140  // CollationCoercibility implements sql.CollationCoercible interface.
   141  func (PointType) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   142  	return sql.Collation_binary, 5
   143  }
   144  
   145  // ValueType implements Type interface.
   146  func (t PointType) ValueType() reflect.Type {
   147  	return pointValueType
   148  }
   149  
   150  // GetSpatialTypeSRID implements SpatialColumnType interface.
   151  func (t PointType) GetSpatialTypeSRID() (uint32, bool) {
   152  	return t.SRID, t.DefinedSRID
   153  }
   154  
   155  // SetSRID implements SpatialColumnType interface.
   156  func (t PointType) SetSRID(v uint32) sql.Type {
   157  	t.SRID = v
   158  	t.DefinedSRID = true
   159  	return t
   160  }
   161  
   162  // MatchSRID implements SpatialColumnType interface
   163  func (t PointType) MatchSRID(v interface{}) error {
   164  	val, ok := v.(Point)
   165  	if !ok {
   166  		return sql.ErrNotPoint.New(v)
   167  	}
   168  	if !t.DefinedSRID {
   169  		return nil
   170  	} else if t.SRID == val.SRID {
   171  		return nil
   172  	}
   173  	return sql.ErrNotMatchingSRID.New(val.SRID, t.SRID)
   174  }
   175  
   176  // implementsGeometryValue implements GeometryValue interface.
   177  func (p Point) implementsGeometryValue() {}
   178  
   179  // GetSRID implements GeometryValue interface.
   180  func (p Point) GetSRID() uint32 {
   181  	return p.SRID
   182  }
   183  
   184  // SetSRID implements GeometryValue interface.
   185  func (p Point) SetSRID(srid uint32) GeometryValue {
   186  	return Point{
   187  		SRID: srid,
   188  		X:    p.X,
   189  		Y:    p.Y,
   190  	}
   191  }
   192  
   193  // Serialize implements GeometryValue interface.
   194  func (p Point) Serialize() (buf []byte) {
   195  	buf = AllocateGeoTypeBuffer(1, 0, 0)
   196  	WriteEWKBHeader(buf, p.SRID, WKBPointID)
   197  	p.WriteData(buf[EWKBHeaderSize:])
   198  	return
   199  }
   200  
   201  // WriteData implements GeometryValue interface.
   202  func (p Point) WriteData(buf []byte) int {
   203  	binary.LittleEndian.PutUint64(buf, math.Float64bits(p.X))
   204  	buf = buf[PointSize/2:]
   205  	binary.LittleEndian.PutUint64(buf, math.Float64bits(p.Y))
   206  	return PointSize
   207  }
   208  
   209  // Swap implements GeometryValue interface.
   210  // TODO: possible in place?
   211  func (p Point) Swap() GeometryValue {
   212  	return Point{
   213  		SRID: p.SRID,
   214  		X:    p.Y,
   215  		Y:    p.X,
   216  	}
   217  }
   218  
   219  // BBox implements GeometryValue interface.
   220  func (p Point) BBox() (float64, float64, float64, float64) {
   221  	return p.X, p.Y, p.X, p.Y
   222  }