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