github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/spatial/wkt.go (about)

     1  // Copyright 2020-2021 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 spatial
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/dolthub/go-mysql-server/sql/expression"
    24  	"github.com/dolthub/go-mysql-server/sql/types"
    25  )
    26  
    27  // AsWKT is a function that converts a spatial type into WKT format (alias for AsText)
    28  type AsWKT struct {
    29  	expression.UnaryExpression
    30  }
    31  
    32  var _ sql.FunctionExpression = (*AsWKT)(nil)
    33  var _ sql.CollationCoercible = (*AsWKT)(nil)
    34  
    35  // NewAsWKT creates a new point expression.
    36  func NewAsWKT(e sql.Expression) sql.Expression {
    37  	return &AsWKT{expression.UnaryExpression{Child: e}}
    38  }
    39  
    40  // FunctionName implements sql.FunctionExpression
    41  func (p *AsWKT) FunctionName() string {
    42  	return "st_aswkb"
    43  }
    44  
    45  // Description implements sql.FunctionExpression
    46  func (p *AsWKT) Description() string {
    47  	return "returns binary representation of given spatial type."
    48  }
    49  
    50  // IsNullable implements the sql.Expression interface.
    51  func (p *AsWKT) IsNullable() bool {
    52  	return p.Child.IsNullable()
    53  }
    54  
    55  // Type implements the sql.Expression interface.
    56  func (p *AsWKT) Type() sql.Type {
    57  	return types.LongText
    58  }
    59  
    60  // CollationCoercibility implements the interface sql.CollationCoercible.
    61  func (*AsWKT) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
    62  	return ctx.GetCollation(), 4
    63  }
    64  
    65  func (p *AsWKT) String() string {
    66  	return fmt.Sprintf("%s(%s)", p.FunctionName(), p.Child.String())
    67  }
    68  
    69  // WithChildren implements the Expression interface.
    70  func (p *AsWKT) WithChildren(children ...sql.Expression) (sql.Expression, error) {
    71  	if len(children) != 1 {
    72  		return nil, sql.ErrInvalidChildrenNumber.New(p, len(children), 1)
    73  	}
    74  	return NewAsWKT(children[0]), nil
    75  }
    76  
    77  // TODO: these functions could be refactored to be inside the sql.GeometryValue interface
    78  
    79  // PointToWKT converts a sql.Point to a string
    80  func PointToWKT(p types.Point, order bool) string {
    81  	x := strconv.FormatFloat(p.X, 'g', -1, 64)
    82  	y := strconv.FormatFloat(p.Y, 'g', -1, 64)
    83  	if order {
    84  		x, y = y, x
    85  	}
    86  	return fmt.Sprintf("%s %s", x, y)
    87  }
    88  
    89  // LineToWKT converts a sql.LineString to a string
    90  func LineToWKT(l types.LineString, order bool) string {
    91  	points := make([]string, len(l.Points))
    92  	for i, p := range l.Points {
    93  		points[i] = PointToWKT(p, order)
    94  	}
    95  	return strings.Join(points, ",")
    96  }
    97  
    98  // PolygonToWKT converts a sql.Polygon to a string
    99  func PolygonToWKT(p types.Polygon, order bool) string {
   100  	lines := make([]string, len(p.Lines))
   101  	for i, l := range p.Lines {
   102  		lines[i] = "(" + LineToWKT(l, order) + ")"
   103  	}
   104  	return strings.Join(lines, ",")
   105  }
   106  
   107  // MultiPointToWKT converts a sql.MultiPoint to a string
   108  func MultiPointToWKT(p types.MultiPoint, order bool) string {
   109  	points := make([]string, len(p.Points))
   110  	for i, p := range p.Points {
   111  		points[i] = PointToWKT(p, order)
   112  	}
   113  	return strings.Join(points, ",")
   114  }
   115  
   116  // MultiLineStringToWKT converts a sql.Polygon to a string
   117  func MultiLineStringToWKT(l types.MultiLineString, order bool) string {
   118  	lines := make([]string, len(l.Lines))
   119  	for i, line := range l.Lines {
   120  		lines[i] = "(" + LineToWKT(line, order) + ")"
   121  	}
   122  	return strings.Join(lines, ",")
   123  }
   124  
   125  // MultiPolygonToWKT converts a sql.Polygon to a string
   126  func MultiPolygonToWKT(p types.MultiPolygon, order bool) string {
   127  	polys := make([]string, len(p.Polygons))
   128  	for i, poly := range p.Polygons {
   129  		polys[i] = "(" + PolygonToWKT(poly, order) + ")"
   130  	}
   131  	return strings.Join(polys, ",")
   132  }
   133  
   134  // GeomCollToWKT converts a sql.Polygon to a string
   135  func GeomCollToWKT(g types.GeomColl, order bool) string {
   136  	geoms := make([]string, len(g.Geoms))
   137  	for i, geom := range g.Geoms {
   138  		switch g := geom.(type) {
   139  		case types.Point:
   140  			geoms[i] = "POINT(" + PointToWKT(g, order) + ")"
   141  		case types.LineString:
   142  			geoms[i] = "LINESTRING(" + LineToWKT(g, order) + ")"
   143  		case types.Polygon:
   144  			geoms[i] = "POLYGON(" + PolygonToWKT(g, order) + ")"
   145  		case types.MultiPoint:
   146  			geoms[i] = "MULTIPOINT(" + MultiPointToWKT(g, order) + ")"
   147  		case types.MultiLineString:
   148  			geoms[i] = "MULTILINESTRING(" + MultiLineStringToWKT(g, order) + ")"
   149  		case types.MultiPolygon:
   150  			geoms[i] = "MULTIPOLYGON(" + MultiPolygonToWKT(g, order) + ")"
   151  		case types.GeomColl:
   152  			geoms[i] = "GEOMETRYCOLLECTION(" + GeomCollToWKT(g, order) + ")"
   153  		}
   154  	}
   155  	return strings.Join(geoms, ",")
   156  }
   157  
   158  // Eval implements the sql.Expression interface.
   159  func (p *AsWKT) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   160  	// Evaluate child
   161  	val, err := p.Child.Eval(ctx, row)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	if val == nil {
   167  		return nil, nil
   168  	}
   169  
   170  	var geomType string
   171  	var data string
   172  	switch v := val.(type) {
   173  	case types.Point:
   174  		geomType = "POINT"
   175  		data = PointToWKT(v, v.SRID == types.GeoSpatialSRID)
   176  	case types.LineString:
   177  		geomType = "LINESTRING"
   178  		data = LineToWKT(v, v.SRID == types.GeoSpatialSRID)
   179  	case types.Polygon:
   180  		geomType = "POLYGON"
   181  		data = PolygonToWKT(v, v.SRID == types.GeoSpatialSRID)
   182  	case types.MultiPoint:
   183  		geomType = "MULTIPOINT"
   184  		data = MultiPointToWKT(v, v.SRID == types.GeoSpatialSRID)
   185  	case types.MultiLineString:
   186  		geomType = "MULTILINESTRING"
   187  		data = MultiLineStringToWKT(v, v.SRID == types.GeoSpatialSRID)
   188  	case types.MultiPolygon:
   189  		geomType = "MULTIPOLYGON"
   190  		data = MultiPolygonToWKT(v, v.SRID == types.GeoSpatialSRID)
   191  	case types.GeomColl:
   192  		geomType = "GEOMETRYCOLLECTION"
   193  		data = GeomCollToWKT(v, v.SRID == types.GeoSpatialSRID)
   194  	default:
   195  		return nil, sql.ErrInvalidGISData.New(p.FunctionName())
   196  	}
   197  
   198  	return fmt.Sprintf("%s(%s)", geomType, data), nil
   199  }
   200  
   201  // GeomFromText is a function that returns a point type from a WKT string
   202  type GeomFromText struct {
   203  	expression.NaryExpression
   204  }
   205  
   206  var _ sql.FunctionExpression = (*GeomFromText)(nil)
   207  var _ sql.CollationCoercible = (*GeomFromText)(nil)
   208  
   209  // NewGeomFromText creates a new point expression.
   210  func NewGeomFromText(args ...sql.Expression) (sql.Expression, error) {
   211  	if len(args) < 1 || len(args) > 3 {
   212  		return nil, sql.ErrInvalidArgumentNumber.New("ST_GEOMFROMTEXT", "1, 2, or 3", len(args))
   213  	}
   214  	return &GeomFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   215  }
   216  
   217  // FunctionName implements sql.FunctionExpression
   218  func (g *GeomFromText) FunctionName() string {
   219  	return "st_geomfromtext"
   220  }
   221  
   222  // Description implements sql.FunctionExpression
   223  func (g *GeomFromText) Description() string {
   224  	return "returns a new point from a WKT string."
   225  }
   226  
   227  // Type implements the sql.Expression interface.
   228  func (g *GeomFromText) Type() sql.Type {
   229  	// TODO: return type is determined after Eval, use Geometry for now?
   230  	return types.GeometryType{}
   231  }
   232  
   233  // CollationCoercibility implements the interface sql.CollationCoercible.
   234  func (*GeomFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   235  	return sql.Collation_binary, 4
   236  }
   237  
   238  func (g *GeomFromText) String() string {
   239  	var args = make([]string, len(g.ChildExpressions))
   240  	for i, arg := range g.ChildExpressions {
   241  		args[i] = arg.String()
   242  	}
   243  	return fmt.Sprintf("%s(%s)", g.FunctionName(), strings.Join(args, ","))
   244  }
   245  
   246  // WithChildren implements the Expression interface.
   247  func (g *GeomFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   248  	return NewGeomFromText(children...)
   249  }
   250  
   251  func TrimWKTData(s string) (string, int, error) {
   252  	// Must start with open parenthesis
   253  	if s[0] != '(' {
   254  		return "", 0, sql.ErrInvalidGISData.New()
   255  	}
   256  
   257  	// Read until all parentheses are closed
   258  	var count, end int
   259  	for count, end = 1, 1; end < len(s) && count != 0; end++ {
   260  		switch s[end] {
   261  		case '(':
   262  			count++
   263  		case ')':
   264  			count--
   265  		}
   266  	}
   267  	if count != 0 {
   268  		return "", 0, sql.ErrInvalidGISData.New()
   269  	}
   270  
   271  	// Remove parentheses, extract data, and trim
   272  	data := s[1 : end-1]
   273  	data = strings.TrimSpace(data)
   274  
   275  	return data, end, nil
   276  }
   277  
   278  // ParseWKTHeader should extract the type and data from the geometry string
   279  // `end` is used to detect extra characters after a valid geometry
   280  func ParseWKTHeader(s string) (string, string, int, error) {
   281  	// Read until first open parenthesis
   282  	start := strings.Index(s, "(")
   283  
   284  	// Bad if no parenthesis found
   285  	if start == -1 {
   286  		return "", "", 0, sql.ErrInvalidGISData.New()
   287  	}
   288  
   289  	// Get Geometry Type
   290  	geomType := s[:start]
   291  	geomType = strings.TrimSpace(geomType)
   292  	geomType = strings.ToLower(geomType)
   293  
   294  	data, end, err := TrimWKTData(s[start:])
   295  	if err != nil {
   296  		return "", "", 0, err
   297  	}
   298  
   299  	return geomType, data, start + end, nil
   300  }
   301  
   302  // WKTToPoint expects a string like this "1.2 3.4"
   303  func WKTToPoint(s string, srid uint32, order bool) (types.Point, error) {
   304  	if len(s) == 0 {
   305  		return types.Point{}, sql.ErrInvalidGISData.New()
   306  	}
   307  
   308  	// Get everything between spaces
   309  	args := strings.Fields(s)
   310  	if len(args) != 2 {
   311  		return types.Point{}, sql.ErrInvalidGISData.New()
   312  	}
   313  
   314  	x, err := strconv.ParseFloat(args[0], 64)
   315  	if err != nil {
   316  		return types.Point{}, sql.ErrInvalidGISData.New()
   317  	}
   318  
   319  	y, err := strconv.ParseFloat(args[1], 64)
   320  	if err != nil {
   321  		return types.Point{}, sql.ErrInvalidGISData.New()
   322  	}
   323  
   324  	if order {
   325  		x, y = y, x
   326  	}
   327  
   328  	return types.Point{SRID: srid, X: x, Y: y}, nil
   329  }
   330  
   331  // WKTToLine expects a string like "1.2 3.4, 5.6 7.8, ..."
   332  func WKTToLine(s string, srid uint32, order bool) (types.LineString, error) {
   333  	if len(s) == 0 {
   334  		return types.LineString{}, sql.ErrInvalidGISData.New()
   335  	}
   336  
   337  	pointStrs := strings.Split(s, ",")
   338  	var points = make([]types.Point, len(pointStrs))
   339  	var err error
   340  	for i, ps := range pointStrs {
   341  		ps = strings.TrimSpace(ps)
   342  		if points[i], err = WKTToPoint(ps, srid, order); err != nil {
   343  			return types.LineString{}, sql.ErrInvalidGISData.New()
   344  		}
   345  	}
   346  
   347  	// Create LineString object
   348  	return types.LineString{SRID: srid, Points: points}, nil
   349  }
   350  
   351  // WKTToPoly Expects a string like "(1 2, 3 4), (5 6, 7 8), ..."
   352  func WKTToPoly(s string, srid uint32, order bool) (types.Polygon, error) {
   353  	var lines []types.LineString
   354  	for {
   355  		// Get first linestring
   356  		lineStr, end, err := TrimWKTData(s)
   357  		if err != nil {
   358  			return types.Polygon{}, err
   359  		}
   360  
   361  		// Parse line
   362  		line, err := WKTToLine(lineStr, srid, order)
   363  		if err != nil {
   364  			return types.Polygon{}, sql.ErrInvalidGISData.New()
   365  		}
   366  		if !isLinearRing(line) {
   367  			return types.Polygon{}, sql.ErrInvalidGISData.New()
   368  		}
   369  		lines = append(lines, line)
   370  
   371  		// Prepare next string
   372  		s = s[end:]
   373  		s = strings.TrimSpace(s)
   374  
   375  		// Reached end
   376  		if len(s) == 0 {
   377  			break
   378  		}
   379  
   380  		// LineStrings must be comma-separated
   381  		if s[0] != ',' {
   382  			return types.Polygon{}, sql.ErrInvalidGISData.New()
   383  		}
   384  
   385  		// Drop leading comma
   386  		s = s[1:]
   387  		s = strings.TrimSpace(s)
   388  	}
   389  
   390  	return types.Polygon{SRID: srid, Lines: lines}, nil
   391  }
   392  
   393  // WKTToMPoint expects a string like "1.2 3.4, 5.6 7.8, ..."
   394  func WKTToMPoint(s string, srid uint32, order bool) (types.MultiPoint, error) {
   395  	if len(s) == 0 {
   396  		return types.MultiPoint{}, sql.ErrInvalidGISData.New()
   397  	}
   398  
   399  	pointStrs := strings.Split(s, ",")
   400  	var points = make([]types.Point, len(pointStrs))
   401  	var err error
   402  	for i, ps := range pointStrs {
   403  		ps = strings.TrimSpace(ps)
   404  		if points[i], err = WKTToPoint(ps, srid, order); err != nil {
   405  			return types.MultiPoint{}, sql.ErrInvalidGISData.New()
   406  		}
   407  	}
   408  
   409  	return types.MultiPoint{SRID: srid, Points: points}, nil
   410  }
   411  
   412  // WKTToMLine Expects a string like "(1 2, 3 4), (5 6, 7 8), ..."
   413  func WKTToMLine(s string, srid uint32, order bool) (types.MultiLineString, error) {
   414  	var lines []types.LineString
   415  	for {
   416  		// Get first linestring
   417  		lineStr, end, err := TrimWKTData(s)
   418  		if err != nil {
   419  			return types.MultiLineString{}, err
   420  		}
   421  
   422  		// Parse line
   423  		line, err := WKTToLine(lineStr, srid, order)
   424  		if err != nil {
   425  			return types.MultiLineString{}, sql.ErrInvalidGISData.New()
   426  		}
   427  		lines = append(lines, line)
   428  
   429  		// Prepare next string
   430  		s = s[end:]
   431  		s = strings.TrimSpace(s)
   432  
   433  		// Reached end
   434  		if len(s) == 0 {
   435  			break
   436  		}
   437  
   438  		// LineStrings must be comma-separated
   439  		if s[0] != ',' {
   440  			return types.MultiLineString{}, sql.ErrInvalidGISData.New()
   441  		}
   442  
   443  		// Drop leading comma
   444  		s = s[1:]
   445  		s = strings.TrimSpace(s)
   446  	}
   447  
   448  	return types.MultiLineString{SRID: srid, Lines: lines}, nil
   449  }
   450  
   451  // WKTToMPoly Expects a string like "((1 2, 3 4), (5 6, 7 8), ...), ..."
   452  func WKTToMPoly(s string, srid uint32, order bool) (types.MultiPolygon, error) {
   453  	var polys []types.Polygon
   454  	for {
   455  		// Get first polygon
   456  		polyStr, end, err := TrimWKTData(s)
   457  		if err != nil {
   458  			return types.MultiPolygon{}, err
   459  		}
   460  
   461  		// Parse poly
   462  		poly, err := WKTToPoly(polyStr, srid, order)
   463  		if err != nil {
   464  			return types.MultiPolygon{}, sql.ErrInvalidGISData.New()
   465  		}
   466  		polys = append(polys, poly)
   467  
   468  		// Prepare next string
   469  		s = s[end:]
   470  		s = strings.TrimSpace(s)
   471  
   472  		// Reached end
   473  		if len(s) == 0 {
   474  			break
   475  		}
   476  
   477  		// Polygons must be comma-separated
   478  		if s[0] != ',' {
   479  			return types.MultiPolygon{}, sql.ErrInvalidGISData.New()
   480  		}
   481  
   482  		// Drop leading comma
   483  		s = s[1:]
   484  		s = strings.TrimSpace(s)
   485  	}
   486  
   487  	return types.MultiPolygon{SRID: srid, Polygons: polys}, nil
   488  }
   489  
   490  // WKTToGeomColl Expects a string like "((1 2, 3 4), (5 6, 7 8), ...), ..."
   491  func WKTToGeomColl(s string, srid uint32, order bool) (types.GeomColl, error) {
   492  	// empty geometry collections
   493  	if len(s) == 0 {
   494  		return types.GeomColl{SRID: srid, Geoms: []types.GeometryValue{}}, nil
   495  	}
   496  
   497  	var geoms []types.GeometryValue
   498  	for {
   499  		// parse first type
   500  		geomType, data, end, err := ParseWKTHeader(s)
   501  		if err != nil {
   502  			return types.GeomColl{}, sql.ErrInvalidGISData.New()
   503  		}
   504  		var geom types.GeometryValue
   505  		switch geomType {
   506  		case "point":
   507  			geom, err = WKTToPoint(data, srid, order)
   508  		case "linestring":
   509  			geom, err = WKTToLine(data, srid, order)
   510  		case "polygon":
   511  			geom, err = WKTToPoly(data, srid, order)
   512  		case "multipoint":
   513  			geom, err = WKTToMPoint(data, srid, order)
   514  		case "multilinestring":
   515  			geom, err = WKTToMLine(data, srid, order)
   516  		case "multipolygon":
   517  			geom, err = WKTToMPoly(data, srid, order)
   518  		case "geometrycollection":
   519  			geom, err = WKTToGeomColl(data, srid, order)
   520  		default:
   521  			return types.GeomColl{}, sql.ErrInvalidGISData.New()
   522  		}
   523  		geoms = append(geoms, geom)
   524  
   525  		// Prepare next string
   526  		s = s[end:]
   527  		s = strings.TrimSpace(s)
   528  
   529  		// Reached end
   530  		if len(s) == 0 {
   531  			break
   532  		}
   533  
   534  		// Geometries must be comma-separated
   535  		if s[0] != ',' {
   536  			return types.GeomColl{}, sql.ErrInvalidGISData.New()
   537  		}
   538  
   539  		// Drop leading comma
   540  		s = s[1:]
   541  		s = strings.TrimSpace(s)
   542  	}
   543  
   544  	return types.GeomColl{SRID: srid, Geoms: geoms}, nil
   545  }
   546  
   547  // WKTToGeom expects a string in WKT format, and converts it to a geometry type
   548  func WKTToGeom(ctx *sql.Context, row sql.Row, exprs []sql.Expression, expectedGeomType string) (types.GeometryValue, error) {
   549  	val, err := exprs[0].Eval(ctx, row)
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  
   554  	if val == nil {
   555  		return nil, nil
   556  	}
   557  
   558  	s, ok := val.(string)
   559  	if !ok {
   560  		return nil, sql.ErrInvalidGISData.New()
   561  	}
   562  
   563  	s = strings.TrimSpace(s)
   564  	geomType, data, end, err := ParseWKTHeader(s)
   565  	if err != nil || end != len(s) { // detect extra characters
   566  		return nil, err
   567  	}
   568  
   569  	if expectedGeomType != "" && geomType != expectedGeomType {
   570  		return nil, sql.ErrInvalidGISData.New()
   571  	}
   572  
   573  	srid := uint32(0)
   574  	if len(exprs) >= 2 {
   575  		s, err := exprs[1].Eval(ctx, row)
   576  		if err != nil {
   577  			return nil, err
   578  		}
   579  		if s == nil {
   580  			return nil, nil
   581  		}
   582  		s, _, err = types.Int64.Convert(s)
   583  		if err != nil {
   584  			return nil, err
   585  		}
   586  		if err = types.ValidateSRID(int(s.(int64)), "st_geomfromtext"); err != nil {
   587  			return nil, err
   588  		}
   589  		srid = uint32(s.(int64))
   590  	}
   591  
   592  	order := srid == types.GeoSpatialSRID
   593  	if len(exprs) == 3 {
   594  		o, err := exprs[2].Eval(ctx, row)
   595  		if err != nil {
   596  			return nil, err
   597  		}
   598  		if o == nil {
   599  			return nil, nil
   600  		}
   601  		order, err = ParseAxisOrder(o.(string))
   602  		if err != nil {
   603  			return nil, err
   604  		}
   605  	}
   606  
   607  	switch geomType {
   608  	case "point":
   609  		return WKTToPoint(data, srid, order)
   610  	case "linestring":
   611  		return WKTToLine(data, srid, order)
   612  	case "polygon":
   613  		return WKTToPoly(data, srid, order)
   614  	case "multipoint":
   615  		return WKTToMPoint(data, srid, order)
   616  	case "multilinestring":
   617  		return WKTToMLine(data, srid, order)
   618  	case "multipolygon":
   619  		return WKTToMPoly(data, srid, order)
   620  	case "geometrycollection":
   621  		return WKTToGeomColl(data, srid, order)
   622  	default:
   623  		return nil, sql.ErrInvalidGISData.New()
   624  	}
   625  }
   626  
   627  // Eval implements the sql.Expression interface.
   628  func (g *GeomFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   629  	geom, err := WKTToGeom(ctx, row, g.ChildExpressions, "")
   630  	if sql.ErrInvalidGISData.Is(err) {
   631  		return nil, sql.ErrInvalidGISData.New(g.FunctionName())
   632  	}
   633  	return geom, err
   634  }
   635  
   636  // PointFromText is a function that returns a Point type from a WKT string
   637  type PointFromText struct {
   638  	expression.NaryExpression
   639  }
   640  
   641  var _ sql.FunctionExpression = (*PointFromText)(nil)
   642  var _ sql.CollationCoercible = (*PointFromText)(nil)
   643  
   644  // NewPointFromText creates a new point expression.
   645  func NewPointFromText(args ...sql.Expression) (sql.Expression, error) {
   646  	if len(args) < 1 || len(args) > 3 {
   647  		return nil, sql.ErrInvalidArgumentNumber.New("ST_POINTFROMTEXT", "1, 2, or 3", len(args))
   648  	}
   649  	return &PointFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   650  }
   651  
   652  // FunctionName implements sql.FunctionExpression
   653  func (p *PointFromText) FunctionName() string {
   654  	return "st_pointfromtext"
   655  }
   656  
   657  // Description implements sql.FunctionExpression
   658  func (p *PointFromText) Description() string {
   659  	return "returns a new point from a WKT string."
   660  }
   661  
   662  // Type implements the sql.Expression interface.
   663  func (p *PointFromText) Type() sql.Type {
   664  	return types.PointType{}
   665  }
   666  
   667  // CollationCoercibility implements the interface sql.CollationCoercible.
   668  func (*PointFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   669  	return sql.Collation_binary, 4
   670  }
   671  
   672  func (p *PointFromText) String() string {
   673  	var args = make([]string, len(p.ChildExpressions))
   674  	for i, arg := range p.ChildExpressions {
   675  		args[i] = arg.String()
   676  	}
   677  	return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ","))
   678  }
   679  
   680  // WithChildren implements the Expression interface.
   681  func (p *PointFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   682  	return NewPointFromText(children...)
   683  }
   684  
   685  // Eval implements the sql.Expression interface.
   686  func (p *PointFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   687  	point, err := WKTToGeom(ctx, row, p.ChildExpressions, "point")
   688  	if sql.ErrInvalidGISData.Is(err) {
   689  		return nil, sql.ErrInvalidGISData.New(p.FunctionName())
   690  	}
   691  	return point, err
   692  }
   693  
   694  // LineFromText is a function that returns a LineString type from a WKT string
   695  type LineFromText struct {
   696  	expression.NaryExpression
   697  }
   698  
   699  var _ sql.FunctionExpression = (*LineFromText)(nil)
   700  var _ sql.CollationCoercible = (*LineFromText)(nil)
   701  
   702  // NewLineFromText creates a new point expression.
   703  func NewLineFromText(args ...sql.Expression) (sql.Expression, error) {
   704  	if len(args) < 1 || len(args) > 3 {
   705  		return nil, sql.ErrInvalidArgumentNumber.New("ST_LINEFROMTEXT", "1 or 2", len(args))
   706  	}
   707  	return &LineFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   708  }
   709  
   710  // FunctionName implements sql.FunctionExpression
   711  func (l *LineFromText) FunctionName() string {
   712  	return "st_linefromtext"
   713  }
   714  
   715  // Description implements sql.FunctionExpression
   716  func (l *LineFromText) Description() string {
   717  	return "returns a new line from a WKT string."
   718  }
   719  
   720  // Type implements the sql.Expression interface.
   721  func (l *LineFromText) Type() sql.Type {
   722  	return types.LineStringType{}
   723  }
   724  
   725  // CollationCoercibility implements the interface sql.CollationCoercible.
   726  func (*LineFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   727  	return sql.Collation_binary, 4
   728  }
   729  
   730  func (l *LineFromText) String() string {
   731  	var args = make([]string, len(l.ChildExpressions))
   732  	for i, arg := range l.ChildExpressions {
   733  		args[i] = arg.String()
   734  	}
   735  	return fmt.Sprintf("%s(%s)", l.FunctionName(), strings.Join(args, ","))
   736  }
   737  
   738  // WithChildren implements the Expression interface.
   739  func (l *LineFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   740  	return NewLineFromText(children...)
   741  }
   742  
   743  // Eval implements the sql.Expression interface.
   744  func (l *LineFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   745  	line, err := WKTToGeom(ctx, row, l.ChildExpressions, "linestring")
   746  	if sql.ErrInvalidGISData.Is(err) {
   747  		return nil, sql.ErrInvalidGISData.New(l.FunctionName())
   748  	}
   749  	return line, err
   750  }
   751  
   752  // PolyFromText is a function that returns a Polygon type from a WKT string
   753  type PolyFromText struct {
   754  	expression.NaryExpression
   755  }
   756  
   757  var _ sql.FunctionExpression = (*PolyFromText)(nil)
   758  var _ sql.CollationCoercible = (*PolyFromText)(nil)
   759  
   760  // NewPolyFromText creates a new polygon expression.
   761  func NewPolyFromText(args ...sql.Expression) (sql.Expression, error) {
   762  	if len(args) < 1 || len(args) > 3 {
   763  		return nil, sql.ErrInvalidArgumentNumber.New("ST_POLYFROMTEXT", "1, 2, or 3", len(args))
   764  	}
   765  	return &PolyFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   766  }
   767  
   768  // FunctionName implements sql.FunctionExpression
   769  func (p *PolyFromText) FunctionName() string {
   770  	return "st_polyfromtext"
   771  }
   772  
   773  // Description implements sql.FunctionExpression
   774  func (p *PolyFromText) Description() string {
   775  	return "returns a new polygon from a WKT string."
   776  }
   777  
   778  // Type implements the sql.Expression interface.
   779  func (p *PolyFromText) Type() sql.Type {
   780  	return types.PolygonType{}
   781  }
   782  
   783  // CollationCoercibility implements the interface sql.CollationCoercible.
   784  func (*PolyFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   785  	return sql.Collation_binary, 4
   786  }
   787  
   788  func (p *PolyFromText) String() string {
   789  	var args = make([]string, len(p.ChildExpressions))
   790  	for i, arg := range p.ChildExpressions {
   791  		args[i] = arg.String()
   792  	}
   793  	return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ","))
   794  }
   795  
   796  // WithChildren implements the Expression interface.
   797  func (p *PolyFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   798  	return NewPolyFromText(children...)
   799  }
   800  
   801  // Eval implements the sql.Expression interface.
   802  func (p *PolyFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   803  	poly, err := WKTToGeom(ctx, row, p.ChildExpressions, "polygon")
   804  	if sql.ErrInvalidGISData.Is(err) {
   805  		return nil, sql.ErrInvalidGISData.New(p.FunctionName())
   806  	}
   807  	return poly, err
   808  }
   809  
   810  // MultiPoint is a function that returns a MultiPoint type from a WKT string
   811  type MPointFromText struct {
   812  	expression.NaryExpression
   813  }
   814  
   815  var _ sql.FunctionExpression = (*MPointFromText)(nil)
   816  var _ sql.CollationCoercible = (*MPointFromText)(nil)
   817  
   818  // NewMPointFromText creates a new MultiPoint expression.
   819  func NewMPointFromText(args ...sql.Expression) (sql.Expression, error) {
   820  	if len(args) < 1 || len(args) > 3 {
   821  		return nil, sql.ErrInvalidArgumentNumber.New("ST_MULTIPOINTFROMTEXT", "1 or 2", len(args))
   822  	}
   823  	return &MPointFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   824  }
   825  
   826  // FunctionName implements sql.FunctionExpression
   827  func (p *MPointFromText) FunctionName() string {
   828  	return "st_mpointfromtext"
   829  }
   830  
   831  // Description implements sql.FunctionExpression
   832  func (p *MPointFromText) Description() string {
   833  	return "returns a new multipoint from a WKT string."
   834  }
   835  
   836  // Type implements the sql.Expression interface.
   837  func (p *MPointFromText) Type() sql.Type {
   838  	return types.MultiPointType{}
   839  }
   840  
   841  // CollationCoercibility implements the interface sql.CollationCoercible.
   842  func (*MPointFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   843  	return sql.Collation_binary, 4
   844  }
   845  
   846  func (p *MPointFromText) String() string {
   847  	var args = make([]string, len(p.ChildExpressions))
   848  	for i, arg := range p.ChildExpressions {
   849  		args[i] = arg.String()
   850  	}
   851  	return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ","))
   852  }
   853  
   854  // WithChildren implements the Expression interface.
   855  func (p *MPointFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   856  	return NewMPointFromText(children...)
   857  }
   858  
   859  // Eval implements the sql.Expression interface.
   860  func (p *MPointFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   861  	line, err := WKTToGeom(ctx, row, p.ChildExpressions, "multipoint")
   862  	if sql.ErrInvalidGISData.Is(err) {
   863  		return nil, sql.ErrInvalidGISData.New(p.FunctionName())
   864  	}
   865  	return line, err
   866  }
   867  
   868  // MLineFromText is a function that returns a MultiLineString type from a WKT string
   869  type MLineFromText struct {
   870  	expression.NaryExpression
   871  }
   872  
   873  var _ sql.FunctionExpression = (*MLineFromText)(nil)
   874  var _ sql.CollationCoercible = (*MLineFromText)(nil)
   875  
   876  // NewMLineFromText creates a new multilinestring expression.
   877  func NewMLineFromText(args ...sql.Expression) (sql.Expression, error) {
   878  	if len(args) < 1 || len(args) > 3 {
   879  		return nil, sql.ErrInvalidArgumentNumber.New("ST_MLINEFROMTEXT", "1 or 2", len(args))
   880  	}
   881  	return &MLineFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   882  }
   883  
   884  // FunctionName implements sql.FunctionExpression
   885  func (l *MLineFromText) FunctionName() string {
   886  	return "st_mlinefromtext"
   887  }
   888  
   889  // Description implements sql.FunctionExpression
   890  func (l *MLineFromText) Description() string {
   891  	return "returns a new multi line from a WKT string."
   892  }
   893  
   894  // Type implements the sql.Expression interface.
   895  func (l *MLineFromText) Type() sql.Type {
   896  	return types.MultiLineStringType{}
   897  }
   898  
   899  // CollationCoercibility implements the interface sql.CollationCoercible.
   900  func (*MLineFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   901  	return sql.Collation_binary, 4
   902  }
   903  
   904  func (l *MLineFromText) String() string {
   905  	var args = make([]string, len(l.ChildExpressions))
   906  	for i, arg := range l.ChildExpressions {
   907  		args[i] = arg.String()
   908  	}
   909  	return fmt.Sprintf("%s(%s)", l.FunctionName(), strings.Join(args, ","))
   910  }
   911  
   912  // WithChildren implements the Expression interface.
   913  func (l *MLineFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   914  	return NewMLineFromText(children...)
   915  }
   916  
   917  // Eval implements the sql.Expression interface.
   918  func (l *MLineFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   919  	mline, err := WKTToGeom(ctx, row, l.ChildExpressions, "multilinestring")
   920  	if sql.ErrInvalidGISData.Is(err) {
   921  		return nil, sql.ErrInvalidGISData.New(l.FunctionName())
   922  	}
   923  	return mline, err
   924  }
   925  
   926  // MPolyFromText is a function that returns a MultiPolygon type from a WKT string
   927  type MPolyFromText struct {
   928  	expression.NaryExpression
   929  }
   930  
   931  var _ sql.FunctionExpression = (*MPolyFromText)(nil)
   932  var _ sql.CollationCoercible = (*MPolyFromText)(nil)
   933  
   934  // NewMPolyFromText creates a new multilinestring expression.
   935  func NewMPolyFromText(args ...sql.Expression) (sql.Expression, error) {
   936  	if len(args) < 1 || len(args) > 3 {
   937  		return nil, sql.ErrInvalidArgumentNumber.New("ST_MPOLYFROMTEXT", "1 or 2", len(args))
   938  	}
   939  	return &MPolyFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   940  }
   941  
   942  // FunctionName implements sql.FunctionExpression
   943  func (p *MPolyFromText) FunctionName() string {
   944  	return "st_mpolyfromtext"
   945  }
   946  
   947  // Description implements sql.FunctionExpression
   948  func (p *MPolyFromText) Description() string {
   949  	return "returns a new multipolygon from a WKT string."
   950  }
   951  
   952  // Type implements the sql.Expression interface.
   953  func (p *MPolyFromText) Type() sql.Type {
   954  	return types.MultiPolygonType{}
   955  }
   956  
   957  // CollationCoercibility implements the interface sql.CollationCoercible.
   958  func (*MPolyFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   959  	return sql.Collation_binary, 4
   960  }
   961  
   962  func (p *MPolyFromText) String() string {
   963  	var args = make([]string, len(p.ChildExpressions))
   964  	for i, arg := range p.ChildExpressions {
   965  		args[i] = arg.String()
   966  	}
   967  	return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ","))
   968  }
   969  
   970  // WithChildren implements the Expression interface.
   971  func (p *MPolyFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   972  	return NewMPolyFromText(children...)
   973  }
   974  
   975  // Eval implements the sql.Expression interface.
   976  func (p *MPolyFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   977  	mpoly, err := WKTToGeom(ctx, row, p.ChildExpressions, "multipolygon")
   978  	if sql.ErrInvalidGISData.Is(err) {
   979  		return nil, sql.ErrInvalidGISData.New(p.FunctionName())
   980  	}
   981  	return mpoly, err
   982  }
   983  
   984  // GeomCollFromText is a function that returns a MultiPolygon type from a WKT string
   985  type GeomCollFromText struct {
   986  	expression.NaryExpression
   987  }
   988  
   989  var _ sql.FunctionExpression = (*GeomCollFromText)(nil)
   990  var _ sql.CollationCoercible = (*GeomCollFromText)(nil)
   991  
   992  // NewGeomCollFromText creates a new multilinestring expression.
   993  func NewGeomCollFromText(args ...sql.Expression) (sql.Expression, error) {
   994  	if len(args) < 1 || len(args) > 3 {
   995  		return nil, sql.ErrInvalidArgumentNumber.New("ST_GeomCollFromText", "1 or 2", len(args))
   996  	}
   997  	return &MPolyFromText{expression.NaryExpression{ChildExpressions: args}}, nil
   998  }
   999  
  1000  // FunctionName implements sql.FunctionExpression
  1001  func (p *GeomCollFromText) FunctionName() string {
  1002  	return "st_geomcollfromtext"
  1003  }
  1004  
  1005  // Description implements sql.FunctionExpression
  1006  func (p *GeomCollFromText) Description() string {
  1007  	return "returns a new geometry collection from a WKT string."
  1008  }
  1009  
  1010  // Type implements the sql.Expression interface.
  1011  func (p *GeomCollFromText) Type() sql.Type {
  1012  	return types.GeomCollType{}
  1013  }
  1014  
  1015  // CollationCoercibility implements the interface sql.CollationCoercible.
  1016  func (*GeomCollFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
  1017  	return sql.Collation_binary, 4
  1018  }
  1019  
  1020  func (p *GeomCollFromText) String() string {
  1021  	var args = make([]string, len(p.ChildExpressions))
  1022  	for i, arg := range p.ChildExpressions {
  1023  		args[i] = arg.String()
  1024  	}
  1025  	return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ","))
  1026  }
  1027  
  1028  // WithChildren implements the Expression interface.
  1029  func (p *GeomCollFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) {
  1030  	return NewGeomFromText(children...)
  1031  }
  1032  
  1033  // Eval implements the sql.Expression interface.
  1034  func (p *GeomCollFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
  1035  	geom, err := WKTToGeom(ctx, row, p.ChildExpressions, "geometrycollection")
  1036  	if sql.ErrInvalidGISData.Is(err) {
  1037  		return nil, sql.ErrInvalidGISData.New(p.FunctionName())
  1038  	}
  1039  	return geom, err
  1040  }