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