diff --git a/enginetest/memory_engine_test.go b/enginetest/memory_engine_test.go index a1fcf8f5bb..d8028fad7c 100644 --- a/enginetest/memory_engine_test.go +++ b/enginetest/memory_engine_test.go @@ -145,11 +145,11 @@ func TestSingleQueryPrepared(t *testing.T) { Query: `SELECT ST_SRID(g, 0) from geometry_table order by i`, Expected: []sql.Row{ {sql.Point{X: 1, Y: 2}}, - {sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, - {sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, {sql.Point{X: 1, Y: 2}}, - {sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, - {sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, }, } diff --git a/enginetest/queries/insert_queries.go b/enginetest/queries/insert_queries.go index f42b301e58..ae14f984a9 100644 --- a/enginetest/queries/insert_queries.go +++ b/enginetest/queries/insert_queries.go @@ -641,38 +641,38 @@ var SpatialInsertQueries = []WriteQueryTest{ WriteQuery: "INSERT INTO line_table VALUES (2, LINESTRING(POINT(1,2),POINT(3,4)));", ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM line_table;", - ExpectedSelect: []sql.Row{{0, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, {1, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}}, {2, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, + ExpectedSelect: []sql.Row{{0, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, {1, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}}, {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, }, { WriteQuery: "INSERT INTO line_table VALUES (2, 0x00000000010200000002000000000000000000F03F000000000000004000000000000008400000000000001040);", ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM line_table;", - ExpectedSelect: []sql.Row{{0, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, {1, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}}, {2, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, + ExpectedSelect: []sql.Row{{0, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, {1, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}}, {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, }, { WriteQuery: "INSERT INTO polygon_table VALUES (1, POLYGON(LINESTRING(POINT(1,1),POINT(1,-1),POINT(-1,-1),POINT(-1,1),POINT(1,1))));", ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM polygon_table;", - ExpectedSelect: []sql.Row{{0, sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, {1, sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, + ExpectedSelect: []sql.Row{{0, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, {1, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, }, { WriteQuery: "INSERT INTO polygon_table VALUES (1, 0x0000000001030000000100000005000000000000000000F03F000000000000F03F000000000000F03F000000000000F0BF000000000000F0BF000000000000F0BF000000000000F0BF000000000000F03F000000000000F03F000000000000F03F);", ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM polygon_table;", - ExpectedSelect: []sql.Row{{0, sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, {1, sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, + ExpectedSelect: []sql.Row{{0, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, {1, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, }, { WriteQuery: "INSERT INTO geometry_table VALUES (7, POINT(123.456,7.89));", ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM geometry_table;", ExpectedSelect: []sql.Row{ - {1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}}, - {5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}}, - {6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, - {7, sql.Geometry{Inner: sql.Point{X: 123.456, Y: 7.89}}}, + {1, sql.Point{X: 1, Y: 2}}, + {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {4, sql.Point{SRID: 4326, X: 1, Y: 2}}, + {5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {7, sql.Point{X: 123.456, Y: 7.89}}, }, }, { @@ -680,13 +680,13 @@ var SpatialInsertQueries = []WriteQueryTest{ ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM geometry_table;", ExpectedSelect: []sql.Row{ - {1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}}, - {5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}}, - {6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, - {7, sql.Geometry{Inner: sql.Point{X: 123.456, Y: 7.89}}}, + {1, sql.Point{X: 1, Y: 2}}, + {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {4, sql.Point{SRID: 4326, X: 1, Y: 2}}, + {5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {7, sql.Point{X: 123.456, Y: 7.89}}, }, }, { @@ -694,13 +694,13 @@ var SpatialInsertQueries = []WriteQueryTest{ ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM geometry_table;", ExpectedSelect: []sql.Row{ - {1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}}, - {5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}}, - {6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, - {7, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, + {1, sql.Point{X: 1, Y: 2}}, + {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {4, sql.Point{SRID: 4326, X: 1, Y: 2}}, + {5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {7, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, }, }, { @@ -708,13 +708,13 @@ var SpatialInsertQueries = []WriteQueryTest{ ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM geometry_table;", ExpectedSelect: []sql.Row{ - {1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}}, - {5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}}, - {6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, - {7, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, + {1, sql.Point{X: 1, Y: 2}}, + {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {4, sql.Point{SRID: 4326, X: 1, Y: 2}}, + {5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {7, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, }, }, { @@ -722,13 +722,13 @@ var SpatialInsertQueries = []WriteQueryTest{ ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM geometry_table;", ExpectedSelect: []sql.Row{ - {1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}}, - {5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}}, - {6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, - {7, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, + {1, sql.Point{X: 1, Y: 2}}, + {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {4, sql.Point{SRID: 4326, X: 1, Y: 2}}, + {5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {7, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}, }, }, { @@ -736,13 +736,13 @@ var SpatialInsertQueries = []WriteQueryTest{ ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}}, SelectQuery: "SELECT * FROM geometry_table;", ExpectedSelect: []sql.Row{ - {1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}}, - {5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}}, - {6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, - {7, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, + {1, sql.Point{X: 1, Y: 2}}, + {2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {4, sql.Point{SRID: 4326, X: 1, Y: 2}}, + {5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {7, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}, }, }, } diff --git a/enginetest/queries/queries.go b/enginetest/queries/queries.go index e71ad0c3bd..fe96de25e9 100644 --- a/enginetest/queries/queries.go +++ b/enginetest/queries/queries.go @@ -99,11 +99,11 @@ var SpatialQueryTests = []QueryTest{ }, { Query: `SELECT ST_GEOMFROMWKB(ST_ASWKB(LINESTRING(POINT(1.2,3.45),point(67.8,9))))`, - Expected: []sql.Row{{sql.Linestring{Points: []sql.Point{{X: 1.2, Y: 3.45}, {X: 67.8, Y: 9}}}}}, + Expected: []sql.Row{{sql.LineString{Points: []sql.Point{{X: 1.2, Y: 3.45}, {X: 67.8, Y: 9}}}}}, }, { Query: `SELECT ST_GEOMFROMWKB(ST_ASWKB(POLYGON(LINESTRING(POINT(0,0),POINT(2,2),POINT(1,1),POINT(0,0)))))`, - Expected: []sql.Row{{sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 2, Y: 2}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, + Expected: []sql.Row{{sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 2, Y: 2}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, }, { Query: `SELECT ST_ASWKT(p) from point_table`, @@ -130,11 +130,11 @@ var SpatialQueryTests = []QueryTest{ }, { Query: `SELECT ST_GEOMFROMTEXT(ST_ASWKT(LINESTRING(POINT(1.1,2.22),POINT(3.333,4.4444))))`, - Expected: []sql.Row{{sql.Linestring{Points: []sql.Point{{X: 1.1, Y: 2.22}, {X: 3.333, Y: 4.4444}}}}}, + Expected: []sql.Row{{sql.LineString{Points: []sql.Point{{X: 1.1, Y: 2.22}, {X: 3.333, Y: 4.4444}}}}}, }, { Query: `SELECT ST_GEOMFROMTEXT(ST_ASWKT(POLYGON(LINESTRING(POINT(1.2, 3.4),POINT(2.5, -6.7),POINT(33, 44),POINT(1.2,3.4)))))`, - Expected: []sql.Row{{sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 1.2, Y: 3.4}, {X: 2.5, Y: -6.7}, {X: 33, Y: 44}, {X: 1.2, Y: 3.4}}}}}}}, + Expected: []sql.Row{{sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 1.2, Y: 3.4}, {X: 2.5, Y: -6.7}, {X: 33, Y: 44}, {X: 1.2, Y: 3.4}}}}}}}, }, { Query: `SELECT ST_X(POINT(1,2))`, @@ -191,14 +191,14 @@ var SpatialQueryTests = []QueryTest{ { Query: `SELECT ST_SRID(l, 4326) from line_table ORDER BY l`, Expected: []sql.Row{ - {sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, - {sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}, {SRID: 4326, X: 5, Y: 6}}}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}, {SRID: 4326, X: 5, Y: 6}}}}, }, }, { Query: `SELECT ST_SRID(p, 4326) from polygon_table`, Expected: []sql.Row{ - {sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, }, }, { @@ -206,10 +206,10 @@ var SpatialQueryTests = []QueryTest{ Expected: []sql.Row{ {sql.Point{SRID: 4326, X: 2, Y: 1}}, {sql.Point{SRID: 4326, X: 56.789, Y: 123.45}}, - {sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}}}}, - {sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2.345, Y: 1.23}, {SRID: 4326, X: 4.56, Y: 3.56789}}}}, - {sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2.2, Y: 1.1}, {SRID: 4326, X: 4.4, Y: 3.3}, {SRID: 4326, X: 6.6, Y: 5.5}, {SRID: 4326, X: 2.2, Y: 1.1}}}}}}, - {sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 2, Y: 2}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}}}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2.345, Y: 1.23}, {SRID: 4326, X: 4.56, Y: 3.56789}}}}, + {sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2.2, Y: 1.1}, {SRID: 4326, X: 4.4, Y: 3.3}, {SRID: 4326, X: 6.6, Y: 5.5}, {SRID: 4326, X: 2.2, Y: 1.1}}}}}}, + {sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 2, Y: 2}, {SRID: 4326, X: 0, Y: 0}}}}}}, }, }, { @@ -251,14 +251,14 @@ var SpatialQueryTests = []QueryTest{ { Query: `SELECT ST_GEOMFROMGEOJSON(ST_ASGEOJSON(l)) from line_table`, Expected: []sql.Row{ - {sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}}}}, - {sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}, {SRID: 4326, X: 6, Y: 5}}}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}}}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}, {SRID: 4326, X: 6, Y: 5}}}}, }, }, { Query: `SELECT ST_GEOMFROMGEOJSON(ST_ASGEOJSON(p)) from polygon_table`, Expected: []sql.Row{ - {sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 1, Y: 0}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, + {sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 1, Y: 0}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, }, }, { @@ -289,14 +289,14 @@ var SpatialQueryTests = []QueryTest{ { Query: `SELECT ST_SWAPXY(l) from line_table`, Expected: []sql.Row{ - {sql.Linestring{Points: []sql.Point{{X: 2, Y: 1}, {X: 4, Y: 3}}}}, - {sql.Linestring{Points: []sql.Point{{X: 2, Y: 1}, {X: 4, Y: 3}, {X: 6, Y: 5}}}}, + {sql.LineString{Points: []sql.Point{{X: 2, Y: 1}, {X: 4, Y: 3}}}}, + {sql.LineString{Points: []sql.Point{{X: 2, Y: 1}, {X: 4, Y: 3}, {X: 6, Y: 5}}}}, }, }, { Query: `SELECT ST_SWAPXY(p) from polygon_table`, Expected: []sql.Row{ - {sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, }, }, { @@ -305,9 +305,9 @@ var SpatialQueryTests = []QueryTest{ {"POINT(1 2)"}, {"LINESTRING(1 2,3 4)"}, {"POLYGON((0 0,0 1,1 1,0 0))"}, - {"POINT(1 2)"}, - {"LINESTRING(1 2,3 4)"}, - {"POLYGON((0 0,0 1,1 1,0 0))"}, + {"POINT(2 1)"}, + {"LINESTRING(2 1,4 3)"}, + {"POLYGON((0 0,1 0,1 1,0 0))"}, }, }, { @@ -335,12 +335,12 @@ var SpatialQueryTests = []QueryTest{ { Query: `SELECT ST_SRID(g, 0) from geometry_table order by i`, Expected: []sql.Row{ - {sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}}, - {sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}}, - {sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, + {sql.Point{X: 1, Y: 2}}, + {sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {sql.Point{X: 1, Y: 2}}, + {sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, + {sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, }, }, { @@ -357,12 +357,12 @@ var SpatialQueryTests = []QueryTest{ { Query: `SELECT ST_SWAPXY(g) from geometry_table order by i`, Expected: []sql.Row{ - {sql.Geometry{Inner: sql.Point{X: 2, Y: 1}}}, - {sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 2, Y: 1}, {X: 4, Y: 3}}}}}, - {sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}}, - {sql.Geometry{Inner: sql.Point{SRID: 4326, X: 2, Y: 1}}}, - {sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}}}}}, - {sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 1, Y: 0}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}}, + {sql.Point{X: 2, Y: 1}}, + {sql.LineString{Points: []sql.Point{{X: 2, Y: 1}, {X: 4, Y: 3}}}}, + {sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, + {sql.Point{SRID: 4326, X: 2, Y: 1}}, + {sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 2, Y: 1}, {SRID: 4326, X: 4, Y: 3}}}}, + {sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 1, Y: 0}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, }, }, } @@ -7942,7 +7942,7 @@ var InfoSchemaScripts = []ScriptTest{ }, }, { - Name: "information_schema.columns", + Name: "information_schema.columns for view", SetUpScript: []string{ "USE foo", "drop table other_table", @@ -8029,6 +8029,23 @@ var InfoSchemaScripts = []ScriptTest{ }, }, }, + { + Name: "information_schema.columns with srs_id defined in spatial columns", + SetUpScript: []string{ + "CREATE TABLE stable (geo GEOMETRY NOT NULL DEFAULT (POINT(2, 5)), line LINESTRING NOT NULL, pnt POINT SRID 4326, pol POLYGON NOT NULL SRID 0);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_TYPE, COLUMN_KEY, SRS_ID FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'stable'", + Expected: []sql.Row{ + {"stable", "geo", "POINT(2, 5)", "NO", "geometry", "geometry", "", "NULL"}, + {"stable", "line", nil, "NO", "linestring", "linestring", "", "NULL"}, + {"stable", "pnt", nil, "YES", "point", "point", "", "4326"}, + {"stable", "pol", nil, "NO", "polygon", "polygon", "", "0"}, + }, + }, + }, + }, } var ExplodeQueries = []QueryTest{ diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index f0f69e4fe9..9751f23ca0 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -2155,6 +2155,216 @@ var SpatialScriptTests = []ScriptTest{ }, }, }, + { + Name: "create table using SRID value for geometry type", + SetUpScript: []string{ + "CREATE TABLE tab0 (i int primary key, g geometry srid 4326 default (point(1,1)));", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "show create table tab0", + Expected: []sql.Row{{"tab0", "CREATE TABLE `tab0` (\n `i` int NOT NULL,\n `g` geometry SRID 4326 DEFAULT (POINT(1, 1)),\n PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + }, + { + Query: "INSERT INTO tab0 VALUES (1, ST_GEOMFROMTEXT(ST_ASWKT(POINT(1,2)), 4326))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "select i, ST_ASWKT(g) FROM tab0", + Expected: []sql.Row{{1, "POINT(1 2)"}}, + }, + { + Query: "INSERT INTO tab0 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(POINT(2,4))))", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "INSERT INTO tab0 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(LINESTRING(POINT(1, 6),POINT(4, 3))), 4326))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "select i, ST_ASWKT(g) FROM tab0", + Expected: []sql.Row{{1, "POINT(1 2)"}, {2, "LINESTRING(1 6,4 3)"}}, + }, + }, + }, + { + Name: "create table using SRID value for linestring type", + SetUpScript: []string{ + "CREATE TABLE tab1 (i int primary key, l linestring srid 0);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "show create table tab1", + Expected: []sql.Row{{"tab1", "CREATE TABLE `tab1` (\n `i` int NOT NULL,\n `l` linestring SRID 0,\n PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + }, + { + Query: "INSERT INTO tab1 VALUES (1, LINESTRING(POINT(0, 0),POINT(2, 2)))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "select i, ST_ASWKT(l) FROM tab1", + Expected: []sql.Row{{1, "LINESTRING(0 0,2 2)"}}, + }, + { + Query: "INSERT INTO tab1 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(LINESTRING(POINT(1, 6),POINT(4, 3))), 4326))", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "select i, ST_ASWKT(l) FROM tab1", + Expected: []sql.Row{{1, "LINESTRING(0 0,2 2)"}}, + }, + }, + }, + { + Name: "create table using SRID value for point type", + SetUpScript: []string{ + "CREATE TABLE tab2 (i int primary key);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "ALTER TABLE tab2 ADD COLUMN p POINT NOT NULL SRID 0", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + Query: "show create table tab2", + Expected: []sql.Row{{"tab2", "CREATE TABLE `tab2` (\n `i` int NOT NULL,\n `p` point NOT NULL SRID 0,\n PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + }, + { + Query: "INSERT INTO tab2 VALUES (1, POINT(2, 2))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "select i, ST_ASWKT(p) FROM tab2", + Expected: []sql.Row{{1, "POINT(2 2)"}}, + }, + { + Query: "INSERT INTO tab2 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(POINT(1, 6)), 4326))", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "select i, ST_ASWKT(p) FROM tab2", + Expected: []sql.Row{{1, "POINT(2 2)"}}, + }, + { + Query: "ALTER TABLE tab2 CHANGE COLUMN p p POINT NOT NULL", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + Query: "INSERT INTO tab2 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(POINT(1, 6)), 4326))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "select i, ST_ASWKT(p) FROM tab2", + Expected: []sql.Row{{1, "POINT(2 2)"}, {2, "POINT(1 6)"}}, + }, + { + Query: "ALTER TABLE tab2 CHANGE COLUMN p p POINT NOT NULL SRID 4326", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "delete from tab2 where i = 1", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "ALTER TABLE tab2 CHANGE COLUMN p p POINT NOT NULL SRID 4326", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + Query: "show create table tab2", + Expected: []sql.Row{{"tab2", "CREATE TABLE `tab2` (\n `i` int NOT NULL,\n `p` point NOT NULL SRID 4326,\n PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + }, + }, + }, + { + Name: "create table using SRID value for polygon type", + SetUpScript: []string{ + "CREATE TABLE tab3 (i int primary key, y polygon NOT NULL);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "show create table tab3", + Expected: []sql.Row{{"tab3", "CREATE TABLE `tab3` (\n `i` int NOT NULL,\n `y` polygon NOT NULL,\n PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + }, + { + Query: "INSERT INTO tab3 VALUES (1, polygon(linestring(point(0,0),point(8,0),point(12,9),point(0,9),point(0,0))))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "select i, ST_ASWKT(y) FROM tab3", + Expected: []sql.Row{{1, "POLYGON((0 0,8 0,12 9,0 9,0 0))"}}, + }, + { + Query: "ALTER TABLE tab3 MODIFY COLUMN y POLYGON NOT NULL SRID 0", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + Query: "ALTER TABLE tab3 MODIFY COLUMN y POLYGON NOT NULL SRID 4326", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "select i, ST_ASWKT(y) FROM tab3", + Expected: []sql.Row{{1, "POLYGON((0 0,8 0,12 9,0 9,0 0))"}}, + }, + { + Query: "ALTER TABLE tab3 MODIFY COLUMN y GEOMETRY NULL SRID 0", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + Query: "select i, ST_ASWKT(y) FROM tab3", + Expected: []sql.Row{{1, "POLYGON((0 0,8 0,12 9,0 9,0 0))"}}, + }, + }, + }, + { + Name: "invalid cases of SRID value", + SetUpScript: []string{ + "CREATE TABLE table1 (i int primary key, p point srid 4326);", + "INSERT INTO table1 VALUES (1, ST_SRID(POINT(1, 5), 4326))", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "CREATE TABLE table2 (i int primary key, p point srid 1);", + // error should be ErrInvalidSRID once we support all mysql valid srid values + ExpectedErr: sql.ErrUnsupportedFeature, + }, + { + Query: "SELECT i, ST_ASWKT(p) FROM table1;", + Expected: []sql.Row{{1, "POINT(5 1)"}}, + }, + { + Query: "INSERT INTO table1 VALUES (2, POINT(2, 5))", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "SELECT i, ST_ASWKT(p) FROM table1;", + Expected: []sql.Row{{1, "POINT(5 1)"}}, + }, + { + Query: "ALTER TABLE table1 CHANGE COLUMN p p linestring srid 4326", + ExpectedErr: sql.ErrSpatialTypeConversion, + }, + { + Query: "ALTER TABLE table1 CHANGE COLUMN p p geometry srid 0", + ExpectedErr: sql.ErrNotMatchingSRIDWithColName, + }, + { + Query: "ALTER TABLE table1 CHANGE COLUMN p p geometry srid 4326", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + Query: "show create table table1", + Expected: []sql.Row{{"table1", "CREATE TABLE `table1` (\n `i` int NOT NULL,\n `p` geometry SRID 4326,\n PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + }, + { + Query: "INSERT INTO table1 VALUES (2, ST_SRID(LINESTRING(POINT(0, 0),POINT(2, 2)),4326))", + Expected: []sql.Row{{sql.NewOkResult(1)}}, + }, + { + Query: "ALTER TABLE table1 CHANGE COLUMN p p point srid 4326", + ExpectedErr: sql.ErrSpatialTypeConversion, + }, + }, + }, } var CreateCheckConstraintsScripts = []ScriptTest{ diff --git a/enginetest/queries/update_queries.go b/enginetest/queries/update_queries.go index 334c43eb23..c5c3c6897c 100644 --- a/enginetest/queries/update_queries.go +++ b/enginetest/queries/update_queries.go @@ -424,13 +424,13 @@ var SpatialUpdateTests = []WriteQueryTest{ WriteQuery: "UPDATE line_table SET l = linestring(point(1.2,3.4),point(5.6,7.8));", ExpectedWriteResult: []sql.Row{{newUpdateResult(2, 2)}}, SelectQuery: "SELECT * FROM line_table;", - ExpectedSelect: []sql.Row{{int64(0), sql.Linestring{Points: []sql.Point{{X: 1.2, Y: 3.4}, {X: 5.6, Y: 7.8}}}}, {int64(1), sql.Linestring{Points: []sql.Point{{X: 1.2, Y: 3.4}, {X: 5.6, Y: 7.8}}}}}, + ExpectedSelect: []sql.Row{{int64(0), sql.LineString{Points: []sql.Point{{X: 1.2, Y: 3.4}, {X: 5.6, Y: 7.8}}}}, {int64(1), sql.LineString{Points: []sql.Point{{X: 1.2, Y: 3.4}, {X: 5.6, Y: 7.8}}}}}, }, { WriteQuery: "UPDATE polygon_table SET p = polygon(linestring(point(1,1),point(1,-1),point(-1,-1),point(-1,1),point(1,1)));", ExpectedWriteResult: []sql.Row{{newUpdateResult(1, 1)}}, SelectQuery: "SELECT * FROM polygon_table;", - ExpectedSelect: []sql.Row{{int64(0), sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, + ExpectedSelect: []sql.Row{{int64(0), sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 1, Y: 1}, {X: 1, Y: -1}, {X: -1, Y: -1}, {X: -1, Y: 1}, {X: 1, Y: 1}}}}}}}, }, } diff --git a/enginetest/testdata.go b/enginetest/testdata.go index 5c36767c66..a7c92f12c1 100644 --- a/enginetest/testdata.go +++ b/enginetest/testdata.go @@ -112,12 +112,12 @@ func createSpatialSubsetTestData(t *testing.T, harness Harness, includedTables [ if err == nil { InsertRows(t, NewContext(harness), mustInsertableTable(t, table), - sql.NewRow(1, sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}), - sql.NewRow(2, sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}), - sql.NewRow(3, sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}), - sql.NewRow(4, sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}), - sql.NewRow(5, sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}), - sql.NewRow(6, sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}), + sql.NewRow(1, sql.Point{X: 1, Y: 2}), + sql.NewRow(2, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}), + sql.NewRow(3, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}), + sql.NewRow(4, sql.Point{SRID: 4326, X: 1, Y: 2}), + sql.NewRow(5, sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}), + sql.NewRow(6, sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}), ) } else { t.Logf("Warning: could not create table %s: %s", "geometry_table", err) @@ -146,13 +146,13 @@ func createSpatialSubsetTestData(t *testing.T, harness Harness, includedTables [ wrapInTransaction(t, myDb, harness, func() { table, err = harness.NewTable(myDb, "line_table", sql.NewPrimaryKeySchema(sql.Schema{ {Name: "i", Type: sql.Int64, Source: "line_table", PrimaryKey: true}, - {Name: "l", Type: sql.LinestringType{}, Source: "line_table"}, + {Name: "l", Type: sql.LineStringType{}, Source: "line_table"}, })) if err == nil { InsertRows(t, NewContext(harness), mustInsertableTable(t, table), - sql.NewRow(0, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}), - sql.NewRow(1, sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}), + sql.NewRow(0, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}), + sql.NewRow(1, sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}), ) } else { t.Logf("Warning: could not create table %s: %s", "line_table", err) @@ -169,7 +169,7 @@ func createSpatialSubsetTestData(t *testing.T, harness Harness, includedTables [ if err == nil { InsertRows(t, NewContext(harness), mustInsertableTable(t, table), - sql.NewRow(0, sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}), + sql.NewRow(0, sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}), ) } else { t.Logf("Warning: could not create table %s: %s", "polygon_table", err) diff --git a/go.sum b/go.sum index 50620c0f07..e6f87120dc 100755 --- a/go.sum +++ b/go.sum @@ -55,8 +55,6 @@ github.com/dolthub/jsonpath v0.0.0-20210609232853-d49537a30474 h1:xTrR+l5l+1Lfq0 github.com/dolthub/jsonpath v0.0.0-20210609232853-d49537a30474/go.mod h1:kMz7uXOXq4qRriCEyZ/LUeTqraLJCjf0WVZcUi6TxUY= github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE= github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY= -github.com/dolthub/vitess v0.0.0-20220517011201-8f50d80eae58 h1:v7uMbJKhb9zi2Nz3pxDOUVfWO30E5wbSckVq7AjgXRw= -github.com/dolthub/vitess v0.0.0-20220517011201-8f50d80eae58/go.mod h1:jxgvpEvrTNw2i4BKlwT75E775eUXBeMv5MPeQkIb9zI= github.com/dolthub/vitess v0.0.0-20220525003637-9c94a4060dd1 h1:lwzjI/92DnlmpgNqK+KV0oC31BQ/r6VE6RqDJAcb3GY= github.com/dolthub/vitess v0.0.0-20220525003637-9c94a4060dd1/go.mod h1:jxgvpEvrTNw2i4BKlwT75E775eUXBeMv5MPeQkIb9zI= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= diff --git a/memory/table.go b/memory/table.go index 8c69b57027..44d0373aeb 100644 --- a/memory/table.go +++ b/memory/table.go @@ -710,6 +710,9 @@ func (t *Table) ModifyColumn(ctx *sql.Context, columnName string, column *sql.Co oldRowWithoutVal = append(oldRowWithoutVal, row[oldIdx+1:]...) newVal, err := column.Type.Convert(row[oldIdx]) if err != nil { + if sql.ErrNotMatchingSRID.Is(err) { + err = sql.ErrNotMatchingSRIDWithColName.New(columnName, err) + } return err } var newRow sql.Row diff --git a/sql/analyzer/triggers.go b/sql/analyzer/triggers.go index 8249191987..0120491dd5 100644 --- a/sql/analyzer/triggers.go +++ b/sql/analyzer/triggers.go @@ -106,7 +106,7 @@ func validateCreateTrigger(ctx *sql.Context, a *Analyzer, node sql.Node, scope * } // Check to see if the columns with "new" and "old" table reference are valid columns from the trigger table. - transform.InspectExpressionsWithNode(ct.Body, func(n sql.Node, e sql.Expression) bool { + transform.InspectExpressions(ct.Body, func(e sql.Expression) bool { switch e := e.(type) { case *expression.UnresolvedColumn: if strings.ToLower(e.Table()) == "old" || strings.ToLower(e.Table()) == "new" { diff --git a/sql/errors.go b/sql/errors.go index 31046e2a9d..c772f86c83 100644 --- a/sql/errors.go +++ b/sql/errors.go @@ -564,6 +564,15 @@ var ( // ErrUnsupportedJoinFactorCount is returned for a query with more commutable join tables than we support ErrUnsupportedJoinFactorCount = errors.NewKind("unsupported join factor count: expected fewer than %d tables, found %d") + + // ErrNotMatchingSRID is returned for SRID values not matching + ErrNotMatchingSRID = errors.NewKind("The SRID of the geometry is %v, but the SRID of the column is %v. Consider changing the SRID of the geometry or the SRID property of the column.") + + // ErrNotMatchingSRIDWithColName is returned for error of SRID values not matching with column name detail + ErrNotMatchingSRIDWithColName = errors.NewKind("The SRID of the geometry does not match the SRID of the column '%s'. %v") + + // ErrSpatialTypeConversion is returned when one spatial type cannot be converted to the other spatial type + ErrSpatialTypeConversion = errors.NewKind("Cannot get geometry object from data you send to the GEOMETRY field") ) func CastSQLError(err error) (*mysql.SQLError, error, bool) { diff --git a/sql/expression/function/dimension.go b/sql/expression/function/dimension.go index 7ec7b7a6cc..f419d2ed5d 100644 --- a/sql/expression/function/dimension.go +++ b/sql/expression/function/dimension.go @@ -82,21 +82,10 @@ func (p *Dimension) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { switch val.(type) { case sql.Point: return 0, nil - case sql.Linestring: + case sql.LineString: return 1, nil case sql.Polygon: return 2, nil - case sql.Geometry: - switch val.(sql.Geometry).Inner.(type) { - case sql.Point: - return 0, nil - case sql.Linestring: - return 1, nil - case sql.Polygon: - return 2, nil - default: - return nil, sql.ErrInvalidGISData.New("ST_DIMENSION") - } default: return nil, sql.ErrInvalidGISData.New("ST_DIMENSION") } diff --git a/sql/expression/function/dimension_test.go b/sql/expression/function/dimension_test.go index d617abdb49..970934ba99 100644 --- a/sql/expression/function/dimension_test.go +++ b/sql/expression/function/dimension_test.go @@ -34,7 +34,7 @@ func TestDimension(t *testing.T) { t.Run("linestring is dimension 1", func(t *testing.T) { require := require.New(t) - f := NewDimension(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}, sql.LinestringType{})) + f := NewDimension(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}, sql.LineStringType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal(1, v) @@ -42,7 +42,7 @@ func TestDimension(t *testing.T) { t.Run("polygon dimension 2", func(t *testing.T) { require := require.New(t) - f := NewDimension(expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) + f := NewDimension(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal(2, v) @@ -50,7 +50,7 @@ func TestDimension(t *testing.T) { t.Run("geometry with inner point is dimension 0", func(t *testing.T) { require := require.New(t) - f := NewDimension(expression.NewLiteral(sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}, sql.GeometryType{})) + f := NewDimension(expression.NewLiteral(sql.Point{X: 1, Y: 2}, sql.GeometryType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal(0, v) @@ -58,7 +58,7 @@ func TestDimension(t *testing.T) { t.Run("geometry with inner linestring is dimension 1", func(t *testing.T) { require := require.New(t) - f := NewDimension(expression.NewLiteral(sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}}, sql.GeometryType{})) + f := NewDimension(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}, sql.GeometryType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal(1, v) @@ -66,7 +66,7 @@ func TestDimension(t *testing.T) { t.Run("geometry with inner polygon dimension 2", func(t *testing.T) { require := require.New(t) - f := NewDimension(expression.NewLiteral(sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}}, sql.GeometryType{})) + f := NewDimension(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, sql.GeometryType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal(2, v) diff --git a/sql/expression/function/geojson.go b/sql/expression/function/geojson.go index ef1ace3724..f9dec16959 100644 --- a/sql/expression/function/geojson.go +++ b/sql/expression/function/geojson.go @@ -59,7 +59,7 @@ func PointToSlice(p sql.Point) [2]float64 { return [2]float64{p.X, p.Y} } -func LineToSlice(l sql.Linestring) [][2]float64 { +func LineToSlice(l sql.LineString) [][2]float64 { arr := make([][2]float64, len(l.Points)) for i, p := range l.Points { arr[i] = PointToSlice(p) @@ -80,7 +80,7 @@ func FindBBox(v interface{}) [4]float64 { switch v := v.(type) { case sql.Point: res = [4]float64{v.X, v.Y, v.X, v.Y} - case sql.Linestring: + case sql.LineString: res = [4]float64{math.MaxFloat64, math.MaxFloat64, math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64} for _, p := range v.Points { tmp := FindBBox(p) @@ -125,11 +125,9 @@ func RoundFloatSlices(v interface{}, p float64) interface{} { // GetSRID returns the SRID given a Geometry type, will return -1 otherwise func GetSRID(val interface{}) int { switch v := val.(type) { - case sql.Geometry: - return GetSRID(v.Inner) case sql.Point: return int(v.SRID) - case sql.Linestring: + case sql.LineString: return int(v.SRID) case sql.Polygon: return int(v.SRID) @@ -154,24 +152,10 @@ func (g *AsGeoJSON) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Create map object to hold values obj := make(map[string]interface{}) switch v := val.(type) { - case sql.Geometry: - switch inner := v.Inner.(type) { - case sql.Point: - obj["type"] = "Point" - obj["coordinates"] = PointToSlice(inner) - case sql.Linestring: - obj["type"] = "LineString" - obj["coordinates"] = LineToSlice(inner) - case sql.Polygon: - obj["type"] = "Polygon" - obj["coordinates"] = PolyToSlice(inner) - default: - return nil, ErrInvalidArgumentType.New(g.FunctionName()) - } case sql.Point: obj["type"] = "Point" obj["coordinates"] = PointToSlice(v) - case sql.Linestring: + case sql.LineString: obj["type"] = "LineString" obj["coordinates"] = LineToSlice(v) case sql.Polygon: @@ -358,7 +342,7 @@ func SliceToPoint(coords interface{}) (interface{}, error) { if !ok { return nil, errors.New("coordinate must be of type number") } - return sql.Point{SRID: 4326, X: x, Y: y}, nil + return sql.Point{SRID: sql.GeoSpatialSRID, X: x, Y: y}, nil } func SliceToLine(coords interface{}) (interface{}, error) { @@ -378,7 +362,7 @@ func SliceToLine(coords interface{}) (interface{}, error) { } points[i] = p.(sql.Point) } - return sql.Linestring{SRID: 4326, Points: points}, nil + return sql.LineString{SRID: sql.GeoSpatialSRID, Points: points}, nil } func SliceToPoly(coords interface{}) (interface{}, error) { @@ -390,18 +374,18 @@ func SliceToPoly(coords interface{}) (interface{}, error) { if len(cs) == 0 { return nil, errors.New("not enough lines") } - lines := make([]sql.Linestring, len(cs)) + lines := make([]sql.LineString, len(cs)) for i, c := range cs { l, err := SliceToLine(c) if err != nil { return nil, err } - if !isLinearRing(l.(sql.Linestring)) { + if !isLinearRing(l.(sql.LineString)) { return nil, errors.New("invalid GeoJSON data provided") } - lines[i] = l.(sql.Linestring) + lines[i] = l.(sql.LineString) } - return sql.Polygon{SRID: 4326, Lines: lines}, nil + return sql.Polygon{SRID: sql.GeoSpatialSRID, Lines: lines}, nil } // Eval implements the sql.Expression interface. @@ -535,12 +519,11 @@ func (g *GeomFromGeoJSON) Eval(ctx *sql.Context, row sql.Row) (interface{}, erro default: return nil, errors.New("incorrect srid value") } - // Check for invalid SRID - if _srid != 0 && _srid != 4326 { - return nil, ErrInvalidSRID.New(g.FunctionName(), _srid) + if err = ValidateSRID(_srid); err != nil { + return nil, err } - // If SRID 4326, do nothing - if _srid == 4326 { + // If SRID is GeoSpatialSRID (4326), do nothing + if _srid == sql.GeoSpatialSRID { return res, nil } // Swap coordinates with SRID 0 @@ -551,7 +534,7 @@ func (g *GeomFromGeoJSON) Eval(ctx *sql.Context, row sql.Row) (interface{}, erro _res.X, _res.Y = _res.Y, _res.X return _res, nil case "LineString": - _res := res.(sql.Linestring) + _res := res.(sql.LineString) _res.SRID = _srid for i, p := range _res.Points { _res.Points[i].SRID = _srid diff --git a/sql/expression/function/geojson_test.go b/sql/expression/function/geojson_test.go index 42158f006a..f8a53dcf18 100644 --- a/sql/expression/function/geojson_test.go +++ b/sql/expression/function/geojson_test.go @@ -35,7 +35,7 @@ func TestAsGeoJSON(t *testing.T) { }) t.Run("convert linestring to geojson", func(t *testing.T) { require := require.New(t) - f, err := NewAsGeoJSON(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LinestringType{})) + f, err := NewAsGeoJSON(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LineStringType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) @@ -44,7 +44,7 @@ func TestAsGeoJSON(t *testing.T) { }) t.Run("convert polygon to geojson", func(t *testing.T) { require := require.New(t) - f, err := NewAsGeoJSON(expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) + f, err := NewAsGeoJSON(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) @@ -100,7 +100,7 @@ func TestAsGeoJSON(t *testing.T) { t.Run("convert linestring with bounding box", func(t *testing.T) { require := require.New(t) f, err := NewAsGeoJSON( - expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 100, Y: 2}, {X: 1, Y: 200}}}, sql.LinestringType{}), + expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 100, Y: 2}, {X: 1, Y: 200}}}, sql.LineStringType{}), expression.NewLiteral(2, sql.Int64), expression.NewLiteral(1, sql.Int64), ) @@ -113,7 +113,7 @@ func TestAsGeoJSON(t *testing.T) { t.Run("convert polygon with bounding box", func(t *testing.T) { require := require.New(t) f, err := NewAsGeoJSON( - expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{}), + expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{}), expression.NewLiteral(2, sql.Int64), expression.NewLiteral(1, sql.Int64), ) @@ -244,7 +244,7 @@ func TestGeomFromGeoJSON(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{SRID: 4326, Points: []sql.Point{{4326, 2, 1}, {4326, 4, 3}}}, v) + require.Equal(sql.LineString{SRID: 4326, Points: []sql.Point{{4326, 2, 1}, {4326, 4, 3}}}, v) }) t.Run("convert polygon to geojson", func(t *testing.T) { require := require.New(t) @@ -253,7 +253,7 @@ func TestGeomFromGeoJSON(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{4326, []sql.Point{{4326, 0, 0}, {4326, 1, 1}, {4326, 1, 0}, {4326, 0, 0}}}}}, v) + require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.LineString{{4326, []sql.Point{{4326, 0, 0}, {4326, 1, 1}, {4326, 1, 0}, {4326, 0, 0}}}}}, v) }) t.Run("reject dimensions greater than 2 with flag 1", func(t *testing.T) { require := require.New(t) @@ -275,7 +275,7 @@ func TestGeomFromGeoJSON(t *testing.T) { require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) - require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{4326, []sql.Point{{4326, 0, 0}, {4326, 1, 1}, {4326, 1, 0}, {4326, 0, 0}}}}}, v) + require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.LineString{{4326, []sql.Point{{4326, 0, 0}, {4326, 1, 1}, {4326, 1, 0}, {4326, 0, 0}}}}}, v) }) t.Run("srid 0 swaps x and y", func(t *testing.T) { require := require.New(t) @@ -299,7 +299,7 @@ func TestGeomFromGeoJSON(t *testing.T) { require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) - require.Equal(sql.Linestring{SRID: 0, Points: []sql.Point{{0, 1, 2}, {0, 3, 4}}}, v) + require.Equal(sql.LineString{SRID: 0, Points: []sql.Point{{0, 1, 2}, {0, 3, 4}}}, v) }) t.Run("srid 0 swaps x and y", func(t *testing.T) { require := require.New(t) @@ -311,7 +311,7 @@ func TestGeomFromGeoJSON(t *testing.T) { require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) - require.Equal(sql.Polygon{SRID: 0, Lines: []sql.Linestring{{0, []sql.Point{{0, 0, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}}}}, v) + require.Equal(sql.Polygon{SRID: 0, Lines: []sql.LineString{{0, []sql.Point{{0, 0, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}}}}, v) }) t.Run("check return type", func(t *testing.T) { require := require.New(t) diff --git a/sql/expression/function/linestring.go b/sql/expression/function/linestring.go index daa042e175..a2aaa13b47 100644 --- a/sql/expression/function/linestring.go +++ b/sql/expression/function/linestring.go @@ -23,37 +23,37 @@ import ( "github.com/dolthub/go-mysql-server/sql" ) -// Linestring is a function that returns a point type containing values Y and Y. -type Linestring struct { +// LineString is a function that returns a point type containing values Y and Y. +type LineString struct { expression.NaryExpression } -var _ sql.FunctionExpression = (*Linestring)(nil) +var _ sql.FunctionExpression = (*LineString)(nil) -// NewLinestring creates a new point expression. -func NewLinestring(args ...sql.Expression) (sql.Expression, error) { +// NewLineString creates a new point expression. +func NewLineString(args ...sql.Expression) (sql.Expression, error) { if len(args) < 2 { - return nil, sql.ErrInvalidArgumentNumber.New("Linestring", "2 or more", len(args)) + return nil, sql.ErrInvalidArgumentNumber.New("LineString", "2 or more", len(args)) } - return &Linestring{expression.NaryExpression{ChildExpressions: args}}, nil + return &LineString{expression.NaryExpression{ChildExpressions: args}}, nil } // FunctionName implements sql.FunctionExpression -func (l *Linestring) FunctionName() string { +func (l *LineString) FunctionName() string { return "linestring" } // Description implements sql.FunctionExpression -func (l *Linestring) Description() string { +func (l *LineString) Description() string { return "returns a new linestring." } // Type implements the sql.Expression interface. -func (l *Linestring) Type() sql.Type { - return sql.LinestringType{} +func (l *LineString) Type() sql.Type { + return sql.LineStringType{} } -func (l *Linestring) String() string { +func (l *LineString) String() string { var args = make([]string, len(l.ChildExpressions)) for i, arg := range l.ChildExpressions { args[i] = arg.String() @@ -62,12 +62,12 @@ func (l *Linestring) String() string { } // WithChildren implements the Expression interface. -func (l *Linestring) WithChildren(children ...sql.Expression) (sql.Expression, error) { - return NewLinestring(children...) +func (l *LineString) WithChildren(children ...sql.Expression) (sql.Expression, error) { + return NewLineString(children...) } // Eval implements the sql.Expression interface. -func (l *Linestring) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { +func (l *LineString) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Allocate array of points var points = make([]sql.Point, len(l.ChildExpressions)) @@ -82,12 +82,12 @@ func (l *Linestring) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { switch v := val.(type) { case sql.Point: points[i] = v - case sql.Linestring, sql.Polygon: // TODO: eventually add all spatial types + case sql.LineString, sql.Polygon: // TODO: eventually add all spatial types return nil, sql.ErrInvalidArgumentDetails.New(l.FunctionName(), v) default: return nil, sql.ErrIllegalGISValue.New(v) } } - return sql.Linestring{Points: points}, nil + return sql.LineString{Points: points}, nil } diff --git a/sql/expression/function/linestring_test.go b/sql/expression/function/linestring_test.go index 028365d231..b5e862fd93 100644 --- a/sql/expression/function/linestring_test.go +++ b/sql/expression/function/linestring_test.go @@ -23,10 +23,10 @@ import ( "github.com/dolthub/go-mysql-server/sql/expression" ) -func TestLinestring(t *testing.T) { +func TestLineString(t *testing.T) { t.Run("create valid linestring with points", func(t *testing.T) { require := require.New(t) - f, err := NewLinestring(expression.NewLiteral(sql.Point{X: 1, Y: 2}, sql.PointType{}), + f, err := NewLineString(expression.NewLiteral(sql.Point{X: 1, Y: 2}, sql.PointType{}), expression.NewLiteral(sql.Point{X: 3, Y: 4}, sql.PointType{}), expression.NewLiteral(sql.Point{X: 5, Y: 6}, sql.PointType{}), ) @@ -34,13 +34,13 @@ func TestLinestring(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}, {X: 5, Y: 6}}}, v) }) } -func TestNewLinestring(t *testing.T) { +func TestNewLineString(t *testing.T) { require := require.New(t) - _, err := NewLinestring(expression.NewLiteral(nil, sql.PointType{}), + _, err := NewLineString(expression.NewLiteral(nil, sql.PointType{}), expression.NewLiteral(nil, sql.PointType{}), expression.NewLiteral(nil, sql.PointType{}), ) diff --git a/sql/expression/function/polygon.go b/sql/expression/function/polygon.go index 827ac7de77..68ed48acf1 100644 --- a/sql/expression/function/polygon.go +++ b/sql/expression/function/polygon.go @@ -118,10 +118,10 @@ func lineSegmentsIntersect(a, b, c, d sql.Point) bool { } // TODO: should go in line? -func isLinearRing(line sql.Linestring) bool { +func isLinearRing(line sql.LineString) bool { // Get number of points numPoints := len(line.Points) - // Check length of Linestring (must be 0 or 4+) points + // Check length of LineString (must be 0 or 4+) points if numPoints != 0 && numPoints < 4 { return false } @@ -146,7 +146,7 @@ func isLinearRing(line sql.Linestring) bool { // Eval implements the sql.Expression interface. func (p *Polygon) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Allocate array of lines - var lines = make([]sql.Linestring, len(p.ChildExpressions)) + var lines = make([]sql.LineString, len(p.ChildExpressions)) // Go through each argument for i, arg := range p.ChildExpressions { @@ -157,7 +157,7 @@ func (p *Polygon) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { } // Must be of type linestring, throw error otherwise switch v := val.(type) { - case sql.Linestring: + case sql.LineString: // Check that line is a linear ring if isLinearRing(v) { lines[i] = v diff --git a/sql/expression/function/polygon_test.go b/sql/expression/function/polygon_test.go index fa95971f6d..15e2e0087b 100644 --- a/sql/expression/function/polygon_test.go +++ b/sql/expression/function/polygon_test.go @@ -26,29 +26,29 @@ import ( func TestPolygon(t *testing.T) { t.Run("create valid polygon", func(t *testing.T) { require := require.New(t) - f, err := NewPolygon(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, sql.LinestringType{})) + f, err := NewPolygon(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, sql.LineStringType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) }) t.Run("create valid polygon with multiple linestrings", func(t *testing.T) { require := require.New(t) - f, err := NewPolygon(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, sql.LinestringType{}), - expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}, sql.LinestringType{}), - expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}, sql.LinestringType{})) + f, err := NewPolygon(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, sql.LineStringType{}), + expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}, sql.LineStringType{}), + expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}, sql.LineStringType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, {Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}, {Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, {Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 0}}}, {Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, v) }) t.Run("create invalid using invalid linestring", func(t *testing.T) { require := require.New(t) - f, err := NewPolygon(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 0}}}, sql.LinestringType{})) + f, err := NewPolygon(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 0}}}, sql.LineStringType{})) require.NoError(err) _, err = f.Eval(sql.NewEmptyContext(), nil) @@ -57,7 +57,7 @@ func TestPolygon(t *testing.T) { t.Run("create invalid using non-linearring linestring", func(t *testing.T) { require := require.New(t) - f, err := NewPolygon(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 0}}}, sql.LinestringType{})) + f, err := NewPolygon(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 0}}}, sql.LineStringType{})) require.NoError(err) _, err = f.Eval(sql.NewEmptyContext(), nil) @@ -67,7 +67,7 @@ func TestPolygon(t *testing.T) { func TestNewPolygon(t *testing.T) { require := require.New(t) - _, err := NewLinestring(expression.NewLiteral(nil, sql.PointType{}), + _, err := NewLineString(expression.NewLiteral(nil, sql.PointType{}), expression.NewLiteral(nil, sql.PointType{}), expression.NewLiteral(nil, sql.PointType{}), ) diff --git a/sql/expression/function/registry.go b/sql/expression/function/registry.go index 602f1f5d54..74241030ac 100644 --- a/sql/expression/function/registry.go +++ b/sql/expression/function/registry.go @@ -136,7 +136,7 @@ var BuiltIns = []sql.Function{ sql.FunctionN{Name: "least", Fn: NewLeast}, sql.Function2{Name: "left", Fn: NewLeft}, sql.Function1{Name: "length", Fn: NewLength}, - sql.FunctionN{Name: "linestring", Fn: NewLinestring}, + sql.FunctionN{Name: "linestring", Fn: NewLineString}, sql.Function1{Name: "ln", Fn: NewLogBaseFunc(float64(math.E))}, sql.Function1{Name: "load_file", Fn: NewLoadFile}, sql.FunctionN{Name: "locate", Fn: NewLocate}, diff --git a/sql/expression/function/srid.go b/sql/expression/function/srid.go index a9c319fb0e..c73463f015 100644 --- a/sql/expression/function/srid.go +++ b/sql/expression/function/srid.go @@ -33,11 +33,6 @@ var _ sql.FunctionExpression = (*SRID)(nil) var ErrInvalidSRID = errors.NewKind("There's no spatial reference with SRID %d") -const ( - CartesianSRID = 0 - GeoSpatialSRID = 4326 -) - // NewSRID creates a new STX expression. func NewSRID(args ...sql.Expression) (sql.Expression, error) { if len(args) != 1 && len(args) != 2 { @@ -84,17 +79,17 @@ func PointWithSRID(p sql.Point, srid uint32) sql.Point { } // LineWithSRID creates a deep copy of linestring object with given SRID -func LineWithSRID(l sql.Linestring, srid uint32) sql.Linestring { +func LineWithSRID(l sql.LineString, srid uint32) sql.LineString { points := make([]sql.Point, len(l.Points)) for i, p := range l.Points { points[i] = PointWithSRID(p, srid) } - return sql.Linestring{SRID: srid, Points: points} + return sql.LineString{SRID: srid, Points: points} } // PolyWithSRID creates a deep copy of polygon object with given SRID func PolyWithSRID(p sql.Polygon, srid uint32) sql.Polygon { - lines := make([]sql.Linestring, len(p.Lines)) + lines := make([]sql.LineString, len(p.Lines)) for i, l := range p.Lines { lines[i] = LineWithSRID(l, srid) } @@ -120,21 +115,10 @@ func (s *SRID) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { switch g := g.(type) { case sql.Point: return g.SRID, nil - case sql.Linestring: + case sql.LineString: return g.SRID, nil case sql.Polygon: return g.SRID, nil - case sql.Geometry: - switch inner := g.Inner.(type) { - case sql.Point: - return inner.SRID, nil - case sql.Linestring: - return inner.SRID, nil - case sql.Polygon: - return inner.SRID, nil - default: - return nil, sql.ErrIllegalGISValue.New(g) - } default: return nil, sql.ErrIllegalGISValue.New(g) } @@ -160,30 +144,18 @@ func (s *SRID) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Type assertion _srid := srid.(uint32) - // Must be either 0 or 4326 - if _srid != CartesianSRID && _srid != GeoSpatialSRID { - return nil, ErrInvalidSRID.New(_srid) + if err = ValidateSRID(_srid); err != nil { + return nil, err } // Create new geometry object with matching SRID switch g := g.(type) { case sql.Point: return PointWithSRID(g, _srid), nil - case sql.Linestring: + case sql.LineString: return LineWithSRID(g, _srid), nil case sql.Polygon: return PolyWithSRID(g, _srid), nil - case sql.Geometry: - switch inner := g.Inner.(type) { - case sql.Point: - return sql.Geometry{Inner: PointWithSRID(inner, _srid)}, nil - case sql.Linestring: - return sql.Geometry{Inner: LineWithSRID(inner, _srid)}, nil - case sql.Polygon: - return sql.Geometry{Inner: PolyWithSRID(inner, _srid)}, nil - default: - return nil, sql.ErrIllegalGISValue.New(g) - } default: return nil, sql.ErrIllegalGISValue.New(g) } diff --git a/sql/expression/function/srid_test.go b/sql/expression/function/srid_test.go index faba6d9094..9c8f813f19 100644 --- a/sql/expression/function/srid_test.go +++ b/sql/expression/function/srid_test.go @@ -94,30 +94,30 @@ func TestSRID(t *testing.T) { t.Run("change SRID of linestring to 4326", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LinestringType{}), + f, err := NewSRID(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LineStringType{}), expression.NewLiteral(4326, sql.Int32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}, v) + require.Equal(sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}, v) }) t.Run("change SRID of polygon to 4326", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{}), + f, err := NewSRID(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.PolygonType{}), expression.NewLiteral(4326, sql.Int32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}, v) }) t.Run("select srid of geometry with inner point", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}, sql.GeometryType{})) + f, err := NewSRID(expression.NewLiteral(sql.Point{X: 1, Y: 2}, sql.GeometryType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) @@ -128,7 +128,7 @@ func TestSRID(t *testing.T) { t.Run("select srid of geometry with inner linestring", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, sql.GeometryType{})) + f, err := NewSRID(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.GeometryType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) @@ -139,7 +139,7 @@ func TestSRID(t *testing.T) { t.Run("select srid of geometry with inner polygon", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, sql.GeometryType{})) + f, err := NewSRID(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.GeometryType{})) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) @@ -150,37 +150,37 @@ func TestSRID(t *testing.T) { t.Run("change srid of geometry with inner point to 4326", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}, sql.GeometryType{}), + f, err := NewSRID(expression.NewLiteral(sql.Point{X: 1, Y: 2}, sql.GeometryType{}), expression.NewLiteral(4326, sql.Int32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Geometry{Inner: sql.Point{SRID: 4326, X: 1, Y: 2}}, v) + require.Equal(sql.Point{SRID: 4326, X: 1, Y: 2}, v) }) t.Run("change srid of geometry with inner linestring to 4326", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}}, sql.GeometryType{}), + f, err := NewSRID(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.GeometryType{}), expression.NewLiteral(4326, sql.Int32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Geometry{Inner: sql.Linestring{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}}, v) + require.Equal(sql.LineString{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 1, Y: 2}, {SRID: 4326, X: 3, Y: 4}}}, v) }) t.Run("change srid of geometry with inner polygon to 4326", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}}, sql.GeometryType{}), + f, err := NewSRID(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 1}, {X: 0, Y: 0}}}}}, sql.GeometryType{}), expression.NewLiteral(4326, sql.Int32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Geometry{Inner: sql.Polygon{SRID: 4326, Lines: []sql.Linestring{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}}, v) + require.Equal(sql.Polygon{SRID: 4326, Lines: []sql.LineString{{SRID: 4326, Points: []sql.Point{{SRID: 4326, X: 0, Y: 0}, {SRID: 4326, X: 0, Y: 1}, {SRID: 4326, X: 1, Y: 1}, {SRID: 4326, X: 0, Y: 0}}}}}, v) }) t.Run("return type with one argument", func(t *testing.T) { @@ -198,7 +198,7 @@ func TestSRID(t *testing.T) { t.Run("return type with two arguments", func(t *testing.T) { require := require.New(t) - f, err := NewSRID(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LinestringType{}), + f, err := NewSRID(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LineStringType{}), expression.NewLiteral(4326, sql.Int32)) require.NoError(err) diff --git a/sql/expression/function/swapxy.go b/sql/expression/function/swapxy.go index 7111b36da2..f653a435d2 100644 --- a/sql/expression/function/swapxy.go +++ b/sql/expression/function/swapxy.go @@ -70,20 +70,18 @@ func SwapGeometryXY(v interface{}) interface{} { switch v := v.(type) { case sql.Point: return sql.Point{SRID: v.SRID, X: v.Y, Y: v.X} - case sql.Linestring: + case sql.LineString: points := make([]sql.Point, len(v.Points)) for i, p := range v.Points { points[i] = SwapGeometryXY(p).(sql.Point) } - return sql.Linestring{SRID: v.SRID, Points: points} + return sql.LineString{SRID: v.SRID, Points: points} case sql.Polygon: - lines := make([]sql.Linestring, len(v.Lines)) + lines := make([]sql.LineString, len(v.Lines)) for i, l := range v.Lines { - lines[i] = SwapGeometryXY(l).(sql.Linestring) + lines[i] = SwapGeometryXY(l).(sql.LineString) } return sql.Polygon{SRID: v.SRID, Lines: lines} - case sql.Geometry: - return sql.Geometry{Inner: SwapGeometryXY(v.Inner)} default: return nil } @@ -104,7 +102,7 @@ func (s *SwapXY) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Expect one of the geometry types switch val.(type) { - case sql.Point, sql.Linestring, sql.Polygon, sql.Geometry: + case sql.Point, sql.LineString, sql.Polygon: return SwapGeometryXY(val), nil default: return nil, sql.ErrInvalidGISData.New("ST_DIMENSION") diff --git a/sql/expression/function/swapxy_test.go b/sql/expression/function/swapxy_test.go index 71f3d52380..b2eb783484 100644 --- a/sql/expression/function/swapxy_test.go +++ b/sql/expression/function/swapxy_test.go @@ -34,18 +34,18 @@ func TestSwapXY(t *testing.T) { t.Run("linestring swap", func(t *testing.T) { require := require.New(t) - f := NewSwapXY(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}, sql.LinestringType{})) + f := NewSwapXY(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}, sql.LineStringType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{Points: []sql.Point{{X: 1, Y: 0}, {X: 3, Y: 2}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 1, Y: 0}, {X: 3, Y: 2}}}, v) }) t.Run("polygon swap", func(t *testing.T) { require := require.New(t) - f := NewSwapXY(expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, sql.PointType{})) + f := NewSwapXY(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, sql.PointType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) }) t.Run("swap wrong type", func(t *testing.T) { @@ -65,26 +65,26 @@ func TestSwapXY(t *testing.T) { t.Run("geometry point swap", func(t *testing.T) { require := require.New(t) - f := NewSwapXY(expression.NewLiteral(sql.Geometry{Inner: sql.Point{X: 1, Y: 2}}, sql.GeometryType{})) + f := NewSwapXY(expression.NewLiteral(sql.Point{X: 1, Y: 2}, sql.GeometryType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Geometry{Inner: sql.Point{X: 2, Y: 1}}, v) + require.Equal(sql.Point{X: 2, Y: 1}, v) }) t.Run("geometry linestring swap", func(t *testing.T) { require := require.New(t) - f := NewSwapXY(expression.NewLiteral(sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}}, sql.GeometryType{})) + f := NewSwapXY(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 0, Y: 1}, {X: 2, Y: 3}}}, sql.GeometryType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Geometry{Inner: sql.Linestring{Points: []sql.Point{{X: 1, Y: 0}, {X: 3, Y: 2}}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 1, Y: 0}, {X: 3, Y: 2}}}, v) }) t.Run("geometry polygon swap", func(t *testing.T) { require := require.New(t) - f := NewSwapXY(expression.NewLiteral(sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}}, sql.GeometryType{})) + f := NewSwapXY(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, sql.GeometryType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Geometry{Inner: sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) }) t.Run("check return type", func(t *testing.T) { diff --git a/sql/expression/function/wkb.go b/sql/expression/function/wkb.go index ff7dc50e60..e77211ad3b 100644 --- a/sql/expression/function/wkb.go +++ b/sql/expression/function/wkb.go @@ -84,7 +84,7 @@ func PointToBytes(p sql.Point) []byte { } // serializeLine fills in buf with values from linestring -func serializeLine(l sql.Linestring, buf []byte) { +func serializeLine(l sql.LineString, buf []byte) { // Write number of points binary.LittleEndian.PutUint32(buf[0:4], uint32(len(l.Points))) // Append each point @@ -94,8 +94,8 @@ func serializeLine(l sql.Linestring, buf []byte) { } } -// LineToBytes converts a sql.Linestring to a byte array -func LineToBytes(l sql.Linestring) []byte { +// LineToBytes converts a sql.LineString to a byte array +func LineToBytes(l sql.LineString) []byte { // Initialize line buffer buf := make([]byte, 4+16*len(l.Points)) serializeLine(l, buf) @@ -145,26 +145,11 @@ func (a *AsWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Expect one of the geometry types switch v := val.(type) { - case sql.Geometry: - switch inner := v.Inner.(type) { - case sql.Point: - // Mark as point type - binary.LittleEndian.PutUint32(buf[1:5], 1) - data = PointToBytes(inner) - case sql.Linestring: - // Mark as linestring type - binary.LittleEndian.PutUint32(buf[1:5], 2) - data = LineToBytes(inner) - case sql.Polygon: - // Mark as Polygon type - binary.LittleEndian.PutUint32(buf[1:5], 3) - data = PolyToBytes(inner) - } case sql.Point: // Mark as point type binary.LittleEndian.PutUint32(buf[1:5], 1) data = PointToBytes(v) - case sql.Linestring: + case sql.LineString: // Mark as linestring type binary.LittleEndian.PutUint32(buf[1:5], 2) data = LineToBytes(v) @@ -283,10 +268,10 @@ func WKBToPoint(buf []byte, isBig bool, srid uint32, order bool) (sql.Point, err } // WKBToLine parses the data portion of a byte array in WKB format to a point object -func WKBToLine(buf []byte, isBig bool, srid uint32, order bool) (sql.Linestring, error) { +func WKBToLine(buf []byte, isBig bool, srid uint32, order bool) (sql.LineString, error) { // Must be at least 4 bytes (length of linestring) if len(buf) < 4 { - return sql.Linestring{}, sql.ErrInvalidGISData.New("ST_LineFromWKB") + return sql.LineString{}, sql.ErrInvalidGISData.New("ST_LineFromWKB") } // Read length of line string @@ -302,7 +287,7 @@ func WKBToLine(buf []byte, isBig bool, srid uint32, order bool) (sql.Linestring, // Check length if uint32(len(lineData)) < 16*numPoints { - return sql.Linestring{}, sql.ErrInvalidGISData.New("ST_LineFromWKB") + return sql.LineString{}, sql.ErrInvalidGISData.New("ST_LineFromWKB") } // Parse points @@ -311,11 +296,11 @@ func WKBToLine(buf []byte, isBig bool, srid uint32, order bool) (sql.Linestring, if point, err := WKBToPoint(lineData[16*i:16*(i+1)], isBig, srid, order); err == nil { points[i] = point } else { - return sql.Linestring{}, sql.ErrInvalidGISData.New("ST_LineFromWKB") + return sql.LineString{}, sql.ErrInvalidGISData.New("ST_LineFromWKB") } } - return sql.Linestring{SRID: srid, Points: points}, nil + return sql.LineString{SRID: srid, Points: points}, nil } // WKBToPoly parses the data portion of a byte array in WKB format to a point object @@ -338,7 +323,7 @@ func WKBToPoly(buf []byte, isBig bool, srid uint32, order bool) (sql.Polygon, er // Parse lines s := 0 - lines := make([]sql.Linestring, numLines) + lines := make([]sql.LineString, numLines) for i := uint32(0); i < numLines; i++ { if line, err := WKBToLine(polyData[s:], isBig, srid, order); err == nil { if isLinearRing(line) { @@ -412,9 +397,8 @@ func (g *GeomFromWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Convert this block to helper function @@ -521,7 +505,7 @@ func (p *PointFromWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) // TODO: convert to this block to helper function // Determine SRID - srid := uint32(0) + srid := sql.CartesianSRID if len(p.ChildExpressions) >= 2 { s, err := p.ChildExpressions[1].Eval(ctx, row) if err != nil { @@ -537,9 +521,8 @@ func (p *PointFromWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xy order @@ -589,7 +572,7 @@ func (l *LineFromWKB) Description() string { // Type implements the sql.Expression interface. func (l *LineFromWKB) Type() sql.Type { - return sql.LinestringType{} + return sql.LineStringType{} } func (l *LineFromWKB) String() string { @@ -652,9 +635,8 @@ func (l *LineFromWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xy order @@ -767,9 +749,8 @@ func (p *PolyFromWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xy order @@ -791,3 +772,10 @@ func (p *PolyFromWKB) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Read data return WKBToPoly(v[WKBHeaderLength:], isBig, srid, order) } + +func ValidateSRID(srid uint32) error { + if srid != sql.CartesianSRID && srid != sql.GeoSpatialSRID { + return ErrInvalidSRID.New(srid) + } + return nil +} diff --git a/sql/expression/function/wkb_test.go b/sql/expression/function/wkb_test.go index 6da8584dcb..3fc6939077 100644 --- a/sql/expression/function/wkb_test.go +++ b/sql/expression/function/wkb_test.go @@ -47,7 +47,7 @@ func TestAsWKB(t *testing.T) { t.Run("convert linestring", func(t *testing.T) { require := require.New(t) - f := NewAsWKB(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LinestringType{})) + f := NewAsWKB(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LineStringType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) res, err := hex.DecodeString("010200000002000000000000000000F03F000000000000004000000000000008400000000000001040") @@ -57,7 +57,7 @@ func TestAsWKB(t *testing.T) { t.Run("convert polygon", func(t *testing.T) { require := require.New(t) - f := NewAsWKB(expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) + f := NewAsWKB(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) res, err := hex.DecodeString("0103000000010000000400000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000") @@ -150,7 +150,7 @@ func TestGeomFromWKB(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, v) }) t.Run("convert polygon in little endian", func(t *testing.T) { @@ -162,7 +162,7 @@ func TestGeomFromWKB(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) }) t.Run("convert point with srid 0", func(t *testing.T) { @@ -175,23 +175,23 @@ func TestGeomFromWKB(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 0, X: 1, Y: 2}, v) + require.Equal(sql.Point{SRID: sql.CartesianSRID, X: 1, Y: 2}, v) }) - t.Run("convert point with srid 4230", func(t *testing.T) { + t.Run("convert point with srid valid 4326", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0101000000000000000000F03F0000000000000040") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32)) + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 4230, X: 1, Y: 2}, v) + require.Equal(sql.Point{SRID: sql.GeoSpatialSRID, X: 1, Y: 2}, v) }) - t.Run("convert point with srid 1234", func(t *testing.T) { + t.Run("convert point with invalid srid 1234", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0101000000000000000000F03F0000000000000040") require.NoError(err) @@ -203,100 +203,124 @@ func TestGeomFromWKB(t *testing.T) { require.Error(err) }) - t.Run("convert point with srid 4230 axis srid-defined", func(t *testing.T) { + t.Run("convert point with srid 4326 axis srid-defined", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0101000000000000000000F03F0000000000000040") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=srid-defined", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 4230, X: 1, Y: 2}, v) + require.Equal(sql.Point{SRID: sql.GeoSpatialSRID, X: 1, Y: 2}, v) }) - t.Run("convert point with srid 4230 axis long-lat", func(t *testing.T) { + t.Run("convert point with srid 4326 axis long-lat", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0101000000000000000000F03F0000000000000040") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 4230, X: 2, Y: 1}, v) + require.Equal(sql.Point{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, v) }) - t.Run("convert point with srid 4230 axis long-lat", func(t *testing.T) { + t.Run("convert point with srid 4326 axis long-lat", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0101000000000000000000F03F0000000000000040") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 4230, X: 2, Y: 1}, v) + require.Equal(sql.Point{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, v) }) - t.Run("convert linestring with srid 4230", func(t *testing.T) { + t.Run("convert linestring with valid srid 4326", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("010200000002000000000000000000F03F000000000000004000000000000008400000000000001040") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32)) + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 1, Y: 2}, {SRID: 4230, X: 3, Y: 4}}}, v) + require.Equal(sql.LineString{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 1, Y: 2}, {SRID: sql.GeoSpatialSRID, X: 3, Y: 4}}}, v) }) - t.Run("convert linestring with srid 4230 axis long-lat", func(t *testing.T) { + t.Run("convert linestring with invalid srid 2222", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("010200000002000000000000000000F03F000000000000004000000000000008400000000000001040") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(2222, sql.Uint32)) + require.NoError(err) + + _, err = f.Eval(sql.NewEmptyContext(), nil) + require.Error(err) + }) + + t.Run("convert linestring with srid 4326 axis long-lat", func(t *testing.T) { + require := require.New(t) + res, err := hex.DecodeString("010200000002000000000000000000F03F000000000000004000000000000008400000000000001040") + require.NoError(err) + f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 2, Y: 1}, {SRID: 4230, X: 4, Y: 3}}}, v) + require.Equal(sql.LineString{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 4, Y: 3}}}, v) }) - t.Run("convert polygon with srid 4230", func(t *testing.T) { + t.Run("convert polygon with valid srid 4326", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0103000000010000000400000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32)) + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{SRID: 4230, Lines: []sql.Linestring{{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 0, Y: 0}, {SRID: 4230, X: 1, Y: 1}, {SRID: 4230, X: 1, Y: 0}, {SRID: 4230, X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{SRID: sql.GeoSpatialSRID, Lines: []sql.LineString{{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 0, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 1, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 1, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 0}}}}}, v) + }) + + t.Run("convert polygon with invalid srid 2", func(t *testing.T) { + require := require.New(t) + res, err := hex.DecodeString("0103000000010000000400000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000") + require.NoError(err) + f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), + expression.NewLiteral(2, sql.Uint32)) + require.NoError(err) + + _, err = f.Eval(sql.NewEmptyContext(), nil) + require.Error(err) }) - t.Run("convert polygon with srid 4230 axis long-lat", func(t *testing.T) { + t.Run("convert polygon with srid 4326 axis long-lat", func(t *testing.T) { require := require.New(t) res, err := hex.DecodeString("0103000000010000000400000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000") require.NoError(err) f, err := NewGeomFromWKB(expression.NewLiteral(res, sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{SRID: 4230, Lines: []sql.Linestring{{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 0, Y: 0}, {SRID: 4230, X: 1, Y: 1}, {SRID: 4230, X: 0, Y: 1}, {SRID: 4230, X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{SRID: sql.GeoSpatialSRID, Lines: []sql.LineString{{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 0, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 1, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 0}}}}}, v) }) t.Run("convert null", func(t *testing.T) { diff --git a/sql/expression/function/wkt.go b/sql/expression/function/wkt.go index 69639cf253..29af0a44a6 100644 --- a/sql/expression/function/wkt.go +++ b/sql/expression/function/wkt.go @@ -68,26 +68,29 @@ func (p *AsWKT) WithChildren(children ...sql.Expression) (sql.Expression, error) } // PointToWKT converts a sql.Point to a string -func PointToWKT(p sql.Point) string { +func PointToWKT(p sql.Point, order bool) string { x := strconv.FormatFloat(p.X, 'g', -1, 64) y := strconv.FormatFloat(p.Y, 'g', -1, 64) + if order { + x, y = y, x + } return fmt.Sprintf("%s %s", x, y) } -// LineToWKT converts a sql.Linestring to a string -func LineToWKT(l sql.Linestring) string { +// LineToWKT converts a sql.LineString to a string +func LineToWKT(l sql.LineString, order bool) string { points := make([]string, len(l.Points)) for i, p := range l.Points { - points[i] = PointToWKT(p) + points[i] = PointToWKT(p, order) } return strings.Join(points, ",") } // PolygonToWKT converts a sql.Polygon to a string -func PolygonToWKT(p sql.Polygon) string { +func PolygonToWKT(p sql.Polygon, order bool) string { lines := make([]string, len(p.Lines)) for i, l := range p.Lines { - lines[i] = "(" + LineToWKT(l) + ")" + lines[i] = "(" + LineToWKT(l, order) + ")" } return strings.Join(lines, ",") } @@ -108,33 +111,18 @@ func (p *AsWKT) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { var data string // Expect one of the geometry types switch v := val.(type) { - case sql.Geometry: - switch inner := v.Inner.(type) { - case sql.Point: - // Mark as point type - geomType = "POINT" - data = PointToWKT(inner) - case sql.Linestring: - // Mark as linestring type - geomType = "LINESTRING" - data = LineToWKT(inner) - case sql.Polygon: - // Mark as Polygon type - geomType = "POLYGON" - data = PolygonToWKT(inner) - } case sql.Point: // Mark as point type geomType = "POINT" - data = PointToWKT(v) - case sql.Linestring: + data = PointToWKT(v, v.SRID == sql.GeoSpatialSRID) + case sql.LineString: // Mark as linestring type geomType = "LINESTRING" - data = LineToWKT(v) + data = LineToWKT(v, v.SRID == sql.GeoSpatialSRID) case sql.Polygon: // Mark as Polygon type geomType = "POLYGON" - data = PolygonToWKT(v) + data = PolygonToWKT(v, v.SRID == sql.GeoSpatialSRID) default: return nil, sql.ErrInvalidGISData.New("ST_AsWKT") } @@ -253,10 +241,10 @@ func WKTToPoint(s string, srid uint32, order bool) (sql.Point, error) { } // WKTToLine expects a string like "1.2 3.4, 5.6 7.8, ..." -func WKTToLine(s string, srid uint32, order bool) (sql.Linestring, error) { +func WKTToLine(s string, srid uint32, order bool) (sql.LineString, error) { // Empty string is wrong if len(s) == 0 { - return sql.Linestring{}, sql.ErrInvalidGISData.New("ST_LineFromText") + return sql.LineString{}, sql.ErrInvalidGISData.New("ST_LineFromText") } // Separate by comma @@ -272,17 +260,17 @@ func WKTToLine(s string, srid uint32, order bool) (sql.Linestring, error) { if p, err := WKTToPoint(ps, srid, order); err == nil { points[i] = p } else { - return sql.Linestring{}, sql.ErrInvalidGISData.New("ST_LineFromText") + return sql.LineString{}, sql.ErrInvalidGISData.New("ST_LineFromText") } } - // Create Linestring object - return sql.Linestring{SRID: srid, Points: points}, nil + // Create LineString object + return sql.LineString{SRID: srid, Points: points}, nil } // WKTToPoly Expects a string like "(1 2, 3 4), (5 6, 7 8), ..." func WKTToPoly(s string, srid uint32, order bool) (sql.Polygon, error) { - var lines []sql.Linestring + var lines []sql.LineString for { // Look for closing parentheses end := strings.Index(s, ")") @@ -325,7 +313,7 @@ func WKTToPoly(s string, srid uint32, order bool) (sql.Polygon, error) { break } - // Linestrings must be comma-separated + // LineStrings must be comma-separated if s[0] != ',' { return sql.Polygon{}, sql.ErrInvalidGISData.New("ST_PolyFromText") } @@ -382,13 +370,12 @@ func (g *GeomFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xy order - order := false + order := srid == sql.GeoSpatialSRID if len(g.ChildExpressions) == 3 { o, err := g.ChildExpressions[2].Eval(ctx, row) if err != nil { @@ -506,9 +493,8 @@ func (p *PointFromWKT) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xy order @@ -557,7 +543,7 @@ func (l *LineFromWKT) Description() string { // Type implements the sql.Expression interface. func (l *LineFromWKT) Type() sql.Type { - return sql.LinestringType{} + return sql.LineStringType{} } func (l *LineFromWKT) String() string { @@ -619,9 +605,8 @@ func (l *LineFromWKT) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xt order @@ -732,9 +717,8 @@ func (p *PolyFromWKT) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { srid = s.(uint32) } - // Must be valid SRID - if srid != 0 && srid != 4230 { - return nil, ErrInvalidSRID.New(srid) + if err = ValidateSRID(srid); err != nil { + return nil, err } // Determine xy order diff --git a/sql/expression/function/wkt_test.go b/sql/expression/function/wkt_test.go index 2100a31320..2b9a518bc7 100644 --- a/sql/expression/function/wkt_test.go +++ b/sql/expression/function/wkt_test.go @@ -42,7 +42,7 @@ func TestAsWKT(t *testing.T) { t.Run("convert linestring", func(t *testing.T) { require := require.New(t) - f := NewAsWKT(expression.NewLiteral(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LinestringType{})) + f := NewAsWKT(expression.NewLiteral(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, sql.LineStringType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal("LINESTRING(1 2,3 4)", v) @@ -50,7 +50,7 @@ func TestAsWKT(t *testing.T) { t.Run("convert polygon", func(t *testing.T) { require := require.New(t) - f := NewAsWKT(expression.NewLiteral(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) + f := NewAsWKT(expression.NewLiteral(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, sql.PolygonType{})) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) require.Equal("POLYGON((0 0,1 1,1 0,0 0))", v) @@ -140,7 +140,7 @@ func TestGeomFromText(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, v) }) t.Run("create valid linestring with float", func(t *testing.T) { @@ -150,7 +150,7 @@ func TestGeomFromText(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{Points: []sql.Point{{X: 123.456, Y: 789}, {X: 987.654, Y: 321}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 123.456, Y: 789}, {X: 987.654, Y: 321}}}, v) }) t.Run("create valid linestring with whitespace string", func(t *testing.T) { @@ -160,7 +160,7 @@ func TestGeomFromText(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, v) + require.Equal(sql.LineString{Points: []sql.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}}, v) }) t.Run("null string returns null", func(t *testing.T) { @@ -189,7 +189,7 @@ func TestGeomFromText(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) }) t.Run("create valid polygon with multiple lines", func(t *testing.T) { @@ -199,7 +199,7 @@ func TestGeomFromText(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, {Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}, {Points: []sql.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}}, v) }) t.Run("create valid linestring with whitespace string", func(t *testing.T) { @@ -209,7 +209,7 @@ func TestGeomFromText(t *testing.T) { v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{Lines: []sql.Linestring{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{Lines: []sql.LineString{{Points: []sql.Point{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 1, Y: 0}, {X: 0, Y: 0}}}}}, v) }) t.Run("null string returns null", func(t *testing.T) { @@ -236,7 +236,7 @@ func TestGeomFromText(t *testing.T) { t.Run("null axis options returns null", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("POINT(1 2)", sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral(nil, sql.Null)) require.NoError(err) @@ -263,73 +263,103 @@ func TestGeomFromText(t *testing.T) { require.Error(err) }) - t.Run("create valid point with srid", func(t *testing.T) { + t.Run("create valid point with valid srid", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("POINT(1 2)", sql.Blob), - expression.NewLiteral(4230, sql.Uint32)) + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 4230, X: 1, Y: 2}, v) + require.Equal(sql.Point{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, v) + }) + + t.Run("create valid point with invalid srid", func(t *testing.T) { + require := require.New(t) + f, err := NewGeomFromWKT(expression.NewLiteral("POINT(1 2)", sql.Blob), + expression.NewLiteral(4320, sql.Uint32)) + require.NoError(err) + + _, err = f.Eval(sql.NewEmptyContext(), nil) + require.Error(err) }) t.Run("create valid point with srid and axis order long lat", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("POINT(1 2)", sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Point{SRID: 4230, X: 2, Y: 1}, v) + require.Equal(sql.Point{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, v) }) - t.Run("create valid linestring with srid", func(t *testing.T) { + t.Run("create valid linestring with valid srid", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("LINESTRING(1 2, 3 4)", sql.Blob), - expression.NewLiteral(4230, sql.Uint32)) + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 1, Y: 2}, {SRID: 4230, X: 3, Y: 4}}}, v) + require.Equal(sql.LineString{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 4, Y: 3}}}, v) + }) + + t.Run("create valid linestring with invalid srid", func(t *testing.T) { + require := require.New(t) + f, err := NewGeomFromWKT(expression.NewLiteral("LINESTRING(1 2, 3 4)", sql.Blob), + expression.NewLiteral(1, sql.Uint32)) + require.NoError(err) + + _, err = f.Eval(sql.NewEmptyContext(), nil) + require.Error(err) }) t.Run("create valid linestring with srid and axis order long lat", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("LINESTRING(1 2, 3 4)", sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Linestring{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 2, Y: 1}, {SRID: 4230, X: 4, Y: 3}}}, v) + require.Equal(sql.LineString{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 2, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 4, Y: 3}}}, v) }) - t.Run("create valid polygon with srid", func(t *testing.T) { + t.Run("create valid polygon with valid srid", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("POLYGON((0 0, 0 1, 1 0, 0 0))", sql.Blob), - expression.NewLiteral(4230, sql.Uint32)) + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{SRID: 4230, Lines: []sql.Linestring{{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 0, Y: 0}, {SRID: 4230, X: 0, Y: 1}, {SRID: 4230, X: 1, Y: 0}, {SRID: 4230, X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{SRID: sql.GeoSpatialSRID, Lines: []sql.LineString{{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 0, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 1, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 0}}}}}, v) + }) + + t.Run("create valid polygon with invalid srid", func(t *testing.T) { + require := require.New(t) + f, err := NewGeomFromWKT(expression.NewLiteral("POLYGON((0 0, 0 1, 1 0, 0 0))", sql.Blob), + expression.NewLiteral(1234, sql.Uint32)) + require.NoError(err) + + _, err = f.Eval(sql.NewEmptyContext(), nil) + require.Error(err) }) t.Run("create valid polygon with srid", func(t *testing.T) { require := require.New(t) f, err := NewGeomFromWKT(expression.NewLiteral("POLYGON((0 0, 0 1, 1 0, 0 0))", sql.Blob), - expression.NewLiteral(4230, sql.Uint32), + expression.NewLiteral(sql.GeoSpatialSRID, sql.Uint32), expression.NewLiteral("axis-order=long-lat", sql.Blob)) require.NoError(err) v, err := f.Eval(sql.NewEmptyContext(), nil) require.NoError(err) - require.Equal(sql.Polygon{SRID: 4230, Lines: []sql.Linestring{{SRID: 4230, Points: []sql.Point{{SRID: 4230, X: 0, Y: 0}, {SRID: 4230, X: 1, Y: 0}, {SRID: 4230, X: 0, Y: 1}, {SRID: 4230, X: 0, Y: 0}}}}}, v) + require.Equal(sql.Polygon{SRID: sql.GeoSpatialSRID, Lines: []sql.LineString{{SRID: sql.GeoSpatialSRID, Points: []sql.Point{{SRID: sql.GeoSpatialSRID, X: 0, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 1, Y: 0}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 1}, {SRID: sql.GeoSpatialSRID, X: 0, Y: 0}}}}}, v) }) t.Run("check return type", func(t *testing.T) { diff --git a/sql/expression/function/x_y_latitude_longitude.go b/sql/expression/function/x_y_latitude_longitude.go index 8bdebe7865..1027341d32 100644 --- a/sql/expression/function/x_y_latitude_longitude.go +++ b/sql/expression/function/x_y_latitude_longitude.go @@ -282,7 +282,7 @@ func (l *Longitude) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Point needs to have SRID 4326 // TODO: might need to be == Cartesian instead for other SRIDs - if _p.SRID != GeoSpatialSRID { + if _p.SRID != sql.GeoSpatialSRID { return nil, ErrNonGeographic.New(l.FunctionName(), _p.SRID) } @@ -386,7 +386,7 @@ func (l *Latitude) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { // Point needs to have SRID 4326 // TODO: might need to be == Cartesian instead for other SRIDs - if _p.SRID != GeoSpatialSRID { + if _p.SRID != sql.GeoSpatialSRID { return nil, ErrNonGeographic.New(l.FunctionName(), _p.SRID) } diff --git a/sql/geometry.go b/sql/geometry.go index d738804ff4..68c6316721 100644 --- a/sql/geometry.go +++ b/sql/geometry.go @@ -23,20 +23,21 @@ import ( "gopkg.in/src-d/go-errors.v1" ) -// Represents the Geometry type. -// https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometry.html -type Geometry struct { - Inner interface{} // Will be Point, Linestring, or Polygon -} - type GeometryType struct { - InnerType Type // Will be PointType, LinestringType, or PolygonType + SRID uint32 + DefinedSRID bool } var _ Type = GeometryType{} +var _ SpatialColumnType = GeometryType{} var ErrNotGeometry = errors.NewKind("Value of type %T is not a geometry") +const ( + CartesianSRID = uint32(0) + GeoSpatialSRID = uint32(4326) +) + const ( SRIDSize = 4 EndianSize = 1 @@ -55,11 +56,11 @@ const ( WKBPolyID ) -// isLinearRing checks if a Linestring is a linear ring -func isLinearRing(line Linestring) bool { +// isLinearRing checks if a LineString is a linear ring +func isLinearRing(line LineString) bool { // Get number of points numPoints := len(line.Points) - // Check length of Linestring (must be 0 or 4+) points + // Check length of LineString (must be 0 or 4+) points if numPoints != 0 && numPoints < 4 { return false } @@ -104,10 +105,10 @@ func WKBToPoint(buf []byte, isBig bool, srid uint32) (Point, error) { } // WKBToLine parses the data portion of a byte array in WKB format to a point object -func WKBToLine(buf []byte, isBig bool, srid uint32) (Linestring, error) { +func WKBToLine(buf []byte, isBig bool, srid uint32) (LineString, error) { // Must be at least 4 bytes (length of linestring) if len(buf) < 4 { - return Linestring{}, ErrInvalidGISData.New("WKBToLine") + return LineString{}, ErrInvalidGISData.New("WKBToLine") } // Read length of line string @@ -123,7 +124,7 @@ func WKBToLine(buf []byte, isBig bool, srid uint32) (Linestring, error) { // Check length if uint32(len(lineData)) < 16*numPoints { - return Linestring{}, ErrInvalidGISData.New("WKBToLine") + return LineString{}, ErrInvalidGISData.New("WKBToLine") } // Parse points @@ -132,11 +133,11 @@ func WKBToLine(buf []byte, isBig bool, srid uint32) (Linestring, error) { if point, err := WKBToPoint(lineData[16*i:16*(i+1)], isBig, srid); err == nil { points[i] = point } else { - return Linestring{}, ErrInvalidGISData.New("WKBToLine") + return LineString{}, ErrInvalidGISData.New("WKBToLine") } } - return Linestring{SRID: srid, Points: points}, nil + return LineString{SRID: srid, Points: points}, nil } // WKBToPoly parses the data portion of a byte array in WKB format to a point object @@ -159,7 +160,7 @@ func WKBToPoly(buf []byte, isBig bool, srid uint32) (Polygon, error) { // Parse lines s := 0 - lines := make([]Linestring, numLines) + lines := make([]LineString, numLines) for i := uint32(0); i < numLines; i++ { if line, err := WKBToLine(polyData[s:], isBig, srid); err == nil { if isLinearRing(line) { @@ -183,22 +184,15 @@ func (t GeometryType) Compare(a any, b any) (int, error) { return res, nil } - // If b is a geometry type - if bb, ok := b.(Geometry); ok { - return t.Compare(a, bb.Inner) - } - // TODO: probably define operations for types like []byte and string // Expected to receive a geometry type switch inner := a.(type) { case Point: return PointType{}.Compare(inner, b) - case Linestring: - return LinestringType{}.Compare(inner, b) + case LineString: + return LineStringType{}.Compare(inner, b) case Polygon: return PolygonType{}.Compare(inner, b) - case Geometry: - return t.Compare(inner.Inner, b) default: return 0, ErrNotGeometry.New(a) } @@ -233,28 +227,23 @@ func (t GeometryType) Convert(v interface{}) (interface{}, error) { if err != nil { return nil, err } - return Geometry{Inner: geom}, nil + return geom, nil case string: return t.Convert([]byte(inner)) - case Point: - return Geometry{Inner: inner}, nil - case Linestring: - return Geometry{Inner: inner}, nil - case Polygon: - return Geometry{Inner: inner}, nil - case Geometry: + case Point, LineString, Polygon: + if err := t.MatchSRID(inner); err != nil { + return nil, err + } return inner, nil default: - return nil, ErrNotGeometry.New(inner) + return nil, ErrSpatialTypeConversion.New() } } // Equals implements the Type interface. -func (t GeometryType) Equals(otherType Type) bool { - if ot, ok := otherType.(GeometryType); ok { - return t.InnerType.Equals(ot.InnerType) - } - return false +func (t GeometryType) Equals(otherType Type) (ok bool) { + _, ok = otherType.(GeometryType) + return } // Promote implements the Type interface. @@ -293,3 +282,38 @@ func (t GeometryType) Zero() interface{} { // TODO: it doesn't make sense for geometry to have a zero type return nil } + +// GetSpatialTypeSRID implements SpatialColumnType interface. +func (t GeometryType) GetSpatialTypeSRID() (uint32, bool) { + return t.SRID, t.DefinedSRID +} + +// SetSRID implements SpatialColumnType interface. +func (t GeometryType) SetSRID(v uint32) Type { + t.SRID = v + t.DefinedSRID = true + return t +} + +// MatchSRID implements SpatialColumnType interface +func (t GeometryType) MatchSRID(v interface{}) error { + if !t.DefinedSRID { + return nil + } + // if matched with SRID value of row value + var srid uint32 + switch val := v.(type) { + case Point: + srid = val.SRID + case LineString: + srid = val.SRID + case Polygon: + srid = val.SRID + default: + return ErrNotGeometry.New(v) + } + if t.SRID == srid { + return nil + } + return ErrNotMatchingSRID.New(srid, t.SRID) +} diff --git a/sql/geometry_test.go b/sql/geometry_test.go new file mode 100644 index 0000000000..5edf32a221 --- /dev/null +++ b/sql/geometry_test.go @@ -0,0 +1,90 @@ +// Copyright 2022 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sql + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/src-d/go-errors.v1" +) + +func TestSpatialTypeMatchSRID(t *testing.T) { + var ( + // SRID 0 points + Cx1y2 = Point{CartesianSRID, 1, 2} + Cx2y3 = Point{CartesianSRID, 2, 3} + Cx0y0 = Point{CartesianSRID, 0, 0} + + // SRID 4326 points + Gx1y2 = Point{GeoSpatialSRID, 1, 2} + Gx2y3 = Point{GeoSpatialSRID, 2, 3} + Gx0y0 = Point{GeoSpatialSRID, 0, 0} + Gx0y1 = Point{GeoSpatialSRID, 0, 1} + Gx1y0 = Point{GeoSpatialSRID, 1, 0} + ) + tests := []struct { + typeVal SpatialColumnType + objVal interface{} + expected *errors.Kind + }{ + {PointType{CartesianSRID, false}, Cx1y2, nil}, + {PointType{CartesianSRID, false}, Gx1y2, nil}, + {PointType{GeoSpatialSRID, true}, Cx1y2, ErrNotMatchingSRID}, + {PointType{GeoSpatialSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, ErrNotPoint}, + {PointType{GeoSpatialSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, ErrNotPoint}, + + {LineStringType{GeoSpatialSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, nil}, + // MySQL checks only the container's SRID value, so the objects inside can have any SRID value. + // For example, LineStringType column with 4326 allows LineString object with 4326 containing Points with 0 and 4326 SRID values. + {LineStringType{CartesianSRID, false}, LineString{GeoSpatialSRID, []Point{Cx1y2, Gx2y3}}, nil}, + {LineStringType{CartesianSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, ErrNotMatchingSRID}, + {LineStringType{GeoSpatialSRID, true}, Gx2y3, ErrNotLineString}, + {LineStringType{GeoSpatialSRID, true}, Polygon{GeoSpatialSRID, []LineString{{GeoSpatialSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}}}, ErrNotLineString}, + + {PolygonType{CartesianSRID, true}, Polygon{CartesianSRID, []LineString{{GeoSpatialSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}}}, nil}, + {PolygonType{CartesianSRID, false}, Polygon{GeoSpatialSRID, []LineString{{GeoSpatialSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}}}, nil}, + {PolygonType{CartesianSRID, true}, Polygon{GeoSpatialSRID, []LineString{{GeoSpatialSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}}}, ErrNotMatchingSRID}, + {PolygonType{GeoSpatialSRID, true}, Gx2y3, ErrNotPolygon}, + {PolygonType{GeoSpatialSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, ErrNotPolygon}, + + {GeometryType{CartesianSRID, false}, Cx1y2, nil}, + {GeometryType{CartesianSRID, false}, Gx1y2, nil}, + {GeometryType{GeoSpatialSRID, true}, Gx1y2, nil}, + {GeometryType{GeoSpatialSRID, true}, Cx1y2, ErrNotMatchingSRID}, + {GeometryType{GeoSpatialSRID, true}, LineString{GeoSpatialSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}, nil}, + {GeometryType{GeoSpatialSRID, true}, LineString{CartesianSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}, ErrNotMatchingSRID}, + {GeometryType{GeoSpatialSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, nil}, + {GeometryType{CartesianSRID, true}, LineString{GeoSpatialSRID, []Point{Cx1y2, Cx2y3}}, ErrNotMatchingSRID}, + {GeometryType{GeoSpatialSRID, true}, Polygon{GeoSpatialSRID, []LineString{{GeoSpatialSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Gx0y0}}}}, nil}, + {GeometryType{GeoSpatialSRID, true}, Polygon{GeoSpatialSRID, []LineString{{CartesianSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Cx0y0}}}}, nil}, + {GeometryType{CartesianSRID, true}, Polygon{GeoSpatialSRID, []LineString{{CartesianSRID, []Point{Gx0y0, Gx0y1, Gx1y0, Cx0y0}}}}, ErrNotMatchingSRID}, + } + + for _, test := range tests { + s, d := test.typeVal.GetSpatialTypeSRID() + g, _ := test.typeVal.(Type) + t.Run(fmt.Sprintf("%s %v %v match %v", g.String(), s, d, test.objVal), func(t *testing.T) { + err := test.typeVal.MatchSRID(test.objVal) + if test.expected == nil { + require.NoError(t, err) + } else { + require.Error(t, err) + require.True(t, test.expected.Is(err), "Expected error of type %s but got %s", test.expected, err) + } + }) + } +} diff --git a/sql/information_schema/columns_table.go b/sql/information_schema/columns_table.go index e26ae8536a..60a4e24376 100644 --- a/sql/information_schema/columns_table.go +++ b/sql/information_schema/columns_table.go @@ -204,6 +204,12 @@ func columnsRowIter(ctx *sql.Context, cat sql.Catalog, columnNameToDefault map[s } } + if s, ok := c.Type.(sql.SpatialColumnType); ok { + if srid, d := s.GetSpatialTypeSRID(); d { + srsId = fmt.Sprintf("%v", srid) + } + } + rows = append(rows, sql.Row{ "def", // table_catalog db.Name(), // table_schema diff --git a/sql/linestring.go b/sql/linestring.go index a0a05a32ff..faef48dc8b 100644 --- a/sql/linestring.go +++ b/sql/linestring.go @@ -21,34 +21,38 @@ import ( "github.com/dolthub/vitess/go/vt/proto/query" ) -// Represents the Linestring type. +// Represents the LineString type. // https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html -type Linestring struct { +type LineString struct { SRID uint32 Points []Point } -type LinestringType struct{} +type LineStringType struct { + SRID uint32 + DefinedSRID bool +} -var _ Type = LinestringType{} +var _ Type = LineStringType{} +var _ SpatialColumnType = LineStringType{} -var ErrNotLinestring = errors.NewKind("value of type %T is not a linestring") +var ErrNotLineString = errors.NewKind("value of type %T is not a linestring") // Compare implements Type interface. -func (t LinestringType) Compare(a interface{}, b interface{}) (int, error) { +func (t LineStringType) Compare(a interface{}, b interface{}) (int, error) { // Compare nulls if hasNulls, res := compareNulls(a, b); hasNulls { return res, nil } - // Expect to receive a Linestring, throw error otherwise - _a, ok := a.(Linestring) + // Expect to receive a LineString, throw error otherwise + _a, ok := a.(LineString) if !ok { - return 0, ErrNotLinestring.New(a) + return 0, ErrNotLineString.New(a) } - _b, ok := b.(Linestring) + _b, ok := b.(LineString) if !ok { - return 0, ErrNotLinestring.New(b) + return 0, ErrNotLineString.New(b) } // Get shorter length @@ -85,7 +89,7 @@ func (t LinestringType) Compare(a interface{}, b interface{}) (int, error) { } // Convert implements Type interface. -func (t LinestringType) Convert(v interface{}) (interface{}, error) { +func (t LineStringType) Convert(v interface{}) (interface{}, error) { // Allow null if v == nil { return nil, nil @@ -110,26 +114,29 @@ func (t LinestringType) Convert(v interface{}) (interface{}, error) { return line, nil case string: return t.Convert([]byte(val)) - case Linestring: + case LineString: + if err := t.MatchSRID(val); err != nil { + return nil, err + } return val, nil default: - return nil, ErrNotLinestring.New(val) + return nil, ErrSpatialTypeConversion.New() } } // Equals implements the Type interface. -func (t LinestringType) Equals(otherType Type) bool { - _, ok := otherType.(LinestringType) +func (t LineStringType) Equals(otherType Type) bool { + _, ok := otherType.(LineStringType) return ok } // Promote implements the Type interface. -func (t LinestringType) Promote() Type { +func (t LineStringType) Promote() Type { return t } // SQL implements Type interface. -func (t LinestringType) SQL(dest []byte, v interface{}) (sqltypes.Value, error) { +func (t LineStringType) SQL(dest []byte, v interface{}) (sqltypes.Value, error) { if v == nil { return sqltypes.NULL, nil } @@ -145,16 +152,42 @@ func (t LinestringType) SQL(dest []byte, v interface{}) (sqltypes.Value, error) } // String implements Type interface. -func (t LinestringType) String() string { +func (t LineStringType) String() string { return "LINESTRING" } // Type implements Type interface. -func (t LinestringType) Type() query.Type { +func (t LineStringType) Type() query.Type { return sqltypes.Geometry } // Zero implements Type interface. -func (t LinestringType) Zero() interface{} { - return Linestring{Points: []Point{{}, {}}} +func (t LineStringType) Zero() interface{} { + return LineString{Points: []Point{{}, {}}} +} + +// GetSpatialTypeSRID implements SpatialColumnType interface. +func (t LineStringType) GetSpatialTypeSRID() (uint32, bool) { + return t.SRID, t.DefinedSRID +} + +// SetSRID implements SpatialColumnType interface. +func (t LineStringType) SetSRID(v uint32) Type { + t.SRID = v + t.DefinedSRID = true + return t +} + +// MatchSRID implements SpatialColumnType interface +func (t LineStringType) MatchSRID(v interface{}) error { + val, ok := v.(LineString) + if !ok { + return ErrNotLineString.New(v) + } + if !t.DefinedSRID { + return nil + } else if t.SRID == val.SRID { + return nil + } + return ErrNotMatchingSRID.New(val.SRID, t.SRID) } diff --git a/sql/parse/parse.go b/sql/parse/parse.go index bb3ed8b0d2..6191e8ae16 100644 --- a/sql/parse/parse.go +++ b/sql/parse/parse.go @@ -2010,6 +2010,21 @@ func columnDefinitionToColumn(ctx *sql.Context, cd *sqlparser.ColumnDefinition, extra = "auto_increment" } + if cd.Type.SRID != nil { + sridVal, sErr := strconv.ParseInt(string(cd.Type.SRID.Val), 10, 32) + if sErr != nil { + return nil, sErr + } + if uint32(sridVal) != sql.CartesianSRID && uint32(sridVal) != sql.GeoSpatialSRID { + return nil, sql.ErrUnsupportedFeature.New("unsupported SRID value") + } + if s, ok := internalTyp.(sql.SpatialColumnType); ok { + internalTyp = s.SetSRID(uint32(sridVal)) + } else { + return nil, sql.ErrInvalidType.New(fmt.Sprintf("cannot define SRID for %s", internalTyp)) + } + } + return &sql.Column{ Nullable: !isPkey && !bool(cd.Type.NotNull), Type: internalTyp, diff --git a/sql/plan/alter_table.go b/sql/plan/alter_table.go index 955d3b3f68..5685c840bc 100644 --- a/sql/plan/alter_table.go +++ b/sql/plan/alter_table.go @@ -1341,6 +1341,9 @@ func projectRowWithTypes(ctx *sql.Context, sch sql.Schema, projections []sql.Exp for i := range newRow { newRow[i], err = sch[i].Type.Convert(newRow[i]) if err != nil { + if sql.ErrNotMatchingSRID.Is(err) { + err = sql.ErrNotMatchingSRIDWithColName.New(sch[i].Name, err) + } return nil, err } } diff --git a/sql/plan/insert.go b/sql/plan/insert.go index b982125eb7..4945a94661 100644 --- a/sql/plan/insert.go +++ b/sql/plan/insert.go @@ -347,20 +347,22 @@ func (i *insertIter) Next(ctx *sql.Context) (returnRow sql.Row, returnErr error) // Do any necessary type conversions to the target schema for idx, col := range i.schema { if row[idx] != nil { - converted, err := col.Type.Convert(row[idx]) // allows for better error handling - if err != nil { + converted, cErr := col.Type.Convert(row[idx]) // allows for better error handling + if cErr != nil { if i.ignore { - row, err = i.convertDataAndWarn(ctx, row, idx, err) + row, err = i.convertDataAndWarn(ctx, row, idx, cErr) if err != nil { return nil, err } continue } else { // Fill in error with information - if sql.ErrLengthBeyondLimit.Is(err) { - err = sql.ErrLengthBeyondLimit.New(row[idx], col.Name) + if sql.ErrLengthBeyondLimit.Is(cErr) { + cErr = sql.ErrLengthBeyondLimit.New(row[idx], col.Name) + } else if sql.ErrNotMatchingSRID.Is(cErr) { + cErr = sql.ErrNotMatchingSRIDWithColName.New(col.Name, cErr) } - return nil, sql.NewWrappedInsertError(row, err) + return nil, sql.NewWrappedInsertError(row, cErr) } } row[idx] = converted diff --git a/sql/plan/project.go b/sql/plan/project.go index 6098858b21..a647f03292 100644 --- a/sql/plan/project.go +++ b/sql/plan/project.go @@ -162,9 +162,9 @@ func ProjectRow( secondPass = append(secondPass, i) continue } - f, err := expr.Eval(ctx, row) - if err != nil { - return nil, err + f, fErr := expr.Eval(ctx, row) + if fErr != nil { + return nil, fErr } fields = append(fields, f) } diff --git a/sql/plan/show_create_table.go b/sql/plan/show_create_table.go index 67698183cd..4791cd7e7d 100644 --- a/sql/plan/show_create_table.go +++ b/sql/plan/show_create_table.go @@ -247,6 +247,12 @@ func (i *showCreateTablesIter) produceCreateTableStatement(ctx *sql.Context, tab stmt = fmt.Sprintf("%s AUTO_INCREMENT", stmt) } + if c, ok := col.Type.(sql.SpatialColumnType); ok { + if v, d := c.GetSpatialTypeSRID(); d { + stmt = fmt.Sprintf("%s SRID %v", stmt, v) + } + } + // TODO: The columns that are rendered in defaults should be backticked if col.Default != nil { stmt = fmt.Sprintf("%s DEFAULT %s", stmt, col.Default.String()) diff --git a/sql/point.go b/sql/point.go index 129d7d1f34..b5b832bed4 100644 --- a/sql/point.go +++ b/sql/point.go @@ -28,9 +28,13 @@ type Point struct { Y float64 } -type PointType struct{} +type PointType struct { + SRID uint32 + DefinedSRID bool +} var _ Type = PointType{} +var _ SpatialColumnType = PointType{} var ErrNotPoint = errors.NewKind("value of type %T is not a point") @@ -98,9 +102,12 @@ func (t PointType) Convert(v interface{}) (interface{}, error) { case string: return t.Convert([]byte(val)) case Point: + if err := t.MatchSRID(val); err != nil { + return nil, err + } return val, nil default: - return nil, ErrNotPoint.New(val) + return nil, ErrSpatialTypeConversion.New() } } @@ -143,3 +150,29 @@ func (t PointType) Type() query.Type { func (t PointType) Zero() interface{} { return Point{X: 0.0, Y: 0.0} } + +// GetSpatialTypeSRID implements SpatialColumnType interface. +func (t PointType) GetSpatialTypeSRID() (uint32, bool) { + return t.SRID, t.DefinedSRID +} + +// SetSRID implements SpatialColumnType interface. +func (t PointType) SetSRID(v uint32) Type { + t.SRID = v + t.DefinedSRID = true + return t +} + +// MatchSRID implements SpatialColumnType interface +func (t PointType) MatchSRID(v interface{}) error { + val, ok := v.(Point) + if !ok { + return ErrNotPoint.New(v) + } + if !t.DefinedSRID { + return nil + } else if t.SRID == val.SRID { + return nil + } + return ErrNotMatchingSRID.New(val.SRID, t.SRID) +} diff --git a/sql/polygon.go b/sql/polygon.go index 424c09b95a..01b32259b8 100644 --- a/sql/polygon.go +++ b/sql/polygon.go @@ -25,12 +25,16 @@ import ( // https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html type Polygon struct { SRID uint32 - Lines []Linestring + Lines []LineString } -type PolygonType struct{} +type PolygonType struct { + SRID uint32 + DefinedSRID bool +} var _ Type = PolygonType{} +var _ SpatialColumnType = PolygonType{} var ErrNotPolygon = errors.NewKind("value of type %T is not a polygon") @@ -63,7 +67,7 @@ func (t PolygonType) Compare(a interface{}, b interface{}) (int, error) { // Compare each line until there's a difference for i := 0; i < n; i++ { - diff, err := LinestringType{}.Compare(_a.Lines[i], _b.Lines[i]) + diff, err := LineStringType{}.Compare(_a.Lines[i], _b.Lines[i]) if err != nil { return 0, err } @@ -111,9 +115,12 @@ func (t PolygonType) Convert(v interface{}) (interface{}, error) { case string: return t.Convert([]byte(val)) case Polygon: + if err := t.MatchSRID(val); err != nil { + return nil, err + } return val, nil default: - return nil, ErrNotPolygon.New(val) + return nil, ErrSpatialTypeConversion.New() } } @@ -154,5 +161,31 @@ func (t PolygonType) Type() query.Type { // Zero implements Type interface. func (t PolygonType) Zero() interface{} { - return Polygon{Lines: []Linestring{{Points: []Point{{}, {}, {}, {}}}}} + return Polygon{Lines: []LineString{{Points: []Point{{}, {}, {}, {}}}}} +} + +// GetSpatialTypeSRID implements SpatialColumnType interface. +func (t PolygonType) GetSpatialTypeSRID() (uint32, bool) { + return t.SRID, t.DefinedSRID +} + +// SetSRID implements SpatialColumnType interface. +func (t PolygonType) SetSRID(v uint32) Type { + t.SRID = v + t.DefinedSRID = true + return t +} + +// MatchSRID implements SpatialColumnType interface +func (t PolygonType) MatchSRID(v interface{}) error { + val, ok := v.(Polygon) + if !ok { + return ErrNotPolygon.New(v) + } + if !t.DefinedSRID { + return nil + } else if t.SRID == val.SRID { + return nil + } + return ErrNotMatchingSRID.New(val.SRID, t.SRID) } diff --git a/sql/type.go b/sql/type.go index 13dc78a88d..aeb6c9a05b 100644 --- a/sql/type.go +++ b/sql/type.go @@ -84,6 +84,16 @@ type Type2 interface { SQL2(Value) (sqltypes.Value, error) } +// SpatialColumnType is a node that contains a reference to all spatial types. +type SpatialColumnType interface { + // GetSpatialTypeSRID returns the SRID value for spatial types. + GetSpatialTypeSRID() (uint32, bool) + // SetSRID sets SRID value for spatial types. + SetSRID(uint32) Type + // MatchSRID returns nil if column type SRID matches given value SRID otherwise returns error. + MatchSRID(interface{}) error +} + type LikeMatcher interface { CreateMatcher(likeStr string) (regex.DisposableMatcher, error) } @@ -462,7 +472,7 @@ func ColumnTypeToType(ct *sqlparser.ColumnType) (Type, error) { return GeometryType{}, nil case "geometrycollection": case "linestring": - return LinestringType{}, nil + return LineStringType{}, nil case "multilinestring": case "point": return PointType{}, nil