1 // Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License, version 2.0,
5 // as published by the Free Software Foundation.
6 //
7 // This program is also distributed with certain software (including
8 // but not limited to OpenSSL) that is licensed under separate terms,
9 // as designated in a particular file or component or in included license
10 // documentation.  The authors of MySQL hereby grant you an additional
11 // permission to link the program and your derivative works with the
12 // separately licensed software that they have included with MySQL.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License, version 2.0, for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
22 
23 #include "sql/gis/wkb.h"
24 
25 #include <cmath>  // M_PI, M_PI_2
26 #include <exception>
27 
28 #include "my_byteorder.h"  // float8get, int4store, uint4korr
29 #include "my_sys.h"        // my_error()
30 #include "myisampack.h"
31 #include "mysqld_error.h"
32 #include "sql/check_stack.h"  // check_stack_overrun
33 #include "sql/gis/coordinate_range_visitor.h"
34 #include "sql/gis/geometries.h"
35 #include "sql/gis/geometries_cs.h"
36 #include "sql/gis/ring_flip_visitor.h"
37 #include "sql/gis/wkb_size_visitor.h"
38 #include "sql/gis/wkb_visitor.h"
39 #include "sql/sql_const.h"  // STACK_MIN_SIZE
40 #include "sql/sql_error.h"
41 #include "sql/srs_fetcher.h"
42 #include "sql_string.h"
43 #include "template_utils.h"  // pointer_cast
44 
45 namespace gis {
46 
47 /// WKB endianness.
48 enum class Byte_order : std::uint8_t {
49   /// Big endian
50   XDR = 0,
51   /// Little endian
52   NDR = 1
53 };
54 
55 /// Checks if a given type is a valid (and supported) WKB type.
56 ///
57 /// @param type The type to check
58 ///
59 /// @retval true The type is valid.
60 /// @retval false The type is invalid.
is_valid_type(Geometry_type type)61 static bool is_valid_type(Geometry_type type) {
62   switch (type) {
63     case Geometry_type::kPoint:
64     case Geometry_type::kLinestring:
65     case Geometry_type::kPolygon:
66     case Geometry_type::kMultipoint:
67     case Geometry_type::kMultilinestring:
68     case Geometry_type::kMultipolygon:
69     case Geometry_type::kGeometrycollection:
70       return true;
71     default:
72       return false; /* purecov: inspected */
73   }
74 }
75 
76 /// Checks if a given type is a subtype of a given supertype.
77 ///
78 /// @param sub The type to check.
79 /// @param super The supertype.
80 ///
81 /// @retval true The type is the supertype or a subtype of it.
82 /// @retval false The type is neither the supertype nor a subtype of it.
is_subtype_of(Geometry_type sub,Geometry_type super)83 static bool is_subtype_of(Geometry_type sub, Geometry_type super) {
84   return (super == Geometry_type::kGeometry || sub == super ||
85           (super == Geometry_type::kGeometrycollection &&
86            (sub == Geometry_type::kMultipoint ||
87             sub == Geometry_type::kMultilinestring ||
88             sub == Geometry_type::kMultipolygon)));
89 }
90 
91 /// Checks if a given type is a valid type and that it is a subtype of a given
92 /// supertype.
93 ///
94 /// @param sub The type to check.
95 /// @param super The supertype.
96 ///
97 /// @retval true The type is a valid subtype of the supertype.
98 /// @retval false The type is invalid or not a subtype of the supertype.
is_valid_type_or_subtype(Geometry_type sub,Geometry_type super)99 static bool is_valid_type_or_subtype(Geometry_type sub, Geometry_type super) {
100   return is_valid_type(sub) && is_subtype_of(sub, super);
101 }
102 
103 template <typename Point_t, typename Linestring_t, typename Linearring_t,
104           typename Polygon_t, typename Geometrycollection_t,
105           typename Multipoint_t, typename Multilinestring_t,
106           typename Multipolygon_t>
107 class Wkb_parser {
108  private:
109   uchar *m_begin;
110   uchar *m_end;
111   Coordinate_system m_coordinate_system;
112   double m_angular_unit;
113   double m_prime_meridian;
114   bool m_positive_north;
115   bool m_positive_east;
116   bool m_swap_axes;
117   THD *m_thd;
118 
transform_x(double x)119   double transform_x(double x) {
120     DBUG_ASSERT(!std::isnan(x));
121     switch (m_coordinate_system) {
122       case Coordinate_system::kCartesian:
123         // The on-disk and in-memory format is x in SRS direction and unit.
124         break;
125       case Coordinate_system::kGeographic:
126         // The on-disk format is x = longitude, in the SRS direction and unit,
127         // and with the SRS meridian.
128         // The in-memory format is x = longitude (Easting) in radians with the
129         // meridian at Greenwich.
130         if (!m_positive_east) x *= -1.0;
131         x += m_prime_meridian;  // Both are in the SRS angular unit
132         x *= m_angular_unit;    // Convert to radians
133         break;
134       default:
135         DBUG_ASSERT(false); /* purecov: inspected */
136         break;
137     }
138 
139     DBUG_ASSERT(!std::isnan(x));
140     return x;
141   }
142 
transform_y(double y)143   double transform_y(double y) {
144     DBUG_ASSERT(!std::isnan(y));
145     switch (m_coordinate_system) {
146       case Coordinate_system::kCartesian:
147         // The on-disk and in-memory format is y in SRS direction and unit.
148         break;
149       case Coordinate_system::kGeographic:
150         // The on-disk format is y = latitude, in the SRS direction and unit.
151         // The in-memory format is y = latitude (Northing) in radians.
152         if (!m_positive_north) y *= -1.0;
153         y *= m_angular_unit;  // Convert to radians
154         break;
155       default:
156         DBUG_ASSERT(false); /* purecov: inspected */
157         break;
158     }
159 
160     DBUG_ASSERT(!std::isnan(y));
161     return y;
162   }
163 
164  public:
Wkb_parser(THD * thd,const dd::Spatial_reference_system * srs,bool ignore_axis_order,uchar * begin,uchar * end)165   Wkb_parser(THD *thd, const dd::Spatial_reference_system *srs,
166              bool ignore_axis_order, uchar *begin, uchar *end)
167       : m_begin(begin),
168         m_end(end),
169         m_coordinate_system(Coordinate_system::kCartesian),
170         m_angular_unit(1.0),
171         m_prime_meridian(0.0),
172         m_positive_north(true),
173         m_positive_east(true),
174         m_swap_axes(false),
175         m_thd(thd) {
176     if (srs == nullptr || srs->is_cartesian()) {
177       m_coordinate_system = Coordinate_system::kCartesian;
178     } else if (srs->is_geographic()) {
179       m_coordinate_system = Coordinate_system::kGeographic;
180       m_angular_unit = srs->angular_unit();
181       m_prime_meridian = srs->prime_meridian();
182       m_positive_north = srs->positive_north();
183       m_positive_east = srs->positive_east();
184       if (!ignore_axis_order && srs->is_lat_long()) m_swap_axes = true;
185     }
186   }
parse_byte_order()187   Byte_order parse_byte_order() {
188     if (m_begin + 1 > m_end) throw std::exception();
189 
190     switch (*(m_begin++)) {
191       case 0:
192         return Byte_order::XDR;
193       case 1:
194         return Byte_order::NDR;
195     }
196 
197     throw std::exception(); /* purecov: inspected */
198   }
199 
reached_end() const200   bool reached_end() const { return m_begin == m_end; }
201 
parse_uint32(Byte_order bo)202   std::uint32_t parse_uint32(Byte_order bo) {
203     if (m_begin + sizeof(std::uint32_t) > m_end) throw std::exception();
204 
205     std::uint32_t i;
206     if (bo == Byte_order::NDR) {
207       i = uint4korr(m_begin);
208     } else {
209       i = load32be(m_begin);
210     }
211 
212     m_begin += 4;
213     return i;
214   }
215 
parse_double(Byte_order bo)216   double parse_double(Byte_order bo) {
217     if (m_begin + sizeof(double) > m_end) throw std::exception();
218 
219     double d;
220     if (bo == Byte_order::NDR) {
221       // Little endian data. Use conversion functions to native endianness.
222       d = float8get(m_begin);
223     } else {
224       d = mi_float8get(m_begin);
225     }
226 
227     m_begin += sizeof(double);
228     return d;
229   }
230 
parse_geometry_type(Byte_order bo)231   Geometry_type parse_geometry_type(Byte_order bo) {
232     if (m_begin + sizeof(std::uint32_t) > m_end) {
233       throw std::exception();
234     }
235 
236     std::uint32_t wkb_type = parse_uint32(bo);
237     Geometry_type type = static_cast<Geometry_type>(wkb_type);
238 
239     if (!is_valid_type_or_subtype(type, Geometry_type::kGeometry))
240       throw std::exception();
241     return type;
242   }
243 
parse_point(Byte_order bo)244   Point_t parse_point(Byte_order bo) {
245     double x = parse_double(bo);
246     double y = parse_double(bo);
247     if (!std::isfinite(x) || !std::isfinite(y)) throw std::exception();
248     if (m_swap_axes)
249       return Point_t(transform_x(y), transform_y(x));
250     else
251       return Point_t(transform_x(x), transform_y(y));
252   }
253 
parse_wkb_point()254   Point_t parse_wkb_point() {
255     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
256       throw std::exception();
257     Byte_order bo = parse_byte_order();
258     Geometry_type type = parse_geometry_type(bo);
259     if (type != Geometry_type::kPoint) throw std::exception();
260     return parse_point(bo);
261   }
262 
parse_linestring(Byte_order bo)263   Linestring_t parse_linestring(Byte_order bo) {
264     Linestring_t ls;
265     std::uint32_t num_points = parse_uint32(bo);
266     if (num_points < 2) throw std::exception();
267     for (std::uint32_t i = 0; i < num_points; i++) {
268       ls.push_back(parse_point(bo));
269     }
270     return ls;
271   }
272 
parse_wkb_linestring()273   Linestring_t parse_wkb_linestring() {
274     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
275       throw std::exception();
276     Byte_order bo = parse_byte_order();
277     Geometry_type type = parse_geometry_type(bo);
278     if (type != Geometry_type::kLinestring) throw std::exception();
279     return parse_linestring(bo);
280   }
281 
parse_polygon(Byte_order bo)282   Polygon_t parse_polygon(Byte_order bo) {
283     Polygon_t py;
284     std::uint32_t num_rings = parse_uint32(bo);
285     if (num_rings == 0) throw std::exception();
286     for (std::uint32_t i = 0; i < num_rings; i++) {
287       Linearring_t lr;
288       std::uint32_t num_points = parse_uint32(bo);
289       if (num_points < 4) throw std::exception();
290       for (std::uint32_t j = 0; j < num_points; j++) {
291         lr.push_back(parse_point(bo));
292       }
293       py.push_back(std::forward<gis::Linearring>(lr));
294     }
295     return py;
296   }
297 
parse_wkb_polygon()298   Polygon_t parse_wkb_polygon() {
299     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
300       throw std::exception();
301     Byte_order bo = parse_byte_order();
302     Geometry_type type = parse_geometry_type(bo);
303 
304     if (type != Geometry_type::kPolygon) throw std::exception();
305     return parse_polygon(bo);
306   }
307 
parse_multipoint(Byte_order bo)308   Multipoint_t parse_multipoint(Byte_order bo) {
309     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
310       throw std::exception();
311     Multipoint_t mpt;
312     std::uint32_t num_points = parse_uint32(bo);
313     if (num_points == 0) throw std::exception();
314     for (std::uint32_t i = 0; i < num_points; i++) {
315       mpt.push_back(parse_wkb_point());
316     }
317     return mpt;
318   }
319 
parse_multilinestring(Byte_order bo)320   Multilinestring_t parse_multilinestring(Byte_order bo) {
321     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
322       throw std::exception();
323     Multilinestring_t mls;
324     std::uint32_t num_linestrings = parse_uint32(bo);
325     if (num_linestrings == 0) throw std::exception();
326     for (std::uint32_t i = 0; i < num_linestrings; i++) {
327       Linestring_t ls;
328       mls.push_back(parse_wkb_linestring());
329     }
330     return mls;
331   }
332 
parse_multipolygon(Byte_order bo)333   Multipolygon_t parse_multipolygon(Byte_order bo) {
334     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
335       throw std::exception();
336     Multipolygon_t mpy;
337     std::uint32_t num_polygons = parse_uint32(bo);
338     if (num_polygons == 0) throw std::exception();
339     for (std::uint32_t i = 0; i < num_polygons; i++) {
340       mpy.push_back(parse_wkb_polygon());
341     }
342     return mpy;
343   }
344 
parse_geometrycollection(Byte_order bo)345   Geometrycollection_t parse_geometrycollection(Byte_order bo) {
346     if (m_thd != nullptr && check_stack_overrun(m_thd, STACK_MIN_SIZE, nullptr))
347       throw std::exception();
348     Geometrycollection_t gc;
349     std::uint32_t num_geometries = parse_uint32(bo);
350     for (std::uint32_t i = 0; i < num_geometries; i++) {
351       Geometry *g = parse_wkb();
352       gc.push_back(std::move(*g));
353       delete g;
354     }
355     return gc;
356   }
357 
parse_wkb()358   Geometry *parse_wkb() {
359     Byte_order bo = parse_byte_order();
360     Geometry_type type = parse_geometry_type(bo);
361 
362     switch (type) {
363       case Geometry_type::kPoint:
364         return new Point_t(parse_point(bo));
365       case Geometry_type::kLinestring:
366         return new Linestring_t(parse_linestring(bo));
367       case Geometry_type::kPolygon:
368         return new Polygon_t(parse_polygon(bo));
369       case Geometry_type::kMultipoint:
370         return new Multipoint_t(parse_multipoint(bo));
371       case Geometry_type::kMultilinestring:
372         return new Multilinestring_t(parse_multilinestring(bo));
373       case Geometry_type::kMultipolygon:
374         return new Multipolygon_t(parse_multipolygon(bo));
375       case Geometry_type::kGeometrycollection:
376         return new Geometrycollection_t(parse_geometrycollection(bo));
377       default:
378         throw std::exception(); /* purecov: inspected */
379     }
380   }
381 };
382 
parse_wkb(THD * thd,const dd::Spatial_reference_system * srs,const char * wkb,std::size_t length,bool ignore_axis_order)383 std::unique_ptr<Geometry> parse_wkb(THD *thd,
384                                     const dd::Spatial_reference_system *srs,
385                                     const char *wkb, std::size_t length,
386                                     bool ignore_axis_order) {
387   unsigned char *begin = pointer_cast<unsigned char *>(const_cast<char *>(wkb));
388   unsigned char *end = begin + length;
389   std::unique_ptr<Geometry> g = nullptr;
390   bool res;
391 
392   if (srs == nullptr || srs->is_cartesian()) {
393     try {
394       Wkb_parser<Cartesian_point, Cartesian_linestring, Cartesian_linearring,
395                  Cartesian_polygon, Cartesian_geometrycollection,
396                  Cartesian_multipoint, Cartesian_multilinestring,
397                  Cartesian_multipolygon>
398           parser(thd, srs, ignore_axis_order, begin, end);
399       g.reset(parser.parse_wkb());
400       res = !g || !parser.reached_end();
401     } catch (...) {
402       res = true;
403     }
404   } else if (srs->is_geographic()) {
405     try {
406       Wkb_parser<Geographic_point, Geographic_linestring, Geographic_linearring,
407                  Geographic_polygon, Geographic_geometrycollection,
408                  Geographic_multipoint, Geographic_multilinestring,
409                  Geographic_multipolygon>
410           parser(thd, srs, ignore_axis_order, begin, end);
411       g.reset(parser.parse_wkb());
412       res = !g || !parser.reached_end();
413     } catch (...) {
414       res = true;
415     }
416   } else {
417     DBUG_ASSERT(false); /* purecov: inspected */
418     return std::unique_ptr<Geometry>();
419   }
420 
421   if (res) {
422     return std::unique_ptr<Geometry>();
423   }
424 
425   return g;
426 }
427 
parse_srid(const char * str,std::size_t length,srid_t * srid)428 bool parse_srid(const char *str, std::size_t length, srid_t *srid) {
429   unsigned char *begin = pointer_cast<unsigned char *>(const_cast<char *>(str));
430 
431   if (length < sizeof(srid_t)) return true;
432   *srid = uint4korr(begin);  // Always little-endian.
433   return false;
434 }
435 
parse_geometry(THD * thd,const char * func_name,const String * str,const dd::Spatial_reference_system ** srs,std::unique_ptr<Geometry> * geometry,bool treat_unknown_srid_as_cartesian)436 bool parse_geometry(THD *thd, const char *func_name, const String *str,
437                     const dd::Spatial_reference_system **srs,
438                     std::unique_ptr<Geometry> *geometry,
439                     bool treat_unknown_srid_as_cartesian) {
440   srid_t srid;
441   if (parse_srid(str->ptr(), str->length(), &srid)) {
442     my_error(ER_GIS_INVALID_DATA, MYF(0), func_name);
443     return true;
444   }
445 
446   Srs_fetcher fetcher(thd);
447   *srs = nullptr;
448   if (srid != 0 && fetcher.acquire(srid, srs)) return true;
449 
450   if (srid != 0 && *srs == nullptr && !treat_unknown_srid_as_cartesian) {
451     my_error(ER_SRS_NOT_FOUND, MYF(0), srid);
452     return true;
453   }
454 
455   *geometry = gis::parse_wkb(thd, *srs, str->ptr() + sizeof(srid_t),
456                              str->length() - sizeof(srid_t), true);
457   if (!(*geometry)) {
458     // Parsing failed, assume invalid input data.
459     my_error(ER_GIS_INVALID_DATA, MYF(0), func_name);
460     return true;
461   }
462 
463   // Flip polygon rings so that the exterior ring is counter-clockwise and
464   // interior rings are clockwise.
465   double semi_major = 1.0;
466   double semi_minor = 1.0;
467   if (*srs && (*srs)->is_geographic()) {
468     semi_major = (*srs)->semi_major_axis();
469     semi_minor = (*srs)->semi_minor_axis();
470   }
471   gis::Ring_flip_visitor rfv(semi_major, semi_minor);
472   (*geometry)->accept(&rfv);
473   if (rfv.invalid()) {
474     // There's something wrong with a polygon in the geometry.
475     my_error(ER_GIS_INVALID_DATA, MYF(0), func_name);
476     return true;
477   }
478 
479   gis::Coordinate_range_visitor crv(*srs);
480   if ((*geometry)->accept(&crv)) {
481     if (crv.longitude_out_of_range()) {
482       my_error(ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE, MYF(0), func_name,
483                crv.coordinate_value(), (*srs)->from_radians(-M_PI),
484                (*srs)->from_radians(M_PI));
485       return true;
486     }
487     if (crv.latitude_out_of_range()) {
488       my_error(ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE, MYF(0), func_name,
489                crv.coordinate_value(), (*srs)->from_radians(-M_PI_2),
490                (*srs)->from_radians(M_PI_2));
491       return true;
492     }
493   }
494 
495   return false;
496 }
497 
write_geometry(const dd::Spatial_reference_system * srs,Geometry & geometry,String * str)498 bool write_geometry(const dd::Spatial_reference_system *srs, Geometry &geometry,
499                     String *str) {
500   Wkb_size_visitor wkb_size;
501   geometry.accept(&wkb_size);
502   size_t geometry_size =
503       sizeof(std::uint32_t) + wkb_size.size();  // SRID + WKB.
504   str->set_charset(&my_charset_bin);
505   if (str->reserve(geometry_size)) {
506     /* purecov: begin inspected */
507     my_error(ER_OUTOFMEMORY, MYF(0));
508     return true;
509     /* purecov: end */
510   }
511   str->length(geometry_size);
512   char *buffer = &((*str)[0]);
513   int4store(buffer, srs == nullptr ? 0 : srs->id());
514   buffer += sizeof(std::uint32_t);
515   Wkb_visitor wkb(srs, buffer, wkb_size.size());
516   geometry.accept(&wkb);
517 
518   return false;
519 }
520 
521 }  // namespace gis
522