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 }