github.com/dolthub/go-mysql-server@v0.18.0/sql/types/geometrycollection.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 "math" 19 "reflect" 20 21 "github.com/dolthub/vitess/go/sqltypes" 22 "github.com/dolthub/vitess/go/vt/proto/query" 23 "gopkg.in/src-d/go-errors.v1" 24 25 "github.com/dolthub/go-mysql-server/sql" 26 ) 27 28 // GeomCollType represents the GeometryCollection type. 29 // https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html 30 // The type of the returned value is GeomColl. 31 type GeomCollType struct { 32 SRID uint32 33 DefinedSRID bool 34 } 35 36 // GeomColl is the value type returned from GeomCollType. Implements GeometryValue. 37 type GeomColl struct { 38 SRID uint32 39 Geoms []GeometryValue 40 } 41 42 var _ sql.Type = GeomCollType{} 43 var _ sql.SpatialColumnType = GeomCollType{} 44 var _ sql.CollationCoercible = GeomCollType{} 45 var _ GeometryValue = GeomColl{} 46 47 var ( 48 ErrNotGeomColl = errors.NewKind("value of type %T is not a point") 49 50 geomcollValueType = reflect.TypeOf(GeomColl{}) 51 ) 52 53 // Compare implements Type interface. 54 func (t GeomCollType) Compare(a interface{}, b interface{}) (int, error) { 55 return GeometryType{}.Compare(a, b) 56 } 57 58 // Convert implements Type interface. 59 func (t GeomCollType) Convert(v interface{}) (interface{}, sql.ConvertInRange, error) { 60 // Allow null 61 if v == nil { 62 return nil, sql.InRange, nil 63 } 64 // Handle conversions 65 switch val := v.(type) { 66 case []byte: 67 // Parse header 68 srid, isBig, geomType, err := DeserializeEWKBHeader(val) 69 if err != nil { 70 return nil, sql.OutOfRange, err 71 } 72 // Throw error if not marked as geometry collection 73 if geomType != WKBGeomCollID { 74 return nil, sql.OutOfRange, sql.ErrInvalidGISData.New("GeomCollType.Convert") 75 } 76 // Parse data section 77 geom, _, err := DeserializeGeomColl(val[EWKBHeaderSize:], isBig, srid) 78 if err != nil { 79 return nil, sql.OutOfRange, err 80 } 81 return geom, sql.InRange, nil 82 case string: 83 return t.Convert([]byte(val)) 84 case GeomColl: 85 if err := t.MatchSRID(val); err != nil { 86 return nil, sql.OutOfRange, err 87 } 88 return val, sql.InRange, nil 89 default: 90 return nil, sql.OutOfRange, sql.ErrSpatialTypeConversion.New() 91 } 92 } 93 94 // Equals implements the Type interface. 95 func (t GeomCollType) Equals(otherType sql.Type) bool { 96 _, ok := otherType.(GeomCollType) 97 return ok 98 } 99 100 // MaxTextResponseByteLength implements the Type interface 101 func (t GeomCollType) MaxTextResponseByteLength(_ *sql.Context) uint32 { 102 return GeometryMaxByteLength 103 } 104 105 // Promote implements the Type interface. 106 func (t GeomCollType) Promote() sql.Type { 107 return t 108 } 109 110 // SQL implements Type interface. 111 func (t GeomCollType) SQL(ctx *sql.Context, dest []byte, v interface{}) (sqltypes.Value, error) { 112 if v == nil { 113 return sqltypes.NULL, nil 114 } 115 116 v, _, err := t.Convert(v) 117 if err != nil { 118 return sqltypes.Value{}, nil 119 } 120 121 buf := v.(GeomColl).Serialize() 122 123 return sqltypes.MakeTrusted(sqltypes.Geometry, buf), nil 124 } 125 126 // String implements Type interface. 127 func (t GeomCollType) String() string { 128 return "geometrycollection" 129 } 130 131 // Type implements Type interface. 132 func (t GeomCollType) Type() query.Type { 133 return sqltypes.Geometry 134 } 135 136 // Zero implements Type interface. 137 func (t GeomCollType) Zero() interface{} { 138 return GeomColl{} 139 } 140 141 // ValueType implements Type interface. 142 func (t GeomCollType) ValueType() reflect.Type { 143 return geomcollValueType 144 } 145 146 // CollationCoercibility implements sql.CollationCoercible interface. 147 func (GeomCollType) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 148 return sql.Collation_binary, 5 149 } 150 151 // GetSpatialTypeSRID implements SpatialColumnType interface. 152 func (t GeomCollType) GetSpatialTypeSRID() (uint32, bool) { 153 return t.SRID, t.DefinedSRID 154 } 155 156 // SetSRID implements SpatialColumnType interface. 157 func (t GeomCollType) SetSRID(v uint32) sql.Type { 158 t.SRID = v 159 t.DefinedSRID = true 160 return t 161 } 162 163 // MatchSRID implements SpatialColumnType interface 164 func (t GeomCollType) MatchSRID(v interface{}) error { 165 val, ok := v.(GeomColl) 166 if !ok { 167 return ErrNotGeomColl.New(v) 168 } 169 if !t.DefinedSRID { 170 return nil 171 } else if t.SRID == val.SRID { 172 return nil 173 } 174 return sql.ErrNotMatchingSRID.New(val.SRID, t.SRID) 175 } 176 177 // implementsGeometryValue implements GeometryValue interface. 178 func (g GeomColl) implementsGeometryValue() {} 179 180 // GetSRID implements GeometryValue interface. 181 func (g GeomColl) GetSRID() uint32 { 182 return g.SRID 183 } 184 185 // SetSRID implements GeometryValue interface. 186 func (g GeomColl) SetSRID(srid uint32) GeometryValue { 187 geoms := make([]GeometryValue, len(g.Geoms)) 188 for i, geom := range g.Geoms { 189 geoms[i] = geom.SetSRID(srid) 190 } 191 return GeomColl{ 192 SRID: srid, 193 Geoms: geoms, 194 } 195 } 196 197 // CalculateSize is a helper method to determine how much space to allocate for geometry collections 198 // TODO: recursion could be better; possible to expand to fit all types 199 func (g GeomColl) CalculateSize() (numPoints int, numCounts int, numHeaders int) { 200 for _, geom := range g.Geoms { 201 switch g := geom.(type) { 202 case Point: 203 numPoints += 1 204 numHeaders += 1 205 case LineString: 206 numPoints += len(g.Points) 207 numCounts += 1 208 numHeaders += 1 209 case Polygon: 210 for _, l := range g.Lines { 211 numPoints += len(l.Points) 212 numCounts += 1 213 } 214 numCounts += 1 215 numHeaders += 1 216 case MultiPoint: 217 numPoints += len(g.Points) 218 numCounts += 1 219 numHeaders += len(g.Points) + 1 220 case MultiLineString: 221 for _, l := range g.Lines { 222 numPoints += len(l.Points) 223 numCounts += 1 224 } 225 numCounts += 1 226 numHeaders += len(g.Lines) + 1 227 case MultiPolygon: 228 for _, p := range g.Polygons { 229 for _, l := range p.Lines { 230 numPoints += len(l.Points) 231 numCounts += 1 232 } 233 numCounts += 1 234 } 235 numCounts += 1 236 numHeaders += len(g.Polygons) + 1 237 case GeomColl: 238 p, c, h := g.CalculateSize() 239 numPoints += p 240 numCounts += c + 1 241 numHeaders += h + 1 242 } 243 } 244 return 245 } 246 247 // Serialize implements GeometryValue interface. 248 // TODO: actually count all points to allocate 249 func (g GeomColl) Serialize() (buf []byte) { 250 numPoints, numCounts, numHeaders := g.CalculateSize() 251 buf = AllocateGeoTypeBuffer(numPoints, numCounts+1, numHeaders) 252 WriteEWKBHeader(buf, g.SRID, WKBGeomCollID) 253 g.WriteData(buf[EWKBHeaderSize:]) 254 return 255 } 256 257 // WriteData implements GeometryValue interface. 258 func (g GeomColl) WriteData(buf []byte) int { 259 WriteCount(buf, uint32(len(g.Geoms))) 260 buf = buf[CountSize:] 261 count := CountSize 262 for _, geom := range g.Geoms { 263 var typ uint32 264 switch geom.(type) { 265 case Point: 266 typ = WKBPointID 267 case LineString: 268 typ = WKBLineID 269 case Polygon: 270 typ = WKBPolyID 271 case MultiPoint: 272 typ = WKBMultiPointID 273 case MultiLineString: 274 typ = WKBMultiLineID 275 case MultiPolygon: 276 typ = WKBMultiPolyID 277 case GeomColl: 278 typ = WKBGeomCollID 279 } 280 WriteWKBHeader(buf, typ) 281 buf = buf[WKBHeaderSize:] 282 c := geom.WriteData(buf) 283 buf = buf[c:] 284 count += WKBHeaderSize + c 285 } 286 return count 287 } 288 289 // Swap implements GeometryValue interface. 290 func (g GeomColl) Swap() GeometryValue { 291 geoms := make([]GeometryValue, len(g.Geoms)) 292 for i, g := range g.Geoms { 293 geoms[i] = g.Swap() 294 } 295 return GeomColl{ 296 SRID: g.SRID, 297 Geoms: geoms, 298 } 299 } 300 301 // BBox implements GeometryValue interface. 302 func (g GeomColl) BBox() (float64, float64, float64, float64) { 303 minX, minY, maxX, maxY := math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64 304 for _, g := range g.Geoms { 305 gMinX, gMinY, gMaxX, gMaxY := g.BBox() 306 minX = math.Min(minX, gMinX) 307 minY = math.Min(minY, gMinY) 308 maxX = math.Max(maxX, gMaxX) 309 maxY = math.Max(maxY, gMaxY) 310 } 311 return minX, minY, maxX, maxY 312 }