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  }