1 /*
2 Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24 #include "sql/spatial.h"
25
26 #include <algorithm>
27 #include <cmath> // isfinite
28 #include <map>
29 #include <memory>
30 #include <new>
31 #include <type_traits>
32 #include <utility>
33
34 #include "m_ctype.h"
35 #include "m_string.h"
36 #include "my_byteorder.h"
37 #include "my_dbug.h"
38 #include "my_macros.h"
39 #include "my_sys.h"
40 #include "myisampack.h"
41 #include "mysqld_error.h"
42 #include "prealloced_array.h"
43 #include "sql/check_stack.h" // check_stack_overrun
44 #include "sql/current_thd.h"
45 #include "sql/gis/srid.h"
46 #include "sql/gis_bg_traits.h" // IWYU pragma: keep
47 #include "sql/gstream.h" // Gis_read_stream
48 #include "sql/psi_memory_key.h"
49 #include "sql/sql_const.h" // STACK_MIN_SIZE
50 #include "sql_string.h" // String
51 #include "template_utils.h" // pointer_cast
52 #include "unsafe_string_append.h"
53
gis_wkb_alloc(size_t sz)54 void *gis_wkb_alloc(size_t sz) {
55 sz += GEOM_HEADER_SIZE;
56 char *p = static_cast<char *>(
57 my_malloc(key_memory_Geometry_objects_data, sz, MYF(MY_FAE)));
58 p += GEOM_HEADER_SIZE;
59 return p;
60 }
61
gis_wkb_realloc(void * p,size_t sz)62 void *gis_wkb_realloc(void *p, size_t sz) {
63 char *cp = static_cast<char *>(p);
64 if (cp) cp -= GEOM_HEADER_SIZE;
65 sz += GEOM_HEADER_SIZE;
66
67 p = my_realloc(key_memory_Geometry_objects_data, cp, sz, MYF(MY_FAE));
68 cp = static_cast<char *>(p);
69 return cp + GEOM_HEADER_SIZE;
70 }
71
72 /***************************** MBR *******************************/
73
74 /*
75 Returns 0/1 if this MBR doesn't/does touch mbr. Returns -1 if the MBRs
76 contain invalid data. This convention is true for all MBR relation test
77 functions.
78 */
touches(const MBR * mbr) const79 int MBR::touches(const MBR *mbr) const {
80 const MBR *mbr2 = mbr;
81 const MBR *mbr1 = this;
82 int ret = 0;
83 int dim1 = dimension();
84 int dim2 = mbr->dimension();
85
86 DBUG_ASSERT(dim1 >= 0 && dim1 <= 2 && dim2 >= 0 && dim2 <= 2);
87 if (dim1 == 0 && dim2 == 0) return 0;
88 if (dim1 == 0 && dim2 == 1)
89 return ((mbr1->xmin == mbr2->xmin && mbr1->ymin == mbr2->ymin) ||
90 (mbr1->xmin == mbr2->xmax && mbr1->ymin == mbr2->ymax));
91 if (dim1 == 1 && dim2 == 0) return mbr->touches(this);
92
93 DBUG_ASSERT(dim1 + dim2 >= 2);
94 ret = ((mbr2->xmin == mbr1->xmax || mbr2->xmax == mbr1->xmin) &&
95 (mbr1->ymin <= mbr2->ymax && mbr1->ymax >= mbr2->ymin)) ||
96 ((mbr2->ymin == mbr1->ymax || mbr2->ymax == mbr1->ymin) &&
97 (mbr1->xmin <= mbr2->xmax && mbr1->xmax >= mbr2->xmin));
98
99 if (ret && dim1 == 1 && dim2 == 1) {
100 // The two line segments may overlap, rather than touch.
101 int overlaps = ((mbr1->ymin == mbr1->ymax && mbr1->ymin == mbr2->ymax &&
102 mbr2->ymin == mbr2->ymax && mbr1->xmin < mbr2->xmax &&
103 mbr1->xmax > mbr2->xmin) ||
104 (mbr1->xmin == mbr1->xmax && mbr2->xmin == mbr2->xmax &&
105 mbr1->xmin == mbr2->xmin && mbr1->ymin < mbr2->ymax &&
106 mbr1->ymax > mbr2->ymin));
107 if (overlaps) ret = 0;
108 }
109
110 return ret;
111 }
112
within(const MBR * mbr) const113 int MBR::within(const MBR *mbr) const {
114 int dim1 = dimension();
115 int dim2 = mbr->dimension();
116
117 DBUG_ASSERT(dim1 >= 0 && dim1 <= 2 && dim2 >= 0 && dim2 <= 2);
118
119 /*
120 Either/both of the two operands can degrade to a point or a
121 horizontal/vertical line segment, and we have to treat such cases
122 separately.
123 */
124 switch (dim1) {
125 case 0:
126 DBUG_ASSERT(xmin == xmax && ymin == ymax);
127 switch (dim2) {
128 case 0:
129 DBUG_ASSERT(mbr->xmin == mbr->xmax && mbr->ymin == mbr->ymax);
130 return equals(mbr);
131 break;
132 case 1:
133 DBUG_ASSERT((mbr->xmin == mbr->xmax && mbr->ymin != mbr->ymax) ||
134 (mbr->ymin == mbr->ymax && mbr->xmin != mbr->xmax));
135 return ((xmin > mbr->xmin && xmin < mbr->xmax && ymin == mbr->ymin) ||
136 (ymin > mbr->ymin && ymin < mbr->ymax && xmin == mbr->xmin));
137 break;
138 case 2:
139 DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
140 return (xmin > mbr->xmin && xmax < mbr->xmax && ymin > mbr->ymin &&
141 ymax < mbr->ymax);
142 break;
143 }
144 break;
145 case 1:
146 DBUG_ASSERT((xmin == xmax && ymin != ymax) ||
147 (ymin == ymax && xmin != xmax));
148 switch (dim2) {
149 case 0:
150 DBUG_ASSERT(mbr->xmin == mbr->xmax && mbr->ymin == mbr->ymax);
151 return 0;
152 break;
153 case 1:
154 DBUG_ASSERT((mbr->xmin == mbr->xmax && mbr->ymin != mbr->ymax) ||
155 (mbr->ymin == mbr->ymax && mbr->xmin != mbr->xmax));
156 return (
157 (xmin == xmax && mbr->xmin == mbr->xmax && mbr->xmin == xmin &&
158 mbr->ymin <= ymin && mbr->ymax >= ymax) ||
159 (ymin == ymax && mbr->ymin == mbr->ymax && mbr->ymin == ymin &&
160 mbr->xmin <= xmin && mbr->xmax >= xmax));
161 break;
162 case 2:
163 DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
164 return ((xmin == xmax && xmin > mbr->xmin && xmax < mbr->xmax &&
165 ymin >= mbr->ymin && ymax <= mbr->ymax) ||
166 (ymin == ymax && ymin > mbr->ymin && ymax < mbr->ymax &&
167 xmin >= mbr->xmin && xmax <= mbr->xmax));
168 break;
169 }
170 break;
171 case 2:
172 DBUG_ASSERT(xmin != xmax && ymin != ymax);
173 switch (dim2) {
174 case 0:
175 case 1:
176 return 0;
177 break;
178 case 2:
179 DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
180 return ((mbr->xmin <= xmin) && (mbr->ymin <= ymin) &&
181 (mbr->xmax >= xmax) && (mbr->ymax >= ymax));
182 break;
183 }
184 break;
185 }
186
187 // Never reached.
188 DBUG_ASSERT(false);
189 return 0;
190 }
191
192 /*
193 exponential notation :
194 1 sign
195 1 number before the decimal point
196 1 decimal point
197 17 number of significant digits (see String::qs_append(double))
198 1 'e' sign
199 1 exponent sign
200 3 exponent digits
201 ==
202 25
203
204 "f" notation :
205 1 optional 0
206 1 sign
207 17 number significant digits (see String::qs_append(double) )
208 1 decimal point
209 ==
210 20
211 */
212
213 #define MAX_DIGITS_IN_DOUBLE 25
214
215 /**
216 Distance to another point.
217 */
distance(const point_xy & p) const218 double point_xy::distance(const point_xy &p) const {
219 /* On 32bit platforms, sqrt(inf) may produce a wrong number that isn't inf. */
220 const double a = pow(x - p.x, 2.0);
221 if (!std::isfinite(a)) return a;
222 const double b = pow(y - p.y, 2.0);
223 if (!std::isfinite(a + b)) return a + b;
224 return sqrt(a + b);
225 }
226
227 /***************************** Gis_class_info *******************************/
228
229 String Geometry::bad_geometry_data("Bad object", &my_charset_bin);
230
231 Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last + 1] = {
232 nullptr};
233
234 static Geometry::Class_info **ci_collection_end =
235 Geometry::ci_collection + Geometry::wkb_last + 1;
236
Class_info(const char * name,int type_id,create_geom_t create_func)237 Geometry::Class_info::Class_info(const char *name, int type_id,
238 create_geom_t create_func)
239 : m_name{name, strlen(name)},
240 m_type_id(type_id),
241 m_create_func(create_func) {
242 ci_collection[type_id] = this;
243 }
244
create_point(char * buffer)245 inline static Geometry *create_point(char *buffer) {
246 return ::new (buffer) Gis_point(false);
247 }
248
create_linestring(char * buffer)249 inline static Geometry *create_linestring(char *buffer) {
250 return ::new (buffer) Gis_line_string(false);
251 }
252
create_polygon(char * buffer)253 inline static Geometry *create_polygon(char *buffer) {
254 return ::new (buffer) Gis_polygon(false);
255 }
256
create_multipoint(char * buffer)257 inline static Geometry *create_multipoint(char *buffer) {
258 return ::new (buffer) Gis_multi_point(false);
259 }
260
create_multipolygon(char * buffer)261 inline static Geometry *create_multipolygon(char *buffer) {
262 return ::new (buffer) Gis_multi_polygon(false);
263 }
264
create_multilinestring(char * buffer)265 inline static Geometry *create_multilinestring(char *buffer) {
266 return ::new (buffer) Gis_multi_line_string(false);
267 }
268
create_geometrycollection(char * buffer)269 inline static Geometry *create_geometrycollection(char *buffer) {
270 return ::new (buffer) Gis_geometry_collection();
271 }
272
273 /**
274 Check if geometry type sub is a subtype of super.
275
276 Since Geometry::wkbType can't represent the geometry type, the
277 superclass of all geometry types, this function can't check
278 that. The supertype has to be a subtype of geometry.
279
280 @param sub The type to check
281 @param super The supertype
282
283 @return True if t1 is a subtype of t2
284 */
is_subtype_of(Geometry::wkbType sub,Geometry::wkbType super)285 inline static bool is_subtype_of(Geometry::wkbType sub,
286 Geometry::wkbType super) {
287 return (super == Geometry::wkb_geometrycollection &&
288 (sub == Geometry::wkb_multipoint ||
289 sub == Geometry::wkb_multilinestring ||
290 sub == Geometry::wkb_multipolygon));
291 }
292
293 static Geometry::Class_info point_class("POINT", Geometry::wkb_point,
294 create_point);
295
296 static Geometry::Class_info linestring_class("LINESTRING",
297 Geometry::wkb_linestring,
298 create_linestring);
299 static Geometry::Class_info polygon_class("POLYGON", Geometry::wkb_polygon,
300 create_polygon);
301 static Geometry::Class_info multipoint_class("MULTIPOINT",
302 Geometry::wkb_multipoint,
303 create_multipoint);
304 static Geometry::Class_info multilinestring_class("MULTILINESTRING",
305 Geometry::wkb_multilinestring,
306 create_multilinestring);
307 static Geometry::Class_info multipolygon_class("MULTIPOLYGON",
308 Geometry::wkb_multipolygon,
309 create_multipolygon);
310 static Geometry::Class_info geometrycollection_class(
311 "GEOMCOLLECTION", Geometry::wkb_geometrycollection,
312 create_geometrycollection);
313
314 /***************************** Geometry *******************************/
315
find_class(const char * name,size_t len)316 Geometry::Class_info *Geometry::find_class(const char *name, size_t len) {
317 for (Class_info **cur_rt = ci_collection; cur_rt < ci_collection_end;
318 cur_rt++) {
319 if (*cur_rt && (*cur_rt)->m_type_id == Geometry::wkb_geometrycollection) {
320 if (len == 18 &&
321 my_strnncoll(&my_charset_latin1,
322 pointer_cast<const uchar *>("GEOMETRYCOLLECTION"), len,
323 pointer_cast<const uchar *>(name), len) == 0)
324 return *cur_rt;
325 } else if (*cur_rt && ((*cur_rt)->m_name.length == len) &&
326 (my_strnncoll(&my_charset_latin1,
327 pointer_cast<const uchar *>((*cur_rt)->m_name.str),
328 len, pointer_cast<const uchar *>(name), len) == 0))
329 return *cur_rt;
330 }
331 return nullptr;
332 }
333
create_by_typeid(Geometry_buffer * buffer,int type_id)334 Geometry *Geometry::create_by_typeid(Geometry_buffer *buffer, int type_id) {
335 Class_info *ci;
336 if (!(ci = find_class(type_id))) return nullptr;
337 return (*ci->m_create_func)(buffer->data);
338 }
339
340 /**
341 Construct a Geometry object using GEOMETRY byte string. This function is
342 called by all GIS functions to make a Geometry object from a GEOMETRY byte
343 string, which can come from table storage, or returned from other GIS
344 function, or directly provided by user via client.
345
346 The WKB can be of either endianess --- when user directly pass WKB
347 byte string to us, he/she can pass big endian WKB, otherwise the WKB is
348 always little endian. And we should reject big endian WKB because all the
349 rest of the GIS code assumes the internal WKB data being always little endian.
350
351 @param buffer The place where the Geometry object is constructed on.
352 @param data is a byte string with an optional srid prepending a WKB format
353 byte string, which is called a GEOMETRY byte string and which is the inner
354 storage format of all geometries in MySQL.
355 @param data_len number of bytes of the byte string refered by data.
356 @param has_srid whether data argument starts with an srid or not.
357 By default it's true, if false, data starts with WKB header, and caller
358 is responsible to specify an srid to this object later.
359 @return Constructed geometry object.
360 */
construct(Geometry_buffer * buffer,const char * data,uint32 data_len,bool has_srid)361 Geometry *Geometry::construct(Geometry_buffer *buffer, const char *data,
362 uint32 data_len, bool has_srid) {
363 uint32 geom_type;
364 Geometry *result;
365 wkbByteOrder bo;
366 String wkb_le;
367 uint32 srid_sz = has_srid ? SRID_SIZE : 0;
368 // Check the GEOMETRY byte string is valid, which would at least have an
369 // SRID, a WKB header, and 4 more bytes for object count or Point
370 // coordinate.
371 if (data_len < srid_sz + WKB_HEADER_SIZE + 4) return nullptr;
372
373 bo = ::get_byte_order(data + srid_sz);
374
375 if (bo != Geometry::wkb_ndr) {
376 my_error(ER_GIS_DATA_WRONG_ENDIANESS, MYF(0));
377 return nullptr;
378 /*
379 Don't try to convert endianess but error out because we can't
380 replace the bytes refered by data, it can be from any source.
381 Users can call GeometryFromWKB to use WKB of either endianess
382 if they have to pass WKB/Geometry byte string from client to us.
383 */
384 }
385
386 /* + 1 to skip the byte order (stored in position SRID_SIZE). */
387 geom_type = uint4korr(data + srid_sz + 1);
388 if (geom_type < wkb_first || geom_type > wkb_last ||
389 !(result = create_by_typeid(buffer, (int)geom_type)))
390 return nullptr;
391
392 gis::srid_t srid = 0;
393 if (has_srid) {
394 srid = uint4korr(data);
395 result->set_srid(srid);
396 }
397
398 if (geom_type == wkb_point) {
399 if (data_len - srid_sz - WKB_HEADER_SIZE < POINT_DATA_SIZE) return nullptr;
400 result->set_data_ptr(data + srid_sz + WKB_HEADER_SIZE, POINT_DATA_SIZE);
401 } else
402 result->set_data_ptr(data + srid_sz + WKB_HEADER_SIZE,
403 data_len - srid_sz - WKB_HEADER_SIZE);
404 result->has_geom_header_space(has_srid);
405 if (result->get_geotype() == wkb_polygon) result->polygon_is_wkb_form(true);
406
407 /*
408 Check whether the GEOMETRY byte string is a valid and complete one.
409 Do not allow extra trailing bytes if this is a GEOMETRY byte string,
410 otherwise we are creating a geometry object using part of the byte string
411 which is longer than we need here but are not trash bytes.
412 */
413 const uint32 result_len = result->get_data_size();
414 const uint32 header_size = (has_srid ? GEOM_HEADER_SIZE : WKB_HEADER_SIZE);
415 if (result_len == GET_SIZE_ERROR ||
416 (has_srid && (result_len + header_size) != data_len))
417 return nullptr;
418
419 return result;
420 }
421
422 /**
423 Read wkt text from trs, and write little endian wkb encoding into 'wkt',
424 and create a Geometry instance in 'buffer'. If 'init_stream' is true,
425 shallow assign data in 'wkt' to the Geometry object to be returned.
426 @param buffer Place to create the returned Geometry object at.
427 @param trs WKT read stream.
428 @param wkb Little endian WKB buffer for WKB data of the returned Geometry
429 object.
430 @param init_stream Whether set WKB buffer pointer to returned Geometry
431 object.
432 @param check_trailing Whether to flag an error (by returning nullptr) if there
433 are trailing bytes in the string.
434 @return A Geometry object with data specified by the WKT.
435 */
create_from_wkt(Geometry_buffer * buffer,Gis_read_stream * trs,String * wkb,bool init_stream,bool check_trailing)436 Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer,
437 Gis_read_stream *trs, String *wkb,
438 bool init_stream, bool check_trailing) {
439 LEX_CSTRING name;
440 Class_info *ci;
441
442 if (trs->get_next_word(&name)) {
443 trs->set_error_msg("Geometry name expected");
444 return nullptr;
445 }
446 if (!(ci = find_class(name.str, name.length)) ||
447 wkb->reserve(WKB_HEADER_SIZE, 512))
448 return nullptr;
449 Geometry *result = (*ci->m_create_func)(buffer->data);
450 q_append((char)wkb_ndr, wkb);
451 q_append((uint32)result->get_class_info()->m_type_id, wkb);
452
453 if (result->init_from_wkt(trs, wkb) ||
454 (check_trailing && !trs->is_end_of_stream()))
455 return nullptr;
456
457 if (init_stream)
458 result->set_data_ptr(wkb->ptr() + WKB_HEADER_SIZE,
459 wkb->length() - WKB_HEADER_SIZE);
460 result->has_geom_header_space(true);
461 if (result->get_geotype() == wkb_polygon) result->polygon_is_wkb_form(true);
462
463 return result;
464 }
465
466 /**
467 Write this geometry's WKB byte string into specified buffer, the SRID is
468 not written into the buffer.
469
470 @param wkb The buffer to write WKB byte string into.
471 @param shallow_copy Whether do shallow copy by using this object's memory
472 without owning it or duplicating the byte string.
473 @return true if got error, false if successful.
474 */
as_wkb(String * wkb,bool shallow_copy) const475 bool Geometry::as_wkb(String *wkb, bool shallow_copy) const {
476 DBUG_ASSERT(wkb->ptr() < get_cptr() - GEOM_HEADER_SIZE ||
477 wkb->ptr() > get_cptr() + get_nbytes());
478
479 if (shallow_copy) {
480 /*
481 This object must have GEOMETRY header space, and we simply assign to
482 wkb, the memory is still owned by the String object of this Geometry
483 object, i.e. the String object holding WKB data for this object.
484
485 Don't write to this object's own String buffer.
486 */
487 DBUG_ASSERT(wkb->ptr() != get_cptr() - GEOM_HEADER_SIZE);
488
489 DBUG_ASSERT(!(get_geotype() == wkb_polygon &&
490 (!polygon_is_wkb_form() || is_bg_adapter())));
491 wkb->set(get_cptr() - WKB_HEADER_SIZE, get_nbytes() + WKB_HEADER_SIZE,
492 &my_charset_bin);
493 return false;
494 }
495
496 if (wkb->reserve(WKB_HEADER_SIZE + this->get_nbytes(), 512) ||
497 get_data_ptr() == nullptr)
498 return true;
499
500 write_wkb_header(wkb, get_geotype());
501 if (get_geotype() != wkb_polygon)
502 q_append(static_cast<const char *>(this->get_data_ptr()),
503 this->get_nbytes(), wkb);
504 else {
505 size_t len = 0;
506 void *ptr = get_packed_ptr(this, &len);
507 wkb->append(static_cast<char *>(ptr), len);
508 gis_wkb_free(ptr);
509 }
510
511 return false;
512 }
513
514 /**
515 Write this geometry's GEOMETRY byte string into specified buffer, the SRID
516 will be written before the WKB string to form a GEOMETRY byte string.
517
518 @param buf The buffer to write GEOMETRY byte string into.
519 @param shallow_copy Whether do shallow copy by using this object's memory
520 without owning it or duplicating the byte string.
521 @return true if got error, false if successful.
522 */
as_geometry(String * buf,bool shallow_copy) const523 bool Geometry::as_geometry(String *buf, bool shallow_copy) const {
524 if (shallow_copy) {
525 /*
526 This object must have GEOMETRY header space, and we simply assign to
527 buf, the memory is still owned by the String object of this Geometry
528 object, i.e. the String object holding WKB data for this object.
529
530 Don't write to this object's own String buffer.
531 */
532 DBUG_ASSERT(has_geom_header_space());
533
534 DBUG_ASSERT(!(get_geotype() == wkb_polygon &&
535 (!polygon_is_wkb_form() || is_bg_adapter())));
536
537 if (buf->ptr() != get_cptr() - GEOM_HEADER_SIZE) {
538 DBUG_ASSERT(buf->ptr() < get_cptr() - GEOM_HEADER_SIZE ||
539 buf->ptr() > get_cptr() + get_nbytes());
540 buf->set(get_cptr() - GEOM_HEADER_SIZE, get_nbytes() + GEOM_HEADER_SIZE,
541 &my_charset_bin);
542 }
543 return false;
544 }
545
546 if ((buf->ptr() == get_cptr() - GEOM_HEADER_SIZE)) {
547 if (buf->is_alloced()) return false;
548 } else
549 DBUG_ASSERT(buf->ptr() < get_cptr() - GEOM_HEADER_SIZE ||
550 buf->ptr() > get_cptr() + get_nbytes());
551
552 if (buf->reserve(SRID_SIZE + WKB_HEADER_SIZE + this->get_nbytes(), 512) ||
553 get_data_ptr() == nullptr)
554 return true;
555
556 write_geometry_header(buf, get_srid(), get_geotype());
557 if (get_geotype() != wkb_polygon)
558 q_append(static_cast<const char *>(this->get_data_ptr()),
559 this->get_nbytes(), buf);
560 else {
561 size_t len = 0;
562 void *ptr = get_packed_ptr(this, &len);
563 buf->append(static_cast<char *>(ptr), len);
564 gis_wkb_free(ptr);
565 }
566 return false;
567 }
568
569 /**
570 WKB scanner event handler that checks if the WKB string is well formed.
571
572 This doesn't check if the geometry is valid (e.g., it's not checking
573 if a polygon is self-intersecting), it only checks some simple rules
574 for WKB well-formedness:
575
576 R1. The byte order is as specified (constructor parameter)
577 R2. The wkbType is within the supported range
578 R3. The geometry is of the specified type (constructor parameter),
579 or a subtype
580 R4. Nested geometries contain only geometries that can be contained
581 by that type
582 R5. Linestrings have at least two points
583 R6. Polygon rings have at least four points
584 R7. Polygons have at least one ring
585 R8. Collections, except geometrycollection, contain at least one
586 element.
587
588 An additional requirement, that the WKB ends exactly at the end of
589 the string, is checked by Geometry::is_well_formed(). The last parse
590 position is maintained as last_position here to make that test
591 possible.
592 */
593 class Geometry_well_formed_checker : public WKB_scanner_event_handler {
594 private:
595 Prealloced_array<Geometry::wkbType, 8> type; /// Current stack of types
596 Geometry::wkbType previous_type; /// Type of previous start/end
597 int points_in_linestring; /// Points in current ls
598 const void *last_position; /// Last wkb pointer seen
599 bool is_ok; /// Whether the WKB is OK so far
600 Geometry::wkbByteOrder required_byte_order;
601
602 public:
603 /**
604 Create a new even handler.
605
606 @param type Expected geometry type. If set to
607 Geometry::wkb_invalid_type, any geometry is allowed.
608 @param required_byte_order The expected byted order
609 */
Geometry_well_formed_checker(Geometry::wkbType type,Geometry::wkbByteOrder required_byte_order)610 Geometry_well_formed_checker(Geometry::wkbType type,
611 Geometry::wkbByteOrder required_byte_order)
612 : type(PSI_NOT_INSTRUMENTED),
613 previous_type(Geometry::wkb_invalid_type),
614 points_in_linestring(0),
615 last_position(nullptr),
616 is_ok(true),
617 required_byte_order(required_byte_order) {
618 this->type.push_back(type);
619 }
620
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void *,uint32,bool has_hdr)621 virtual void on_wkb_start(Geometry::wkbByteOrder bo,
622 Geometry::wkbType geotype, const void *, uint32,
623 bool has_hdr) {
624 if (!is_ok) return;
625
626 // The byte order must be the specified one (R1).
627 if (required_byte_order != Geometry::wkb_invalid &&
628 bo != required_byte_order) {
629 is_ok = false;
630 return;
631 }
632
633 Geometry::wkbType outer_type = type[type.size() - 1];
634
635 type.push_back(geotype);
636 previous_type = geotype;
637
638 // The geometry type must be in the valid range (R2).
639 if (geotype < Geometry::wkb_first ||
640 geotype > Geometry::wkb_geometrycollection) {
641 is_ok = false;
642 return;
643 }
644
645 // First geometry must be of the specified type (if specified), or
646 // a subtype (R3).
647 if (type.size() == 2) {
648 if (geotype != outer_type && outer_type != Geometry::wkb_invalid_type &&
649 !is_subtype_of(geotype, outer_type))
650 is_ok = false;
651 return;
652 }
653
654 // Any type is allowed in geometry collections (R4).
655 if (outer_type == Geometry::wkb_geometrycollection) return;
656
657 switch (geotype) {
658 case Geometry::wkb_point:
659 // Points can only appear in multipoints and in linestrings (R4).
660 if (!(outer_type == Geometry::wkb_multipoint ||
661 (!has_hdr && outer_type == Geometry::wkb_linestring)))
662 is_ok = false;
663 if (outer_type == Geometry::wkb_linestring) ++points_in_linestring;
664 break;
665 case Geometry::wkb_linestring:
666 // Linestrings can only appear in multilinestrings and as rings
667 // in polygons (R4).
668 if (!(outer_type == Geometry::wkb_multilinestring ||
669 (!has_hdr && outer_type == Geometry::wkb_polygon)))
670 is_ok = false;
671 break;
672 case Geometry::wkb_polygon:
673 // Polygons can only appear in multipolygons (R4).
674 if (outer_type != Geometry::wkb_multipolygon) is_ok = false;
675 break;
676 case Geometry::wkb_multipoint:
677 case Geometry::wkb_multilinestring:
678 case Geometry::wkb_multipolygon:
679 case Geometry::wkb_geometrycollection:
680 // These are only allowed if outer_type is geometry collection,
681 // in which case they're handled before entering the switch (R4).
682 is_ok = false;
683 break;
684 default:
685 // The list of cases above should be complete (R2).
686 DBUG_ASSERT(0);
687 break;
688 }
689 }
690
on_wkb_end(const void * wkb)691 virtual void on_wkb_end(const void *wkb) {
692 if (!is_ok) return;
693
694 Geometry::wkbType current_type = type[type.size() - 1];
695 type.pop_back();
696 last_position = wkb;
697
698 switch (current_type) {
699 case Geometry::wkb_linestring:
700 // Linestrings must have at least two points. Polygon rings must
701 // have at least four points (R5, R6).
702 if (points_in_linestring < 2 ||
703 (type[type.size() - 1] == Geometry::wkb_polygon &&
704 points_in_linestring < 4))
705 is_ok = false;
706 points_in_linestring = 0;
707 break;
708 case Geometry::wkb_polygon:
709 // Polygons must have at least one ring (R7).
710 if (previous_type != Geometry::wkb_linestring) is_ok = false;
711 break;
712 case Geometry::wkb_multipoint:
713 // Multipoints must contain at least one point (R8).
714 if (previous_type != Geometry::wkb_point) is_ok = false;
715 break;
716 case Geometry::wkb_multilinestring:
717 // Multilinestrings must contain at least one linestring (R8).
718 if (previous_type != Geometry::wkb_linestring) is_ok = false;
719 break;
720 case Geometry::wkb_multipolygon:
721 // Multipolygons must contain at least one polygon (R8).
722 if (previous_type != Geometry::wkb_polygon) is_ok = false;
723 break;
724 default:
725 break;
726 }
727
728 previous_type = current_type;
729 }
730
continue_scan() const731 virtual bool continue_scan() const { return is_ok; }
732
733 /**
734 Check if the parsed WKB was well-formed, as far as this handler
735 knows.
736
737 There may be other conditions that cause the object to not be
738 well-formed.
739
740 @see Geometry::is_well_formed
741
742 @return True if the WKB was well-formed, false otherwise.
743 */
is_well_formed()744 bool is_well_formed() { return is_ok; }
745
746 /**
747 Get the position after the last parsed byte.
748
749 @return Pointer pointing to after the last parsed byte.
750 */
get_last_position()751 const char *get_last_position() {
752 return static_cast<const char *>(last_position);
753 }
754 };
755
is_well_formed(const char * from,size_t length,Geometry::wkbType type,Geometry::wkbByteOrder bo)756 bool Geometry::is_well_formed(const char *from, size_t length,
757 Geometry::wkbType type,
758 Geometry::wkbByteOrder bo) {
759 bool is_well_formed = true;
760 Geometry_well_formed_checker checker(type, bo);
761 uint32 len = length - SRID_SIZE;
762
763 if (length < GEOM_HEADER_SIZE) return false;
764
765 is_well_formed = (wkb_scanner(current_thd, from + SRID_SIZE, &len, 0, true,
766 &checker) != nullptr);
767
768 return (is_well_formed && checker.is_well_formed() &&
769 checker.get_last_position() == from + length);
770 }
771
wkb_get_double(const char * ptr,Geometry::wkbByteOrder bo)772 static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo) {
773 if (bo == Geometry::wkb_ndr)
774 return float8get(ptr);
775 else
776 return mi_float8get(pointer_cast<const uchar *>(ptr));
777 }
778
779 /**
780 Check that a pair of geographic coordinates are within the valid range.
781
782 Checks if the coordinates are within the allowed range for geographic
783 coordinates. Valid range for longitude and latitude coordinates in geographic
784 spatial reference systems are (-180, 180) and [-90, 90] degrees,
785 respectively.
786
787 @param[in] x Longitude coordinate.
788 @param[in] y Latitude coordinate.
789 @param[in] srs_angular_unit Unit to radians conversion factor.
790 @param[out] long_out_of_range Longitude is out of range.
791 @param[out] lat_out_of_range Latitude is out of range.
792 @param[out] out_of_range_value The value that is out of range.
793
794 @retval false Coordinates are within allowed range.
795 @retval true Coordinates are not within allowed range.
796 */
797
check_coordinate_range(double x,double y,double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)798 static bool check_coordinate_range(double x, double y, double srs_angular_unit,
799 bool *long_out_of_range,
800 bool *lat_out_of_range,
801 double *out_of_range_value) {
802 // Check if longitude coordinates are within allowed range.
803 if (x * srs_angular_unit <= -M_PI || x * srs_angular_unit > M_PI) {
804 *long_out_of_range = true;
805 *out_of_range_value = x;
806 return true;
807 }
808
809 // Check if latitude coordinates are within allowed range.
810 if (y * srs_angular_unit < -M_PI_2 || y * srs_angular_unit > M_PI_2) {
811 *lat_out_of_range = true;
812 *out_of_range_value = y;
813 return true;
814 }
815
816 return false;
817 }
818
wkb_get_uint(const char * ptr,Geometry::wkbByteOrder bo)819 static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo) {
820 if (bo == Geometry::wkb_ndr)
821 return uint4korr(ptr);
822 else
823 return mi_uint4korr(pointer_cast<const uchar *>(ptr));
824 }
825
826 /**
827 Scan WKB byte string and notify WKB events by calling registered callbacks.
828 @param wkb a little endian WKB byte string of 'len' bytes, with or
829 without WKB header.
830 @param[in] thd Thread context.
831 @param [in,out] len remaining number of bytes of the wkb string.
832 @param geotype the type of the geometry to be scanned.
833 @param has_hdr whether the 'wkb' point to a WKB header or right after
834 the header. If it is true, the
835 'geotype' should be the same as the type in the header;
836 otherwise, and we will use the type specified in WKB header.
837 @param handler the registered WKB_scanner_event_handler object to be notified.
838 @return the next byte after last valid geometry just scanned, or NULL on error
839 */
wkb_scanner(THD * thd,const char * wkb,uint32 * len,uint32 geotype,bool has_hdr,WKB_scanner_event_handler * handler)840 const char *wkb_scanner(THD *thd, const char *wkb, uint32 *len, uint32 geotype,
841 bool has_hdr, WKB_scanner_event_handler *handler) {
842 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr)) return nullptr;
843
844 Geometry::wkbType gt;
845 const char *q = nullptr;
846 uint32 ngeos = 0, comp_type = 0, gtype = 0;
847 bool comp_hashdr = false, done = false;
848
849 if (has_hdr) {
850 if (*len < WKB_HEADER_SIZE) return nullptr; // Invalid WKB data.
851
852 gtype = uint4korr(wkb + 1);
853 // The geotype isn't used in this case.
854 if (geotype != gtype && geotype != 0 /* unknown */) return nullptr;
855
856 if ((*wkb != Geometry::wkb_ndr && *wkb != Geometry::wkb_xdr) ||
857 gtype < Geometry::wkb_first || gtype > Geometry::wkb_last)
858 return nullptr;
859
860 gt = static_cast<Geometry::wkbType>(gtype);
861
862 q = wkb + WKB_HEADER_SIZE;
863 *len -= WKB_HEADER_SIZE;
864 if (*len <= 0) return nullptr;
865 handler->on_wkb_start(get_byte_order(wkb), gt, q, *len, true);
866 if (!handler->continue_scan()) return nullptr;
867 } else {
868 DBUG_ASSERT(geotype >= Geometry::wkb_first &&
869 geotype <= Geometry::wkb_last);
870 q = wkb;
871 gt = static_cast<Geometry::wkbType>(geotype);
872 handler->on_wkb_start(Geometry::wkb_ndr, gt, q, *len, false);
873 if (!handler->continue_scan()) return nullptr;
874 }
875
876 if (gt != Geometry::wkb_point) {
877 if (*len < 4) return nullptr;
878 ngeos = uint4korr(q);
879 q += sizeof(uint32);
880 *len -= 4;
881 }
882
883 switch (gt) {
884 case Geometry::wkb_point:
885 if (*len < POINT_DATA_SIZE) return nullptr;
886 q += POINT_DATA_SIZE;
887 *len -= POINT_DATA_SIZE;
888 done = true;
889 handler->on_wkb_end(q);
890 if (!handler->continue_scan()) return nullptr;
891 break;
892 case Geometry::wkb_linestring:
893 comp_type = Geometry::wkb_point;
894 break;
895 case Geometry::wkb_polygon:
896 comp_type = Geometry::wkb_linestring;
897 break;
898 case Geometry::wkb_multipoint:
899 comp_type = Geometry::wkb_point;
900 comp_hashdr = true;
901 break;
902 case Geometry::wkb_multilinestring:
903 comp_type = Geometry::wkb_linestring;
904 comp_hashdr = true;
905 break;
906 case Geometry::wkb_multipolygon:
907 comp_type = Geometry::wkb_polygon;
908 comp_hashdr = true;
909 break;
910 case Geometry::wkb_geometrycollection:
911 comp_hashdr = true;
912 break;
913 default:
914 DBUG_ASSERT(false);
915 break;
916 }
917
918 if (!done && q != nullptr) {
919 for (uint32 i = 0; i < ngeos; i++) {
920 q = wkb_scanner(thd, q, len, comp_type, comp_hashdr, handler);
921 if (q == nullptr) return nullptr;
922 }
923 handler->on_wkb_end(q);
924 if (!handler->continue_scan()) return nullptr;
925 }
926
927 return q;
928 }
929
930 /**
931 Read from 'wkb' (which contains WKB encoded in either endianess) the
932 geometry data, and write WKB of returned Geometry object in little endianess
933 into 'res', and also create geometry object on 'buffer' and return it.
934 The returned Geometry object points to bytes (without WKB HEADER) in 'res'.
935
936 @param thd Thread context.
937 @param buffer the place to create the returned Geometry object at.
938 @param wkb the input WKB buffer which contains WKB of either endianess.
939 @param len the number of bytes of WKB in 'wkb'.
940 @param res the buffer to write little endian WKB into.
941 @param init_stream Whether set WKB buffer pointer to returned Geometry
942 object.
943 @return the created Geometry object.
944 */
create_from_wkb(THD * thd,Geometry_buffer * buffer,const char * wkb,uint32 len,String * res,bool init_stream)945 Geometry *Geometry::create_from_wkb(THD *thd, Geometry_buffer *buffer,
946 const char *wkb, uint32 len, String *res,
947 bool init_stream) {
948 uint32 geom_type;
949 Geometry *geom;
950
951 if (len < WKB_HEADER_SIZE) return nullptr;
952
953 geom_type = wkb_get_uint(wkb + 1, ::get_byte_order(wkb));
954 if ((*wkb != wkb_xdr && *wkb != wkb_ndr) || geom_type < wkb_first ||
955 geom_type > wkb_last ||
956 !(geom = create_by_typeid(buffer, (int)geom_type)) ||
957 res->reserve(WKB_HEADER_SIZE, 512))
958 return nullptr;
959
960 q_append((char)wkb_ndr, res);
961 q_append(geom_type, res);
962
963 uint tret =
964 geom->init_from_wkb(thd, wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE,
965 ::get_byte_order(wkb), res);
966
967 // The WKB string is invalid if it has trailing trash bytes.
968 if (tret != len - WKB_HEADER_SIZE) return nullptr;
969
970 if (init_stream)
971 geom->set_data_ptr(res->ptr() + WKB_HEADER_SIZE,
972 res->length() - WKB_HEADER_SIZE);
973 geom->has_geom_header_space(true);
974 if (geom->get_geotype() == wkb_polygon) geom->polygon_is_wkb_form(true);
975 return tret ? geom : nullptr;
976 }
977
envelope(MBR * mbr) const978 bool Geometry::envelope(MBR *mbr) const {
979 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
980
981 if (get_mbr(mbr, &wkb)) return true;
982
983 return false;
984 }
985
986 class GeomColl_component_counter : public WKB_scanner_event_handler {
987 public:
988 size_t num;
989
GeomColl_component_counter()990 GeomColl_component_counter() : num(0) {}
991
on_wkb_start(Geometry::wkbByteOrder,Geometry::wkbType geotype,const void *,uint32,bool)992 virtual void on_wkb_start(Geometry::wkbByteOrder, Geometry::wkbType geotype,
993 const void *, uint32, bool) {
994 if (geotype != Geometry::wkb_geometrycollection) num++;
995 }
996
on_wkb_end(const void *)997 virtual void on_wkb_end(const void *) {}
998 };
999
envelope(String * result) const1000 bool Geometry::envelope(String *result) const {
1001 MBR mbr;
1002 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1003
1004 if (result->reserve(1 + 4 * 3 + SIZEOF_STORED_DOUBLE * 10)) return true;
1005
1006 if (get_mbr(&mbr, &wkb)) {
1007 /*
1008 The geometry has no effective components in this branch, which is
1009 impossible for geometries other than geometry collections(GC).
1010 A GC may have empty nested GCs.
1011 */
1012 if (get_type() != wkb_geometrycollection) return true;
1013
1014 uint32 num = uint4korr(get_cptr());
1015 if (num != 0) {
1016 GeomColl_component_counter counter;
1017 uint32 wkb_len = get_data_size();
1018
1019 wkb_scanner(current_thd, get_cptr(), &wkb_len,
1020 Geometry::wkb_geometrycollection, false, &counter);
1021 // Non-empty nested geometry collections.
1022 if (counter.num > 0) return true;
1023 }
1024
1025 // An empty geometry collection's envelope is an empty geometry.
1026 write_wkb_header(result, wkb_geometrycollection, 0);
1027 return false;
1028 }
1029
1030 q_append(static_cast<char>(wkb_ndr), result);
1031
1032 int dim = mbr.dimension();
1033 if (dim < 0) return true;
1034
1035 uint32 num_elems, num_elems2;
1036
1037 if (dim == 0) {
1038 q_append(static_cast<uint32>(wkb_point), result);
1039 q_append(mbr.xmin, result);
1040 q_append(mbr.ymin, result);
1041 } else if (dim == 1) {
1042 q_append(static_cast<uint32>(wkb_linestring), result);
1043 num_elems = 2;
1044 q_append(num_elems, result);
1045 q_append(mbr.xmin, result);
1046 q_append(mbr.ymin, result);
1047 q_append(mbr.xmax, result);
1048 q_append(mbr.ymax, result);
1049 } else {
1050 q_append(static_cast<uint32>(wkb_polygon), result);
1051 num_elems = 1;
1052 q_append(num_elems, result);
1053 num_elems2 = 5;
1054 q_append(num_elems2, result);
1055 q_append(mbr.xmin, result);
1056 q_append(mbr.ymin, result);
1057 q_append(mbr.xmax, result);
1058 q_append(mbr.ymin, result);
1059 q_append(mbr.xmax, result);
1060 q_append(mbr.ymax, result);
1061 q_append(mbr.xmin, result);
1062 q_append(mbr.ymax, result);
1063 q_append(mbr.xmin, result);
1064 q_append(mbr.ymin, result);
1065 }
1066 return false;
1067 }
1068
1069 /**
1070 Create a point from data.
1071
1072 @param [out] result Put result here
1073 @param wkb Data for point is here.
1074
1075 @return false on success, true on error
1076 */
1077
create_point(String * result,wkb_parser * wkb) const1078 bool Geometry::create_point(String *result, wkb_parser *wkb) const {
1079 if (wkb->no_data(POINT_DATA_SIZE) ||
1080 result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE, 32))
1081 return true;
1082 q_append((char)wkb_ndr, result);
1083 q_append((uint32)wkb_point, result);
1084 /* Copy two double in same format */
1085 q_append(wkb->data(), POINT_DATA_SIZE, result);
1086 return false;
1087 }
1088
1089 /**
1090 Create a point from coordinates.
1091
1092 @param [out] result The resulting point
1093 @param p coordinates for point
1094
1095 @return false on success, true on error
1096 */
1097
create_point(String * result,point_xy p) const1098 bool Geometry::create_point(String *result, point_xy p) const {
1099 if (result->reserve(1 + 4 + POINT_DATA_SIZE, 32)) return true;
1100
1101 q_append((char)wkb_ndr, result);
1102 q_append((uint32)wkb_point, result);
1103 q_append(p.x, result);
1104 q_append(p.y, result);
1105 return false;
1106 }
1107
1108 /**
1109 Append N points from packed format to text
1110 Before calling this function, caller must have already checked that wkb's
1111 buffer is complete and not truncated.
1112
1113 @param [out] txt Append points here
1114 @param n_points Number of points
1115 @param wkb Packed data
1116 @param offset Offset between points
1117 @param bracket_pt whether to bracket the point coordinate with (),
1118 multipoint need so.
1119 */
1120
append_points(String * txt,uint32 n_points,wkb_parser * wkb,uint32 offset,bool bracket_pt) const1121 void Geometry::append_points(String *txt, uint32 n_points, wkb_parser *wkb,
1122 uint32 offset, bool bracket_pt) const {
1123 DBUG_ASSERT(0.0 == 0 && 0 == -0 && -0.0 == 0.0);
1124
1125 while (n_points--) {
1126 point_xy p;
1127 wkb->skip_unsafe(offset);
1128 wkb->scan_xy_unsafe(&p);
1129 txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 1);
1130 if (bracket_pt) qs_append('(', txt);
1131 qs_append(p.x, MAX_DIGITS_IN_DOUBLE, txt);
1132 qs_append(' ', txt);
1133 qs_append(p.y, MAX_DIGITS_IN_DOUBLE, txt);
1134 if (bracket_pt) qs_append(')', txt);
1135 qs_append(',', txt);
1136 }
1137 }
1138
1139 /**
1140 Get most bounding rectangle (mbr) for X points
1141
1142 @param [out] mbr Result
1143 @param wkb Data for point is here.
1144 @param offset Offset between points
1145
1146 @return false on success, true on error
1147 */
1148
get_mbr_for_points(MBR * mbr,wkb_parser * wkb,uint offset) const1149 bool Geometry::get_mbr_for_points(MBR *mbr, wkb_parser *wkb,
1150 uint offset) const {
1151 uint32 n_points;
1152
1153 if (wkb->scan_n_points_and_check_data(&n_points, offset)) return true;
1154
1155 /* Calculate MBR for points */
1156 while (n_points--) {
1157 wkb->skip_unsafe(offset);
1158
1159 point_xy p;
1160 wkb->scan_xy_unsafe(&p);
1161 if (!std::isfinite(p.x) || !std::isfinite(p.y)) return true;
1162 mbr->add_xy(p);
1163 }
1164 return false;
1165 }
1166
Geometry(const Geometry & geo)1167 Geometry::Geometry(const Geometry &geo) {
1168 #if !defined(DBUG_OFF)
1169 wkbType geotype = geo.get_geotype();
1170 #endif
1171 DBUG_ASSERT(is_valid_geotype(geotype) &&
1172 ((geo.get_ptr() != nullptr && geo.get_nbytes() > 0) ||
1173 (geo.get_ptr() == nullptr && geo.get_nbytes() == 0) ||
1174 (geo.get_geotype() == wkb_polygon && geo.get_nbytes() == 0)));
1175
1176 m_ptr = geo.m_ptr;
1177 m_flags = geo.m_flags;
1178 m_owner = geo.m_owner;
1179 set_srid(geo.get_srid());
1180 }
1181
~Geometry()1182 Geometry::~Geometry() {
1183 /*
1184 Make sure no exceptions can be thrown in destructors of Geometry classes,
1185 by asserting in debug builds, so that future code won't accidentally throw.
1186
1187 If an exception is thrown when we destroy a geometry object G,
1188 although the exception will still be caught and converted to MySQL
1189 error report, the geometries that are in the same container as G and
1190 that are placed after G will not be properly destroyed. This is the
1191 problem we want to address/avoid by forbiding throwing exceptions in
1192 destructors of Geometry classes.
1193
1194 Since DBUG_ASSERT only works when DBUG_OFF is not defined, the
1195 try/catch is only enabled here depending on the same condition, so that
1196 in release builds we don't have the overhead of the try-catch statement.
1197
1198 This is true also for destructors of children classes of Geometry.
1199 */
1200 #if !defined(DBUG_OFF)
1201 try {
1202 #endif
1203 if (!is_bg_adapter()) return;
1204
1205 Geometry::wkbType gt = get_geotype();
1206
1207 if (gt != Geometry::wkb_polygon) {
1208 if (get_ownmem() && m_ptr) {
1209 set_ownmem(false);
1210 gis_wkb_free(m_ptr);
1211 m_ptr = nullptr;
1212 }
1213
1214 set_nbytes(0);
1215 }
1216
1217 donate_data();
1218
1219 #if !defined(DBUG_OFF)
1220 } catch (...) {
1221 // Should never throw exceptions in destructor.
1222 DBUG_ASSERT(false);
1223 }
1224 #endif
1225 }
1226
1227 /**
1228 Assignment operator for Geometry class, assignment operators of children
1229 classes calls this to do general assignment.
1230 */
operator =(const Geometry & rhs)1231 Geometry &Geometry::operator=(const Geometry &rhs) {
1232 if (this == &rhs) return *this;
1233
1234 #if !defined(DBUG_OFF)
1235 Geometry::wkbType geotype = rhs.get_geotype();
1236 #endif
1237 DBUG_ASSERT((is_bg_adapter() || rhs.is_bg_adapter()) &&
1238 m_flags.geotype == rhs.m_flags.geotype &&
1239 is_valid_geotype(geotype));
1240
1241 set_bg_adapter(true);
1242
1243 /*
1244 Update the mutable state of rhs.
1245 */
1246 rhs.set_bg_adapter(true);
1247 set_srid(rhs.get_srid());
1248 // Don't set_flags, it's done in operator= of children classes.
1249
1250 return *this;
1251 }
1252
1253 /***************************** Point *******************************/
1254
Gis_point(const self & pt)1255 Gis_point::Gis_point(const self &pt) : Geometry(pt) {
1256 size_t nbytes = get_nbytes();
1257 DBUG_ASSERT((nbytes == SIZEOF_STORED_DOUBLE * GEOM_DIM || nbytes == 0));
1258 if (nbytes == 0) {
1259 DBUG_ASSERT(get_ownmem() == false);
1260 // Allocate even if pt isn't initialized with proper value, this is
1261 // required behavior from Boost Geometry.
1262 nbytes = SIZEOF_STORED_DOUBLE * GEOM_DIM;
1263 set_nbytes(nbytes);
1264 }
1265
1266 m_ptr = gis_wkb_fixed_alloc(nbytes);
1267 if (m_ptr == nullptr) {
1268 set_nbytes(0);
1269 set_ownmem(false);
1270 return;
1271 }
1272
1273 if (pt.get_nbytes() > 0)
1274 memcpy(m_ptr, pt.get_ptr(), pt.get_nbytes());
1275 else
1276 memset(m_ptr, 0, nbytes);
1277 set_ownmem(true);
1278 }
1279
1280 /**
1281 Deep assignment from point 'p' to this object.
1282 @param rhs the Gis_point to duplicate from.
1283 */
operator =(const Gis_point & rhs)1284 Gis_point &Gis_point::operator=(const Gis_point &rhs) {
1285 if (this == &rhs) return *this;
1286 Geometry::operator=(rhs);
1287
1288 // This point may or may not have own memory. we allow this because in bg,
1289 // std::reverse is called to reverse a linestring/ring, and also,
1290 // points are of equal size. Not allowed on any other type of geometries.
1291 DBUG_ASSERT(
1292 (m_ptr != nullptr && get_nbytes() == SIZEOF_STORED_DOUBLE * GEOM_DIM) ||
1293 (m_ptr == nullptr && get_nbytes() == 0 && !get_ownmem()));
1294
1295 DBUG_ASSERT(
1296 (rhs.get_ptr() != nullptr &&
1297 rhs.get_nbytes() == SIZEOF_STORED_DOUBLE * GEOM_DIM) ||
1298 (rhs.get_ptr() == nullptr && rhs.get_nbytes() == 0 && !rhs.get_ownmem()));
1299
1300 if (m_owner == nullptr) m_owner = rhs.get_owner();
1301
1302 size_t plen = rhs.get_nbytes();
1303
1304 // May or may not have own memory. We allow assignment to a Gis_point
1305 // owning no memory because in BG, std::reverse is called to reverse a
1306 // linestring/ring, and also, points are of equal size.
1307 // Not allowed on any other type of geometries.
1308 if (m_ptr == nullptr) {
1309 set_nbytes(SIZEOF_STORED_DOUBLE * GEOM_DIM);
1310 set_ownmem(true);
1311 m_ptr = gis_wkb_fixed_alloc(get_nbytes());
1312 if (m_ptr == nullptr) {
1313 set_nbytes(0);
1314 set_ownmem(false);
1315 return *this;
1316 }
1317 }
1318
1319 /*
1320 Boost Geometry may use a point that is only default constructed that has
1321 no meaningful value, and in such a case the default value are all zeros.
1322 */
1323 if (plen > 0)
1324 memcpy(m_ptr, rhs.get_ptr(), plen);
1325 else
1326 memset(m_ptr, 0, get_nbytes());
1327
1328 return *this;
1329 }
1330
1331 /**
1332 Shallow assignment, let this point object refer to the specified memory
1333 address as its WKB data, and this object never owns the memory assigned.
1334 @param ptr WKB data address for the point.
1335 @param len WKB data length for the point.
1336 */
set_ptr(void * ptr,size_t len)1337 void Gis_point::set_ptr(void *ptr, size_t len) {
1338 set_bg_adapter(true);
1339 if (m_ptr && get_ownmem()) {
1340 DBUG_ASSERT(get_nbytes() == SIZEOF_STORED_DOUBLE * GEOM_DIM);
1341 gis_wkb_free(m_ptr);
1342 }
1343 m_ptr = ptr;
1344 set_nbytes(len);
1345 set_ownmem(false);
1346 DBUG_ASSERT(
1347 (m_ptr != nullptr && get_nbytes() == SIZEOF_STORED_DOUBLE * GEOM_DIM) ||
1348 (m_ptr == nullptr && get_nbytes() == 0));
1349 }
1350
get_data_size() const1351 uint32 Gis_point::get_data_size() const {
1352 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
1353 return GET_SIZE_ERROR;
1354 if (get_nbytes() != POINT_DATA_SIZE) return GET_SIZE_ERROR;
1355
1356 return POINT_DATA_SIZE;
1357 }
1358
init_from_wkt(Gis_read_stream * trs,String * wkb,const bool parens)1359 bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb,
1360 const bool parens) {
1361 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
1362
1363 double x, y;
1364 if ((parens && trs->check_next_symbol('(')) || trs->get_next_number(&x) ||
1365 trs->get_next_number(&y) || wkb->reserve(POINT_DATA_SIZE, 256) ||
1366 (parens && trs->check_next_symbol(')')))
1367 return true;
1368 q_append(x, wkb);
1369 q_append(y, wkb);
1370 return false;
1371 }
1372
1373 /*
1374 Initialize point using WKB data, the WKB data may come from user input or
1375 internally table-stored geometry, and user's WKB data may be either endian.
1376 The WKB data may be of either little endian or big endian, thus we need to
1377 read them using endian-ness aware functions wkb_get_uint, wkb_get_double,
1378 and store them as portable (little endian) format into res.
1379 Only xxx_from_wkb SQL functions can see big endian WKB data, all other
1380 function Items see portable little endian WKB data.
1381 This is true for all the init_from_wkb functions of all Geometry classes.
1382 */
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)1383 uint Gis_point::init_from_wkb(THD *thd, const char *wkb, uint len,
1384 wkbByteOrder bo, String *res) {
1385 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
1386 double x, y;
1387 if (len < POINT_DATA_SIZE || res->reserve(POINT_DATA_SIZE, 256)) return 0;
1388 x = wkb_get_double(wkb, bo);
1389 y = wkb_get_double(wkb + SIZEOF_STORED_DOUBLE, bo);
1390 q_append(x, res);
1391 q_append(y, res);
1392 return POINT_DATA_SIZE;
1393 }
1394
get_data_as_wkt(String * txt,wkb_parser * wkb) const1395 bool Gis_point::get_data_as_wkt(String *txt, wkb_parser *wkb) const {
1396 point_xy p;
1397 if (wkb->scan_xy(&p)) return true;
1398 if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 3)) return true;
1399 if (!std::isfinite(p.x) || !std::isfinite(p.y)) return true;
1400 qs_append('(', txt);
1401 qs_append(p.x, MAX_DIGITS_IN_DOUBLE, txt);
1402 qs_append(' ', txt);
1403 qs_append(p.y, MAX_DIGITS_IN_DOUBLE, txt);
1404 qs_append(')', txt);
1405 return false;
1406 }
1407
get_mbr(MBR * mbr,wkb_parser * wkb) const1408 bool Gis_point::get_mbr(MBR *mbr, wkb_parser *wkb) const {
1409 point_xy p;
1410 if (wkb->scan_xy(&p)) return true;
1411 if (!std::isfinite(p.x) || !std::isfinite(p.y)) return true;
1412 mbr->add_xy(p);
1413 return false;
1414 }
1415
reverse_coordinates()1416 bool Gis_point::reverse_coordinates() {
1417 double x;
1418 double y;
1419
1420 if (get_x(&x) || get_y(&y)) {
1421 return true;
1422 }
1423
1424 float8store(get_cptr(), y);
1425 float8store((get_cptr() + SIZEOF_STORED_DOUBLE), x);
1426
1427 return false;
1428 }
1429
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)1430 bool Gis_point::validate_coordinate_range(double srs_angular_unit,
1431 bool *long_out_of_range,
1432 bool *lat_out_of_range,
1433 double *out_of_range_value) {
1434 double x;
1435 double y;
1436
1437 *long_out_of_range = false;
1438 *lat_out_of_range = false;
1439
1440 if (get_x(&x) || get_y(&y)) {
1441 return true; /* purecov: inspected */
1442 }
1443
1444 return check_coordinate_range(x, y, srs_angular_unit, long_out_of_range,
1445 lat_out_of_range, out_of_range_value);
1446 }
1447
get_class_info() const1448 const Geometry::Class_info *Gis_point::get_class_info() const {
1449 return &point_class;
1450 }
1451
1452 /***************************** LineString *******************************/
get_data_size() const1453 uint32 Gis_line_string::get_data_size() const {
1454 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
1455 return GET_SIZE_ERROR;
1456
1457 if (is_length_verified()) return static_cast<uint32>(get_nbytes());
1458
1459 uint32 n_points;
1460 uint32 len;
1461 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1462 if (wkb.scan_n_points_and_check_data(&n_points)) return GET_SIZE_ERROR;
1463
1464 len = 4 + n_points * POINT_DATA_SIZE;
1465 if (len != get_nbytes()) set_nbytes(len);
1466 set_length_verified(true);
1467 return len;
1468 }
1469
1470 // Helper function to get coordinate value from a linestring of WKB format.
coord_val(const char * p,int i,int x)1471 inline double coord_val(const char *p, int i, int x) {
1472 return float8get(p + i * POINT_DATA_SIZE + (x ? SIZEOF_STORED_DOUBLE : 0));
1473 }
1474
init_from_wkt(Gis_read_stream * trs,String * wkb)1475 bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) {
1476 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
1477
1478 uint32 n_points = 0;
1479 uint32 np_pos = wkb->length();
1480 Gis_point p(false);
1481
1482 if (trs->check_next_symbol('(')) return true;
1483 if (wkb->reserve(4, 512)) return true;
1484 wkb->length(wkb->length() + 4); // Reserve space for points
1485
1486 for (;;) {
1487 if (p.init_from_wkt(trs, wkb, false)) return true;
1488 n_points++;
1489 if (trs->skip_char(',')) // Didn't find ','
1490 break;
1491 }
1492
1493 if (n_points < 2) {
1494 trs->set_error_msg("Too few points in LINESTRING");
1495 return true;
1496 }
1497
1498 const char *firstpt = nullptr, *lastpt = nullptr;
1499 if (!is_polygon_ring()) goto out;
1500
1501 // Make sure all rings of a polygon are closed, and a ring must have
1502 // at least 4 points.
1503 firstpt = wkb->ptr() + np_pos + 4;
1504 lastpt = wkb->ptr() + wkb->length() - POINT_DATA_SIZE;
1505
1506 if (n_points < 4 || memcmp(lastpt, firstpt, POINT_DATA_SIZE)) return true;
1507
1508 DBUG_ASSERT(n_points == (lastpt - firstpt) / POINT_DATA_SIZE + 1);
1509
1510 out:
1511
1512 write_at_position(np_pos, n_points, wkb);
1513 if (trs->check_next_symbol(')')) return true;
1514 return false;
1515 }
1516
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)1517 uint Gis_line_string::init_from_wkb(THD *thd, const char *wkb, uint len,
1518 wkbByteOrder bo, String *res) {
1519 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
1520
1521 uint32 n_points, proper_length;
1522 const char *wkb_end;
1523 Gis_point p(false);
1524
1525 if (len < 4 || (n_points = wkb_get_uint(wkb, bo)) < 2 ||
1526 (is_polygon_ring() && n_points < 4) || n_points > max_n_points)
1527 return 0;
1528 proper_length = 4 + n_points * POINT_DATA_SIZE;
1529 wkb_end = wkb + proper_length;
1530
1531 if (len < proper_length) return 0;
1532
1533 // Make sure all rings of a polygon are closed.
1534 if (is_polygon_ring() &&
1535 memcmp(wkb + 4, wkb_end - POINT_DATA_SIZE, POINT_DATA_SIZE))
1536 return 0;
1537
1538 if (res->reserve(proper_length, 512)) return 0;
1539
1540 q_append(n_points, res);
1541 for (wkb += 4; wkb < wkb_end; wkb += POINT_DATA_SIZE) {
1542 if (!p.init_from_wkb(thd, wkb, POINT_DATA_SIZE, bo, res)) return 0;
1543 }
1544
1545 return proper_length;
1546 }
1547
get_data_as_wkt(String * txt,wkb_parser * wkb) const1548 bool Gis_line_string::get_data_as_wkt(String *txt, wkb_parser *wkb) const {
1549 uint32 n_points;
1550 if (wkb->scan_n_points_and_check_data(&n_points) ||
1551 txt->reserve(1 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
1552 return true;
1553
1554 qs_append('(', txt);
1555 while (n_points--) {
1556 point_xy p;
1557 wkb->scan_xy_unsafe(&p);
1558 if (!std::isfinite(p.x) || !std::isfinite(p.y)) return true;
1559 qs_append(p.x, MAX_DIGITS_IN_DOUBLE, txt);
1560 qs_append(' ', txt);
1561 qs_append(p.y, MAX_DIGITS_IN_DOUBLE, txt);
1562 qs_append(',', txt);
1563 }
1564 txt->length(txt->length() - 1); // Remove end ','
1565 qs_append(')', txt);
1566 return false;
1567 }
1568
get_mbr(MBR * mbr,wkb_parser * wkb) const1569 bool Gis_line_string::get_mbr(MBR *mbr, wkb_parser *wkb) const {
1570 return get_mbr_for_points(mbr, wkb, 0);
1571 }
1572
geom_length(double * len) const1573 int Gis_line_string::geom_length(double *len) const {
1574 uint32 n_points;
1575 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1576
1577 *len = 0; // In case of errors
1578 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
1579
1580 point_xy prev;
1581 wkb.scan_xy_unsafe(&prev);
1582 while (--n_points) {
1583 point_xy p;
1584 wkb.scan_xy_unsafe(&p);
1585 *len += prev.distance(p);
1586 if (!std::isfinite(*len)) return 1;
1587 prev = p;
1588 }
1589 return 0;
1590 }
1591
is_closed(int * closed) const1592 int Gis_line_string::is_closed(int *closed) const {
1593 uint32 n_points;
1594 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1595
1596 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
1597
1598 if (n_points == 1) {
1599 *closed = 1;
1600 return 0;
1601 }
1602
1603 point_xy p1, p2;
1604
1605 /* Get first point. */
1606 wkb.scan_xy_unsafe(&p1);
1607
1608 /* Get last point. */
1609 wkb.skip_unsafe((n_points - 2) * POINT_DATA_SIZE);
1610 wkb.scan_xy_unsafe(&p2);
1611
1612 *closed = p1.eq(p2);
1613 return 0;
1614 }
1615
num_points(uint32 * n_points) const1616 int Gis_line_string::num_points(uint32 *n_points) const {
1617 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1618 return wkb.scan_uint4(n_points) ? 1 : 0;
1619 }
1620
start_point(String * result) const1621 int Gis_line_string::start_point(String *result) const {
1622 uint32 n_points;
1623 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1624 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
1625 return create_point(result, &wkb);
1626 }
1627
end_point(String * result) const1628 int Gis_line_string::end_point(String *result) const {
1629 uint32 n_points;
1630 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1631 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
1632 wkb.skip_unsafe((n_points - 1) * POINT_DATA_SIZE);
1633 return create_point(result, &wkb);
1634 }
1635
point_n(uint32 num,String * result) const1636 int Gis_line_string::point_n(uint32 num, String *result) const {
1637 uint32 n_points;
1638 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
1639 if (num < 1 || wkb.scan_n_points_and_check_data(&n_points) || num > n_points)
1640 return 1;
1641 wkb.skip_unsafe((num - 1) * POINT_DATA_SIZE);
1642 return create_point(result, &wkb);
1643 }
1644
reverse_coordinates()1645 bool Gis_line_string::reverse_coordinates() {
1646 uint32 num_of_points;
1647
1648 if (num_points(&num_of_points)) {
1649 return true;
1650 }
1651
1652 for (uint32 i = 0; i < num_of_points; i++) {
1653 // +4 in below functions to skip numPoints field.
1654 double x = float8get(get_cptr() + 4 + i * POINT_DATA_SIZE);
1655 double y =
1656 float8get(get_cptr() + 4 + i * POINT_DATA_SIZE + SIZEOF_STORED_DOUBLE);
1657
1658 float8store(get_cptr() + 4 + i * POINT_DATA_SIZE, y);
1659 float8store(get_cptr() + 4 + i * POINT_DATA_SIZE + SIZEOF_STORED_DOUBLE, x);
1660 }
1661
1662 return false;
1663 }
1664
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)1665 bool Gis_line_string::validate_coordinate_range(double srs_angular_unit,
1666 bool *long_out_of_range,
1667 bool *lat_out_of_range,
1668 double *out_of_range_value) {
1669 uint32 num_of_points;
1670 *long_out_of_range = false;
1671 *lat_out_of_range = false;
1672
1673 if (num_points(&num_of_points)) {
1674 return true; /* purecov: inspected */
1675 }
1676
1677 for (uint32 i = 0; i < num_of_points; i++) {
1678 // +4 in below functions to skip numPoints field.
1679 double x = float8get(get_cptr() + 4 + i * POINT_DATA_SIZE);
1680 double y =
1681 float8get(get_cptr() + 4 + i * POINT_DATA_SIZE + SIZEOF_STORED_DOUBLE);
1682
1683 if (check_coordinate_range(x, y, srs_angular_unit, long_out_of_range,
1684 lat_out_of_range, out_of_range_value)) {
1685 return true;
1686 }
1687 }
1688
1689 return false;
1690 }
1691
get_class_info() const1692 const Geometry::Class_info *Gis_line_string::get_class_info() const {
1693 return &linestring_class;
1694 }
1695
1696 /***************************** Polygon *******************************/
1697 /**
1698 Copy constructor.
1699 Coordinate type, closed-ness and direction will never change.
1700 @param r another polygon of same coordinate type, ring closed-ness and
1701 ring direction.
1702 */
Gis_polygon(const self & r)1703 Gis_polygon::Gis_polygon(const self &r) : Geometry(r), m_inn_rings(nullptr) {
1704 Gis_polygon::ring_type *r_out = nullptr, *outer = nullptr;
1705 Gis_polygon::inner_container_type *r_inners = nullptr, *inners = nullptr;
1706
1707 if (r.is_bg_adapter() == false || r.get_ptr() == nullptr) return;
1708
1709 std::unique_ptr<Gis_polygon::ring_type> guard1;
1710 std::unique_ptr<Gis_polygon::inner_container_type> guard2;
1711
1712 if (r.get_ptr()) {
1713 r_out = outer_ring(&r);
1714 outer = new Gis_polygon::ring_type(*r_out);
1715 guard1.reset(outer);
1716 outer->set_owner(this);
1717 m_ptr = outer;
1718 }
1719
1720 if (r.inner_rings()) {
1721 r_inners = r.inner_rings();
1722 inners = new Gis_polygon::inner_container_type(*r_inners);
1723 guard2.reset(inners);
1724 inners->set_owner(this);
1725 set_inner_rings(inners);
1726 }
1727
1728 set_ownmem(false);
1729 set_bg_adapter(true);
1730 guard1.release();
1731 guard2.release();
1732 }
1733
Gis_polygon(const void * wkb,size_t nbytes,const Flags_t & flags,gis::srid_t srid)1734 Gis_polygon::Gis_polygon(const void *wkb, size_t nbytes, const Flags_t &flags,
1735 gis::srid_t srid)
1736 : Geometry(nullptr, nbytes, flags, srid) {
1737 set_geotype(wkb_polygon);
1738 // Make use of Gis_wkb_vector::parse_wkb_data.
1739 inner_container_type v(wkb, nbytes, get_flags(), srid);
1740 m_ptr = v.get_ptr();
1741 m_inn_rings = reinterpret_cast<inner_container_type *>(v.get_geo_vect());
1742 set_ownmem(false);
1743 if (m_ptr) outer_ring(this)->set_owner(this);
1744 if (m_inn_rings) m_inn_rings->set_owner(this);
1745
1746 set_bg_adapter(true);
1747 v.donate_data();
1748 }
1749
~Gis_polygon()1750 Gis_polygon::~Gis_polygon() {
1751 /* See ~Geometry() for why we do try-catch like this. */
1752 #if !defined(DBUG_OFF)
1753 try {
1754 #endif
1755 if (!is_bg_adapter() && !get_ownmem()) return;
1756
1757 if (m_ptr) {
1758 if (polygon_is_wkb_form()) {
1759 if (get_ownmem()) gis_wkb_free(m_ptr);
1760 } else
1761 delete outer_ring(this);
1762 m_ptr = nullptr;
1763 }
1764 if (m_inn_rings) {
1765 delete m_inn_rings;
1766 m_inn_rings = nullptr;
1767 }
1768 /*
1769 Never need to free polygon's wkb memory here, because if it's one chunk
1770 given to us, we don't own it; otherwise the two pieces are already freed
1771 above.
1772 */
1773 #if !defined(DBUG_OFF)
1774 } catch (...) {
1775 // Should never throw exceptions in destructor.
1776 DBUG_ASSERT(false);
1777 }
1778 #endif
1779 }
1780
1781 /**
1782 Deep assignment from polygon 'o' to this object.
1783 @param rhs the Gis_polygon instance to duplicate from.
1784 */
operator =(const Gis_polygon & rhs)1785 Gis_polygon &Gis_polygon::operator=(const Gis_polygon &rhs) {
1786 if (this == &rhs || !is_bg_adapter() || !rhs.is_bg_adapter()) return *this;
1787 Geometry::operator=(rhs);
1788
1789 this->set_flags(rhs.get_flags());
1790 if (this->m_owner == nullptr) this->m_owner = rhs.get_owner();
1791
1792 if (m_ptr) delete outer_ring(this);
1793 if (m_inn_rings) delete m_inn_rings;
1794 m_ptr = nullptr;
1795 m_inn_rings = nullptr;
1796
1797 if (rhs.get_ptr()) {
1798 Gis_polygon::ring_type *outer =
1799 new Gis_polygon::ring_type(*outer_ring(&rhs));
1800 outer->set_owner(this);
1801 m_ptr = outer;
1802 }
1803
1804 if (rhs.inner_rings()) {
1805 Gis_polygon::inner_container_type *inners =
1806 new Gis_polygon::inner_container_type(*rhs.inner_rings());
1807 inners->set_owner(this);
1808 set_inner_rings(inners);
1809 }
1810
1811 return *this;
1812 }
1813
1814 /**
1815 Set WKB data to this object, the WKB data will be used read only.
1816 @param ptr WKB data pointer.
1817 @param len WKB data number of bytes.
1818 */
set_ptr(void * ptr,size_t len)1819 void Gis_polygon::set_ptr(void *ptr, size_t len) {
1820 set_bg_adapter(true);
1821 ring_type *outer = outer_ring(this);
1822
1823 if (outer) delete outer;
1824 if (m_inn_rings) delete m_inn_rings;
1825
1826 set_nbytes(len);
1827
1828 Gis_wkb_vector<ring_type> plgn(ptr, len, get_flags(), get_srid());
1829 m_ptr = plgn.get_ptr();
1830 m_inn_rings = reinterpret_cast<inner_container_type *>(plgn.get_geo_vect());
1831
1832 outer = outer_ring(this);
1833 if (outer != nullptr) outer->set_owner(this);
1834 if (m_inn_rings != nullptr) m_inn_rings->set_owner(this);
1835 // Prevent destructor deleting m_ptr/m_inn_rings.
1836 plgn.donate_data();
1837 }
1838
1839 /**
1840 Make the polygon's data in a single buffer as WKB format. This polygon
1841 must be one for BG use before this call, and after this call it can
1842 never be passed into BG functions directly after this call, and it
1843 is suitable as a Gis_polygon for MySQL GIS code, because it's exactly the
1844 same as a Gis_polygon object returned by Geometry::create_from_wkt/wkb.
1845 */
to_wkb_unparsed()1846 void Gis_polygon::to_wkb_unparsed() {
1847 DBUG_ASSERT(polygon_is_wkb_form() == false && is_bg_adapter());
1848
1849 size_t nbytes = 0;
1850 void *ptr = get_packed_ptr(this, &nbytes);
1851 delete outer_ring(this);
1852 delete m_inn_rings;
1853 m_ptr = ptr;
1854 set_nbytes(nbytes);
1855 m_inn_rings = nullptr;
1856 polygon_is_wkb_form(true);
1857 set_bg_adapter(false);
1858 set_ownmem(true);
1859 }
1860
1861 /**
1862 Set the specified ring to counter-clockwise(CCW) or clockwise(CW) if it's not.
1863 Assuems the ring is closed, i.e. the 1st point is the same as the last one.
1864 Allow duplicate vertices at any position, even rings degraded to a point;
1865 Works for convex and concave rings; Can detect those with spikes and
1866 reject them.
1867
1868 @param want_ccw whether want CCW ring(true) or CW ring(false).
1869 @return false if successful, true if got error -- invalid geometry data.
1870 */
set_ring_order(bool want_ccw)1871 bool Gis_polygon_ring::set_ring_order(bool want_ccw) {
1872 DBUG_ASSERT(is_bg_adapter());
1873 Gis_polygon_ring &ring = *this;
1874 double x1, x2, y1, y2, minx = DBL_MAX, miny = DBL_MAX;
1875 size_t min_i = 0, prev_i, post_i, rsz = ring.size();
1876
1877 static_assert(sizeof(double) == POINT_DATA_SIZE / 2 &&
1878 sizeof(double) == SIZEOF_STORED_DOUBLE,
1879 "");
1880
1881 /*
1882 User input WKT/WKB may contain invalid geometry data that has less
1883 than 3 points in a polygon ring, so we should return error in this case.
1884 */
1885 if (rsz < 3) return true;
1886
1887 for (size_t i = 0; i < rsz; i++) {
1888 x1 = ring[i].get<0>();
1889 y1 = ring[i].get<1>();
1890
1891 if (i == 0) {
1892 minx = x1;
1893 miny = y1;
1894 continue;
1895 }
1896
1897 if (x1 < minx) {
1898 minx = x1;
1899 miny = y1;
1900 min_i = i;
1901 } else if (x1 == minx) {
1902 if (y1 < miny) {
1903 miny = y1;
1904 min_i = i;
1905 }
1906 }
1907 }
1908
1909 prev_i = min_i - 1;
1910 post_i = min_i + 1;
1911
1912 if (min_i == 0) {
1913 // 1st pt and last pt is the same pt, i.e. a closed polygon data, we
1914 // shouldn't use the last pt as prev_i in this case otherwise we will
1915 // get 0 sign.
1916 if (ring[0].get<0>() == ring[rsz - 1].get<0>() &&
1917 ring[0].get<1>() == ring[rsz - 1].get<1>()) {
1918 prev_i = rsz - 2;
1919 /*
1920 Survive from continuous duplicates before prev_i points.
1921 */
1922 while (ring[prev_i].get<0>() == ring[min_i].get<0>() &&
1923 ring[prev_i].get<1>() == ring[min_i].get<1>()) {
1924 prev_i--;
1925 /*
1926 Since the ring must be closed, we will never arrive at the first point
1927 otherwise the 1st point would be the min_i and all points would be
1928 the same and this ring would be a point.
1929 */
1930 if (prev_i == static_cast<size_t>(-1)) return true;
1931 }
1932 } else
1933 prev_i = rsz - 1;
1934 } else if (min_i == rsz - 1) {
1935 // Since we are scanning from 1st point, it's impossible for post_i(0) to
1936 // be the same as min_i here.
1937 post_i = 0;
1938 // Can never come here if all polygon rings are closed.
1939 return true;
1940 }
1941
1942 /*
1943 Survive from continuous duplicates after min_i points. min_i must be
1944 the 1st of such duplicates if any, so no duplicates before min_i.
1945 */
1946 while (ring[post_i].get<0>() == ring[min_i].get<0>() &&
1947 ring[post_i].get<1>() == ring[min_i].get<1>()) {
1948 post_i++;
1949 /*
1950 Since the ring must be closed, we will never arrive at the last point
1951 otherwise the 1st point would be the min_i, i.e. all points are the same,
1952 and this ring would be a point.
1953 */
1954 if (post_i == rsz) return true;
1955 }
1956
1957 // The triangle's area tells the direction.
1958 x1 = ring[min_i].get<0>() - ring[prev_i].get<0>();
1959 y1 = ring[min_i].get<1>() - ring[prev_i].get<1>();
1960 x2 = ring[post_i].get<0>() - ring[min_i].get<0>();
1961 y2 = ring[post_i].get<1>() - ring[min_i].get<1>();
1962 double sign = x1 * y2 - x2 * y1;
1963
1964 if (sign == 0) return true; // Catches error: there is a spike in the ring.
1965
1966 // Reverse points in the ring, do direct memory manipulation rather
1967 // than using std::reverse for better performance.
1968 if ((sign > 0 && !want_ccw) || (sign < 0 && want_ccw)) {
1969 char *p = static_cast<char *>(ring.get_ptr()) + sizeof(uint32);
1970 char *q = nullptr, *p0;
1971 char pt[POINT_DATA_SIZE];
1972 size_t s = ring.size();
1973
1974 DBUG_ASSERT(ring.get_nbytes() == (s * POINT_DATA_SIZE + 4));
1975 p0 = p;
1976
1977 for (size_t i = 0; i < s / 2; i++, p += POINT_DATA_SIZE) {
1978 q = p0 + (s - i - 1) * POINT_DATA_SIZE;
1979 memcpy(&pt, p, POINT_DATA_SIZE);
1980 memcpy(p, q, POINT_DATA_SIZE);
1981 memcpy(q, &pt, POINT_DATA_SIZE);
1982 }
1983 }
1984 return false;
1985 }
1986
1987 /**
1988 Set this polygon's outer ring to be CCW and inner rings to be CW.
1989 @return on error returns true, on success returns false.
1990 */
set_polygon_ring_order()1991 bool Gis_polygon::set_polygon_ring_order() {
1992 DBUG_ASSERT(is_bg_adapter());
1993 if (outer().set_ring_order(true /* Ring order: CCW. */)) return true;
1994 Gis_polygon::inner_container_type::iterator itr;
1995 Gis_polygon::inner_container_type &inns = inners();
1996 for (itr = inns.begin(); itr != inns.end(); ++itr)
1997 if (itr->set_ring_order(false /* Ring order: CW. */)) return true;
1998
1999 return false;
2000 }
2001
2002 /**
2003 Make outer ring and inner rings objects for this polygon if it doesn't
2004 have one yet.
2005 Outer ring and inner rings have to have separated memory space, because
2006 we can't predict which one will be edited first. So the polygon object
2007 doesn't directly have memory, its m_ptr points to the outer ring, its
2008 m_inn_rings points to the inner rings, each have its own memory address
2009 and length, and Gis_polygon::get_nbytes returns the sum of them.
2010
2011 If the polygon doesn't own memory, then there is only one piece of memory
2012 passed into it and used by it, otherwise the two pieces of memory are
2013 separately allocated and released.
2014 */
make_rings()2015 void Gis_polygon::make_rings() {
2016 ring_type *outer = nullptr;
2017
2018 if (this->m_ptr == nullptr) {
2019 outer = new ring_type(nullptr, 0, Flags_t(wkb_linestring, 0), get_srid());
2020 outer->set_owner(this);
2021 this->m_ptr = outer;
2022 }
2023
2024 if (m_inn_rings == nullptr) {
2025 m_inn_rings = new inner_container_type(
2026 nullptr, 0, Flags_t(wkb_polygon_inner_rings, 0), get_srid());
2027 m_inn_rings->set_owner(this);
2028 }
2029 this->set_ownmem(false);
2030 }
2031
get_data_size() const2032 uint32 Gis_polygon::get_data_size() const {
2033 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
2034 return GET_SIZE_ERROR;
2035
2036 uint32 n_linear_rings;
2037 uint32 len;
2038 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2039
2040 if (is_length_verified()) return get_nbytes();
2041
2042 /*
2043 For a BG adapter polygon, its Gis_polygon::m_ptr points to its outer ring
2044 rather than the WKB buffer, it is the only exception.
2045 */
2046 DBUG_ASSERT(polygon_is_wkb_form() || !is_bg_adapter());
2047
2048 if (wkb.scan_non_zero_uint4(&n_linear_rings)) return GET_SIZE_ERROR;
2049
2050 while (n_linear_rings--) {
2051 uint32 n_points;
2052 if (wkb.scan_n_points_and_check_data(&n_points)) return GET_SIZE_ERROR;
2053 wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
2054 }
2055 len = static_cast<uint32>(wkb.data() - (const char *)get_data_ptr());
2056 if (len != get_nbytes()) set_nbytes(len);
2057 set_length_verified(true);
2058 return len;
2059 }
2060
init_from_wkt(Gis_read_stream * trs,String * wkb)2061 bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) {
2062 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
2063
2064 uint32 n_linear_rings = 0;
2065 uint32 lr_pos = wkb->length();
2066
2067 if (trs->check_next_symbol('(')) return true;
2068 if (wkb->reserve(4, 512)) return true;
2069 wkb->length(wkb->length() + 4); // Reserve space for points
2070
2071 bool is_first = true;
2072 for (;;) {
2073 Gis_line_string ls(false);
2074 ls.set_props(is_first ? POLYGON_OUTER_RING : POLYGON_INNER_RING);
2075 is_first = false;
2076
2077 if (ls.init_from_wkt(trs, wkb)) return true;
2078
2079 n_linear_rings++;
2080 if (trs->skip_char(',')) // Didn't find ','
2081 break;
2082 }
2083 write_at_position(lr_pos, n_linear_rings, wkb);
2084 if (trs->check_next_symbol(')')) return true;
2085 return false;
2086 }
2087
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)2088 uint Gis_polygon::init_from_wkb(THD *thd, const char *wkb, uint len,
2089 wkbByteOrder bo, String *res) {
2090 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
2091
2092 uint32 n_linear_rings;
2093 const char *wkb_orig = wkb;
2094
2095 if (len < 4) return 0;
2096
2097 if (0 == (n_linear_rings = wkb_get_uint(wkb, bo)) || res->reserve(4, 512))
2098 return 0;
2099 wkb += 4;
2100 len -= 4;
2101 q_append(n_linear_rings, res);
2102
2103 bool is_first = true;
2104 while (n_linear_rings--) {
2105 Gis_line_string ls(false);
2106 ls.set_props(is_first ? POLYGON_OUTER_RING : POLYGON_INNER_RING);
2107 is_first = false;
2108
2109 uint ls_len = 0;
2110
2111 if (!(ls_len = ls.init_from_wkb(thd, wkb, len, bo, res))) return 0;
2112
2113 wkb += ls_len;
2114 DBUG_ASSERT(len >= ls_len);
2115 len -= ls_len;
2116 }
2117
2118 return (uint)(wkb - wkb_orig);
2119 }
2120
get_data_as_wkt(String * txt,wkb_parser * wkb) const2121 bool Gis_polygon::get_data_as_wkt(String *txt, wkb_parser *wkb) const {
2122 uint32 n_linear_rings;
2123
2124 if (wkb->scan_non_zero_uint4(&n_linear_rings)) return true;
2125
2126 txt->append('(');
2127 while (n_linear_rings--) {
2128 uint32 n_points;
2129 if (wkb->scan_n_points_and_check_data(&n_points) ||
2130 txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
2131 return true;
2132 qs_append('(', txt);
2133 append_points(txt, n_points, wkb, 0);
2134 (*txt)[txt->length() - 1] = ')'; // Replace end ','
2135 qs_append(',', txt);
2136 }
2137 txt->length(txt->length() - 1); // Remove end ','
2138 qs_append(')', txt);
2139 return false;
2140 }
2141
get_mbr(MBR * mbr,wkb_parser * wkb) const2142 bool Gis_polygon::get_mbr(MBR *mbr, wkb_parser *wkb) const {
2143 uint32 n_linear_rings;
2144
2145 if (wkb->scan_non_zero_uint4(&n_linear_rings)) return true;
2146
2147 while (n_linear_rings--) {
2148 if (get_mbr_for_points(mbr, wkb, 0)) return true;
2149 }
2150 return false;
2151 }
2152
exterior_ring(String * result) const2153 int Gis_polygon::exterior_ring(String *result) const {
2154 uint32 n_points, n_linear_rings, length;
2155 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2156
2157 if (wkb.scan_non_zero_uint4(&n_linear_rings) ||
2158 wkb.scan_n_points_and_check_data(&n_points))
2159 return 1;
2160 length = n_points * POINT_DATA_SIZE;
2161 if (result->reserve(1 + 4 + 4 + length, 512)) return 1;
2162
2163 q_append((char)wkb_ndr, result);
2164 q_append((uint32)wkb_linestring, result);
2165 q_append(n_points, result);
2166 q_append(wkb.data(), length, result);
2167 return 0;
2168 }
2169
num_interior_ring(uint32 * n_int_rings) const2170 int Gis_polygon::num_interior_ring(uint32 *n_int_rings) const {
2171 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2172 if (wkb.scan_non_zero_uint4(n_int_rings)) return 1;
2173 *n_int_rings -= 1;
2174 return 0;
2175 }
2176
interior_ring_n(uint32 num,String * result) const2177 int Gis_polygon::interior_ring_n(uint32 num, String *result) const {
2178 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2179 uint32 n_linear_rings;
2180 uint32 n_points;
2181 uint32 points_size;
2182
2183 if (num < 1 || wkb.scan_non_zero_uint4(&n_linear_rings) ||
2184 num >= n_linear_rings)
2185 return 1;
2186
2187 while (num--) {
2188 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
2189 wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
2190 }
2191 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
2192 points_size = n_points * POINT_DATA_SIZE;
2193 if (result->reserve(1 + 4 + 4 + points_size, 512)) return 1;
2194
2195 q_append((char)wkb_ndr, result);
2196 q_append((uint32)wkb_linestring, result);
2197 q_append(n_points, result);
2198 q_append(wkb.data(), points_size, result);
2199 return 0;
2200 }
2201
reverse_coordinates()2202 bool Gis_polygon::reverse_coordinates() {
2203 uint32 current_data_offset = 0;
2204 uint32 numrings;
2205
2206 if (num_interior_ring(&numrings)) {
2207 return true;
2208 }
2209
2210 numrings += 1; // add exterior ring to number of rings.
2211 current_data_offset += 4; // add numRings header size to data offset.
2212
2213 for (uint32 i = 0; i < numrings; i++) {
2214 uint32 num_of_points = uint4korr(get_ucptr() + current_data_offset);
2215 current_data_offset += 4; // add linear ring header size to data offset.
2216
2217 for (uint32 j = 0; j < num_of_points; j++) {
2218 double x = float8get(get_cptr() + current_data_offset);
2219 double y =
2220 float8get(get_cptr() + current_data_offset + SIZEOF_STORED_DOUBLE);
2221
2222 float8store(get_cptr() + current_data_offset, y);
2223 float8store(get_cptr() + current_data_offset + SIZEOF_STORED_DOUBLE, x);
2224
2225 current_data_offset += POINT_DATA_SIZE;
2226 }
2227 }
2228
2229 return false;
2230 }
2231
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)2232 bool Gis_polygon::validate_coordinate_range(double srs_angular_unit,
2233 bool *long_out_of_range,
2234 bool *lat_out_of_range,
2235 double *out_of_range_value) {
2236 uint32 numrings;
2237 *long_out_of_range = false;
2238 *lat_out_of_range = false;
2239
2240 if (num_interior_ring(&numrings)) {
2241 return true; /* purecov: inspected */
2242 }
2243
2244 numrings += 1; // Add exterior ring to number of rings.
2245 uint32 current_data_offset = 4; // Add numRings header size to data offset.
2246
2247 for (uint32 i = 0; i < numrings; i++) {
2248 uint32 num_of_points = uint4korr(get_ucptr() + current_data_offset);
2249 current_data_offset += 4; // Add linear ring header size to data offset.
2250
2251 for (uint32 j = 0; j < num_of_points; j++) {
2252 double x = float8get(get_cptr() + current_data_offset);
2253 double y =
2254 float8get(get_cptr() + current_data_offset + SIZEOF_STORED_DOUBLE);
2255
2256 if (check_coordinate_range(x, y, srs_angular_unit, long_out_of_range,
2257 lat_out_of_range, out_of_range_value)) {
2258 return true;
2259 }
2260
2261 current_data_offset += POINT_DATA_SIZE;
2262 }
2263 }
2264
2265 return false;
2266 }
2267
get_class_info() const2268 const Geometry::Class_info *Gis_polygon::get_class_info() const {
2269 return &polygon_class;
2270 }
2271
2272 /**
2273 Packup a polygon's outer ring and inner rings into a single chunk of
2274 memory as result. nbytes returns the number of bytes in WKB data.
2275 The returned WKB has no WKB header.
2276 Never call get_ptr to obtain a polygon's WKB data.
2277
2278 @param geo0 The polygon whose WKB data we want to pack up.
2279 @param[out] pnbytes Takes back the number of bytes of the packed WKB string.
2280 @return The address of the packed WKB string buffer.
2281 */
get_packed_ptr(const Geometry * geo0,size_t * pnbytes)2282 void *get_packed_ptr(const Geometry *geo0, size_t *pnbytes) {
2283 DBUG_ASSERT(geo0->get_geotype() == Geometry::wkb_polygon &&
2284 pnbytes != nullptr);
2285 const Gis_polygon *geo = static_cast<const Gis_polygon *>(geo0);
2286 Gis_polygon::ring_type *out_ring = outer_ring(geo);
2287 Gis_polygon::inner_container_type *inn_rings = geo->inner_rings();
2288 size_t &nbytes = *pnbytes;
2289
2290 if (out_ring == nullptr) {
2291 DBUG_ASSERT(inn_rings == nullptr);
2292 *pnbytes = 0;
2293 return nullptr;
2294 }
2295
2296 // Inner rings may have out of line rings.
2297 if (inn_rings) inn_rings->reassemble();
2298
2299 size_t vallen = sizeof(uint32) + out_ring->get_nbytes() +
2300 (inn_rings ? inn_rings->get_nbytes() : 0);
2301 void *src_val = gis_wkb_alloc(vallen);
2302 if (src_val == nullptr) {
2303 nbytes = 0;
2304 return nullptr;
2305 }
2306
2307 memcpy(static_cast<char *>(src_val) + sizeof(uint32), out_ring->get_ptr(),
2308 out_ring->get_nbytes());
2309
2310 size_t n_inns = 0;
2311 if (inn_rings && inn_rings->get_nbytes()) {
2312 memcpy(
2313 static_cast<char *>(src_val) + sizeof(uint32) + out_ring->get_nbytes(),
2314 inn_rings->get_ptr(), inn_rings->get_nbytes());
2315 n_inns = inn_rings->size();
2316 }
2317
2318 DBUG_ASSERT(1 + n_inns <= 0xFFFFFFFF);
2319 int4store(static_cast<uchar *>(src_val), static_cast<uint32>(1 + n_inns));
2320
2321 nbytes = vallen;
2322 return src_val;
2323 }
2324
2325 /**
2326 Get a polygon's WKB string's starting address. The polygon is already
2327 packed so that its outer ring and inner rings point to different locations
2328 of a continuous chunk of WKB buffer.
2329
2330 @param geo0 The already packed polygon, we want to get its data address.
2331 @return The WKB string starting address, right after the WKB header if any.
2332 */
get_packed_ptr(Geometry * geo0)2333 const char *get_packed_ptr(Geometry *geo0) {
2334 DBUG_ASSERT(geo0->get_geotype() == Geometry::wkb_polygon);
2335 Gis_polygon *geo = static_cast<Gis_polygon *>(geo0);
2336 Gis_polygon::ring_type *out_ring = outer_ring(geo);
2337 Gis_polygon::inner_container_type *inn_rings = geo->inner_rings();
2338 if (inn_rings)
2339 DBUG_ASSERT(out_ring->get_cptr() + out_ring->get_nbytes() ==
2340 inn_rings->get_cptr());
2341 return (out_ring->get_cptr() - sizeof(uint32) /*polygon's ring count */);
2342 }
2343
2344 /**
2345 Check whether plgn is packed into its owner mplgn's WKB buffer.
2346 @param plgn the polygon to be checked
2347 @param mplgn the multipolygon, owner/holder of plgn.
2348 @return true if plgn is packed into mplgn, false otherwise.
2349 */
polygon_is_packed(Geometry * plgn,Geometry * mplgn)2350 bool polygon_is_packed(Geometry *plgn, Geometry *mplgn) {
2351 DBUG_ASSERT(plgn->get_geotype() == Geometry::wkb_polygon &&
2352 mplgn->get_geotype() == Geometry::wkb_multipolygon);
2353 Gis_polygon *geo = static_cast<Gis_polygon *>(plgn);
2354 Gis_polygon::ring_type *out_ring = outer_ring(geo);
2355 Gis_polygon::inner_container_type *inn_rings = geo->inner_rings();
2356 const char *orstart = out_ring->get_cptr();
2357 bool ret = false;
2358
2359 if (orstart < mplgn->get_cptr() + mplgn->get_nbytes() &&
2360 orstart > mplgn->get_cptr()) {
2361 // This polygon is already stored packed and inline
2362 if (inn_rings && inn_rings->get_nbytes())
2363 DBUG_ASSERT(orstart + out_ring->get_nbytes() == inn_rings->get_ptr());
2364
2365 ret = true;
2366 }
2367
2368 return ret;
2369 }
2370
own_rings(Geometry * geo0)2371 void own_rings(Geometry *geo0) {
2372 DBUG_ASSERT(geo0->get_geotype() == Geometry::wkb_polygon);
2373 Gis_polygon *geo = static_cast<Gis_polygon *>(geo0);
2374
2375 if (outer_ring(geo)) outer_ring(geo)->set_owner(geo);
2376 if (geo->inner_rings()) geo->inner_rings()->set_owner(geo);
2377 }
2378
2379 /***************************** MultiPoint *******************************/
get_data_size() const2380 uint32 Gis_multi_point::get_data_size() const {
2381 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
2382 return GET_SIZE_ERROR;
2383
2384 uint32 n_points;
2385 uint32 len;
2386 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2387
2388 if (is_length_verified()) return get_nbytes();
2389 if (wkb.scan_n_points_and_check_data(&n_points, WKB_HEADER_SIZE))
2390 return GET_SIZE_ERROR;
2391
2392 len = 4 + n_points * (POINT_DATA_SIZE + WKB_HEADER_SIZE);
2393 if (len != get_nbytes()) set_nbytes(len);
2394 set_length_verified(true);
2395 return len;
2396 }
2397
init_from_wkt(Gis_read_stream * trs,String * wkb)2398 bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb) {
2399 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
2400
2401 uint32 n_points = 0;
2402 uint32 np_pos = wkb->length();
2403 Gis_point p(false);
2404
2405 if (trs->check_next_symbol('(')) return true;
2406 if (wkb->reserve(4, 512)) return true;
2407 wkb->length(wkb->length() + 4); // Reserve space for points
2408
2409 /*
2410 According to OGC, the WKT for a multipoint is something like:
2411 MULTIPOINT((1 1), (2 2))
2412 and to be backward compatible with older versions of MySQL, we still
2413 support the MySQL format:
2414 MULTIPOINT(1 1, 2 2)
2415 But we don't allow the mixture of both formats in the same multipoint
2416 geometry.
2417 */
2418 const bool match_pt_lbra =
2419 (trs->get_next_toc_type() == Gis_read_stream::l_bra);
2420
2421 for (;;) {
2422 if (wkb->reserve(1 + 4, 512)) return true;
2423 q_append((char)wkb_ndr, wkb);
2424 q_append((uint32)wkb_point, wkb);
2425
2426 if (match_pt_lbra && trs->check_next_symbol('(')) return true;
2427
2428 if (p.init_from_wkt(trs, wkb, false)) return true;
2429
2430 if (match_pt_lbra && trs->check_next_symbol(')')) return true;
2431
2432 n_points++;
2433 if (trs->skip_char(',')) // Didn't find ','
2434 break;
2435 }
2436 write_at_position(np_pos, n_points, wkb); // Store number of found points
2437 if (trs->check_next_symbol(')')) return true;
2438 return false;
2439 }
2440
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)2441 uint Gis_multi_point::init_from_wkb(THD *thd, const char *wkb, uint len,
2442 wkbByteOrder bo, String *res) {
2443 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
2444
2445 uint32 n_points;
2446 uint proper_size;
2447 Gis_point p(false);
2448 const char *wkb_end;
2449
2450 if (len < 4 || (n_points = wkb_get_uint(wkb, bo)) > max_n_points) return 0;
2451 proper_size = 4 + n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
2452
2453 if (len < proper_size || res->reserve(proper_size, 512)) return 0;
2454
2455 q_append(n_points, res);
2456 wkb_end = wkb + proper_size;
2457 for (wkb += 4; wkb < wkb_end; wkb += (WKB_HEADER_SIZE + POINT_DATA_SIZE)) {
2458 write_wkb_header(res, wkb_point);
2459 if ((*wkb != wkb_xdr && *wkb != wkb_ndr) ||
2460 wkb_point != uint4korr(wkb + 1) ||
2461 !p.init_from_wkb(thd, wkb + WKB_HEADER_SIZE, POINT_DATA_SIZE,
2462 (wkbByteOrder)wkb[0], res))
2463 return 0;
2464 }
2465 return proper_size;
2466 }
2467
get_data_as_wkt(String * txt,wkb_parser * wkb) const2468 bool Gis_multi_point::get_data_as_wkt(String *txt, wkb_parser *wkb) const {
2469 uint32 n_points;
2470
2471 txt->append('(');
2472 if (wkb->scan_n_points_and_check_data(&n_points, WKB_HEADER_SIZE) ||
2473 txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
2474 return true;
2475
2476 /*
2477 Now we will output multipoint in OGC format, i.e. for each of its point,
2478 bracket its coordinates with ().
2479 */
2480 append_points(txt, n_points, wkb, WKB_HEADER_SIZE, true);
2481 txt->length(txt->length() - 1); // Remove end ','
2482 qs_append(')', txt);
2483 return false;
2484 }
2485
get_mbr(MBR * mbr,wkb_parser * wkb) const2486 bool Gis_multi_point::get_mbr(MBR *mbr, wkb_parser *wkb) const {
2487 return get_mbr_for_points(mbr, wkb, WKB_HEADER_SIZE);
2488 }
2489
num_geometries(uint32 * num) const2490 int Gis_multi_point::num_geometries(uint32 *num) const {
2491 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2492 return wkb.scan_non_zero_uint4(num) ? 1 : 0;
2493 }
2494
geometry_n(uint32 num,String * result) const2495 int Gis_multi_point::geometry_n(uint32 num, String *result) const {
2496 uint32 n_points;
2497 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2498
2499 if (num < 1 || wkb.scan_n_points_and_check_data(&n_points, WKB_HEADER_SIZE) ||
2500 num > n_points || result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE, 32))
2501 return 1;
2502 wkb.skip_unsafe((num - 1) * (WKB_HEADER_SIZE + POINT_DATA_SIZE));
2503
2504 q_append(wkb.data(), WKB_HEADER_SIZE + POINT_DATA_SIZE, result);
2505 return 0;
2506 }
2507
reverse_coordinates()2508 bool Gis_multi_point::reverse_coordinates() {
2509 uint32 current_data_offset = 0;
2510 uint32 num_of_points;
2511 if (num_geometries(&num_of_points)) {
2512 return true;
2513 }
2514
2515 current_data_offset += 4; // add number of points header to offset.
2516
2517 for (uint32 i = 0; i < num_of_points; i++) {
2518 current_data_offset +=
2519 WKB_HEADER_SIZE; // since each point includes a header.
2520
2521 double x = float8get(get_cptr() + current_data_offset);
2522 double y =
2523 float8get(get_cptr() + current_data_offset + SIZEOF_STORED_DOUBLE);
2524
2525 float8store(get_cptr() + current_data_offset, y);
2526 float8store(get_cptr() + current_data_offset + SIZEOF_STORED_DOUBLE, x);
2527
2528 current_data_offset += POINT_DATA_SIZE;
2529 }
2530
2531 return false;
2532 }
2533
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)2534 bool Gis_multi_point::validate_coordinate_range(double srs_angular_unit,
2535 bool *long_out_of_range,
2536 bool *lat_out_of_range,
2537 double *out_of_range_value) {
2538 uint32 num_of_points;
2539 *long_out_of_range = false;
2540 *lat_out_of_range = false;
2541
2542 if (num_geometries(&num_of_points)) {
2543 return true; /* purecov: inspected */
2544 }
2545
2546 uint32 current_data_offset = 4; // Add number of points header to offset.
2547
2548 for (uint32 i = 0; i < num_of_points; i++) {
2549 current_data_offset +=
2550 WKB_HEADER_SIZE; // Since each point includes a header.
2551
2552 double x = float8get(get_cptr() + current_data_offset);
2553 double y =
2554 float8get(get_cptr() + current_data_offset + SIZEOF_STORED_DOUBLE);
2555
2556 if (check_coordinate_range(x, y, srs_angular_unit, long_out_of_range,
2557 lat_out_of_range, out_of_range_value)) {
2558 return true;
2559 }
2560
2561 current_data_offset += POINT_DATA_SIZE;
2562 }
2563
2564 return false;
2565 }
2566
get_class_info() const2567 const Geometry::Class_info *Gis_multi_point::get_class_info() const {
2568 return &multipoint_class;
2569 }
2570
2571 /***************************** MultiLineString *******************************/
get_data_size() const2572 uint32 Gis_multi_line_string::get_data_size() const {
2573 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
2574 return GET_SIZE_ERROR;
2575
2576 uint32 n_line_strings;
2577 uint32 len;
2578 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2579
2580 if (is_length_verified()) return get_nbytes();
2581 if (wkb.scan_non_zero_uint4(&n_line_strings)) return GET_SIZE_ERROR;
2582
2583 while (n_line_strings--) {
2584 uint32 n_points;
2585
2586 if (wkb.skip_wkb_header() || wkb.scan_n_points_and_check_data(&n_points))
2587 return GET_SIZE_ERROR;
2588
2589 wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
2590 }
2591
2592 len = static_cast<uint32>(wkb.data() - (const char *)get_data_ptr());
2593 if (len != get_nbytes()) set_nbytes(len);
2594 set_length_verified(true);
2595 return len;
2596 }
2597
init_from_wkt(Gis_read_stream * trs,String * wkb)2598 bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) {
2599 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
2600
2601 uint32 n_line_strings = 0;
2602 uint32 ls_pos = wkb->length();
2603
2604 if (trs->check_next_symbol('(')) return true;
2605 if (wkb->reserve(4, 512)) return true;
2606 wkb->length(wkb->length() + 4); // Reserve space for points
2607
2608 for (;;) {
2609 Gis_line_string ls(false);
2610
2611 if (wkb->reserve(1 + 4, 512)) return true;
2612 q_append((char)wkb_ndr, wkb);
2613 q_append((uint32)wkb_linestring, wkb);
2614
2615 if (ls.init_from_wkt(trs, wkb)) return true;
2616 n_line_strings++;
2617 if (trs->skip_char(',')) // Didn't find ','
2618 break;
2619 }
2620 write_at_position(ls_pos, n_line_strings, wkb);
2621 if (trs->check_next_symbol(')')) return true;
2622 return false;
2623 }
2624
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)2625 uint Gis_multi_line_string::init_from_wkb(THD *thd, const char *wkb, uint len,
2626 wkbByteOrder bo, String *res) {
2627 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
2628
2629 uint32 n_line_strings;
2630 const char *wkb_orig = wkb;
2631
2632 if (len < 4 || (n_line_strings = wkb_get_uint(wkb, bo)) < 1) return 0;
2633
2634 if (res->reserve(4, 512)) return 0;
2635 q_append(n_line_strings, res);
2636
2637 wkb += 4;
2638 len -= 4;
2639
2640 while (n_line_strings--) {
2641 Gis_line_string ls(false);
2642 uint ls_len = 0;
2643
2644 if ((len < WKB_HEADER_SIZE) || uint4korr(wkb + 1) != wkb_linestring ||
2645 (*wkb != wkb_xdr && *wkb != wkb_ndr) ||
2646 res->reserve(WKB_HEADER_SIZE, 512))
2647 return 0;
2648
2649 write_wkb_header(res, wkb_linestring);
2650 if (!(ls_len = ls.init_from_wkb(thd, wkb + WKB_HEADER_SIZE,
2651 len - WKB_HEADER_SIZE, (wkbByteOrder)wkb[0],
2652 res)))
2653 return 0;
2654 ls_len += WKB_HEADER_SIZE;
2655 ;
2656 wkb += ls_len;
2657 DBUG_ASSERT(len >= ls_len);
2658 len -= ls_len;
2659 }
2660 return (uint)(wkb - wkb_orig);
2661 }
2662
get_data_as_wkt(String * txt,wkb_parser * wkb) const2663 bool Gis_multi_line_string::get_data_as_wkt(String *txt,
2664 wkb_parser *wkb) const {
2665 uint32 n_line_strings;
2666
2667 if (wkb->scan_non_zero_uint4(&n_line_strings)) return true;
2668
2669 txt->append('(');
2670 while (n_line_strings--) {
2671 uint32 n_points;
2672
2673 if (wkb->skip_wkb_header() ||
2674 wkb->scan_n_points_and_check_data(&n_points) ||
2675 txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
2676 return true;
2677 qs_append('(', txt);
2678 append_points(txt, n_points, wkb, 0);
2679 (*txt)[txt->length() - 1] = ')';
2680 qs_append(',', txt);
2681 }
2682 txt->length(txt->length() - 1);
2683 qs_append(')', txt);
2684 return false;
2685 }
2686
get_mbr(MBR * mbr,wkb_parser * wkb) const2687 bool Gis_multi_line_string::get_mbr(MBR *mbr, wkb_parser *wkb) const {
2688 uint32 n_line_strings;
2689
2690 if (wkb->scan_non_zero_uint4(&n_line_strings)) return true;
2691
2692 while (n_line_strings--) {
2693 if (wkb->skip_wkb_header() || get_mbr_for_points(mbr, wkb, 0)) return true;
2694 }
2695 return false;
2696 }
2697
num_geometries(uint32 * num) const2698 int Gis_multi_line_string::num_geometries(uint32 *num) const {
2699 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2700 return wkb.scan_non_zero_uint4(num) ? 1 : 0;
2701 }
2702
geometry_n(uint32 num,String * result) const2703 int Gis_multi_line_string::geometry_n(uint32 num, String *result) const {
2704 uint32 n_line_strings, n_points, length;
2705 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2706
2707 if (wkb.scan_non_zero_uint4(&n_line_strings)) return 1;
2708
2709 if ((num > n_line_strings) || (num < 1)) return 1;
2710
2711 for (;;) {
2712 if (wkb.skip_wkb_header() || wkb.scan_n_points_and_check_data(&n_points))
2713 return 1;
2714 length = POINT_DATA_SIZE * n_points;
2715 if (!--num) break;
2716 wkb.skip_unsafe(length);
2717 }
2718 return result->append(wkb.data() - 4 - WKB_HEADER_SIZE,
2719 length + 4 + WKB_HEADER_SIZE, static_cast<size_t>(0));
2720 }
2721
geom_length(double * len) const2722 int Gis_multi_line_string::geom_length(double *len) const {
2723 uint32 n_line_strings;
2724 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2725
2726 if (wkb.scan_non_zero_uint4(&n_line_strings)) return 1;
2727
2728 *len = 0;
2729 while (n_line_strings--) {
2730 double ls_len;
2731 Gis_line_string ls(false);
2732 if (wkb.skip_wkb_header()) return 1;
2733 ls.set_data_ptr(&wkb);
2734 if (ls.geom_length(&ls_len)) return 1;
2735 *len += ls_len;
2736 /*
2737 We know here that ls was ok, so we can call the trivial function
2738 Gis_line_string::get_data_size without error checking.
2739 */
2740 wkb.skip_unsafe(ls.get_data_size());
2741 }
2742 return 0;
2743 }
2744
is_closed(int * closed) const2745 int Gis_multi_line_string::is_closed(int *closed) const {
2746 uint32 n_line_strings;
2747 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2748
2749 if (wkb.scan_non_zero_uint4(&n_line_strings)) return 1;
2750
2751 while (n_line_strings--) {
2752 Gis_line_string ls(false);
2753 if (wkb.skip_wkb_header()) return 1;
2754 ls.set_data_ptr(&wkb);
2755 if (ls.is_closed(closed)) return 1;
2756 if (!*closed) return 0;
2757 wkb.skip_unsafe(ls.get_data_size());
2758 }
2759 return 0;
2760 }
2761
reverse_coordinates()2762 bool Gis_multi_line_string::reverse_coordinates() {
2763 uint32 num_of_linestrings;
2764 size_t current_data_offset = 4; // Skip num_wkbLineStrings header size.
2765
2766 String str(get_cptr(), get_nbytes(), &my_charset_bin);
2767
2768 if (num_geometries(&num_of_linestrings)) {
2769 return true;
2770 }
2771 for (uint32 i = 1; i <= num_of_linestrings; i++) {
2772 String result;
2773
2774 if (geometry_n(i, &result)) {
2775 return true;
2776 }
2777
2778 Geometry *g;
2779 Geometry_buffer buffer;
2780 if (!(g = Geometry::construct(&buffer, &result, false))) {
2781 return true;
2782 }
2783
2784 if (g->reverse_coordinates()) {
2785 return true;
2786 }
2787
2788 if (str.replace(current_data_offset, result.length(), result.ptr(),
2789 result.length())) {
2790 return true;
2791 }
2792
2793 current_data_offset += result.length();
2794 }
2795
2796 return false;
2797 }
2798
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)2799 bool Gis_multi_line_string::validate_coordinate_range(
2800 double srs_angular_unit, bool *long_out_of_range, bool *lat_out_of_range,
2801 double *out_of_range_value) {
2802 uint32 num_of_linestrings;
2803 *long_out_of_range = false;
2804 *lat_out_of_range = false;
2805
2806 if (num_geometries(&num_of_linestrings)) {
2807 return true; /* purecov: inspected */
2808 }
2809
2810 for (uint32 i = 1; i <= num_of_linestrings; i++) {
2811 String result;
2812
2813 if (geometry_n(i, &result)) {
2814 return true; /* purecov: inspected */
2815 }
2816
2817 Geometry *g;
2818 Geometry_buffer buffer;
2819 if (!(g = Geometry::construct(&buffer, &result, false))) {
2820 return true; /* purecov: inspected */
2821 }
2822
2823 if (g->validate_coordinate_range(srs_angular_unit, long_out_of_range,
2824 lat_out_of_range, out_of_range_value)) {
2825 return true;
2826 }
2827 }
2828
2829 return false;
2830 }
2831
get_class_info() const2832 const Geometry::Class_info *Gis_multi_line_string::get_class_info() const {
2833 return &multilinestring_class;
2834 }
2835
2836 /***************************** MultiPolygon *******************************/
get_data_size() const2837 uint32 Gis_multi_polygon::get_data_size() const {
2838 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
2839 return GET_SIZE_ERROR;
2840
2841 uint32 n_polygons;
2842 uint32 len;
2843 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2844
2845 if (is_length_verified()) return get_nbytes();
2846 if (wkb.scan_non_zero_uint4(&n_polygons)) return GET_SIZE_ERROR;
2847
2848 while (n_polygons--) {
2849 uint32 n_linear_rings;
2850 if (wkb.skip_wkb_header() || wkb.scan_non_zero_uint4(&n_linear_rings))
2851 return GET_SIZE_ERROR;
2852
2853 while (n_linear_rings--) {
2854 uint32 n_points;
2855
2856 if (wkb.scan_n_points_and_check_data(&n_points)) return GET_SIZE_ERROR;
2857
2858 wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
2859 }
2860 }
2861
2862 len = static_cast<uint32>(wkb.data() - (const char *)get_data_ptr());
2863 if (len != get_nbytes()) set_nbytes(len);
2864 set_length_verified(true);
2865 return len;
2866 }
2867
init_from_wkt(Gis_read_stream * trs,String * wkb)2868 bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) {
2869 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
2870
2871 uint32 n_polygons = 0;
2872 uint32 np_pos = wkb->length();
2873 Gis_polygon p(false);
2874
2875 if (trs->check_next_symbol('(')) return true;
2876 if (wkb->reserve(4, 512)) return true;
2877 wkb->length(wkb->length() + 4); // Reserve space for points
2878
2879 for (;;) {
2880 if (wkb->reserve(1 + 4, 512)) return true;
2881 q_append((char)wkb_ndr, wkb);
2882 q_append((uint32)wkb_polygon, wkb);
2883
2884 if (p.init_from_wkt(trs, wkb)) return true;
2885 n_polygons++;
2886 if (trs->skip_char(',')) // Didn't find ','
2887 break;
2888 }
2889 write_at_position(np_pos, n_polygons, wkb);
2890 if (trs->check_next_symbol(')')) return true;
2891 return false;
2892 }
2893
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)2894 uint Gis_multi_polygon::init_from_wkb(THD *thd, const char *wkb, uint len,
2895 wkbByteOrder bo, String *res) {
2896 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
2897
2898 uint32 n_poly;
2899 const char *wkb_orig = wkb;
2900
2901 if (len < 4) return 0;
2902 n_poly = wkb_get_uint(wkb, bo);
2903
2904 if (res->reserve(4, 512)) return 0;
2905 q_append(n_poly, res);
2906
2907 wkb += 4;
2908 len -= 4;
2909
2910 while (n_poly--) {
2911 Gis_polygon p(false);
2912 uint p_len = 0;
2913
2914 if (len < WKB_HEADER_SIZE || uint4korr(wkb + 1) != wkb_polygon ||
2915 (*wkb != wkb_xdr && *wkb != wkb_ndr) ||
2916 res->reserve(WKB_HEADER_SIZE, 512))
2917 return 0;
2918 write_wkb_header(res, wkb_polygon);
2919 if (!(p_len =
2920 p.init_from_wkb(thd, wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE,
2921 (wkbByteOrder)wkb[0], res)))
2922 return 0;
2923 p_len += WKB_HEADER_SIZE;
2924 wkb += p_len;
2925 DBUG_ASSERT(len >= p_len);
2926 len -= p_len;
2927 }
2928 return (uint)(wkb - wkb_orig);
2929 }
2930
get_data_as_wkt(String * txt,wkb_parser * wkb) const2931 bool Gis_multi_polygon::get_data_as_wkt(String *txt, wkb_parser *wkb) const {
2932 uint32 n_polygons;
2933
2934 if (wkb->scan_non_zero_uint4(&n_polygons)) return true;
2935
2936 txt->append('(');
2937 while (n_polygons--) {
2938 uint32 n_linear_rings;
2939
2940 if (wkb->skip_wkb_header() || wkb->scan_non_zero_uint4(&n_linear_rings) ||
2941 txt->reserve(1, 512))
2942 return true;
2943 q_append('(', txt);
2944
2945 while (n_linear_rings--) {
2946 uint32 n_points;
2947 if (wkb->scan_n_points_and_check_data(&n_points) ||
2948 txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
2949 return true;
2950 qs_append('(', txt);
2951 append_points(txt, n_points, wkb, 0);
2952 (*txt)[txt->length() - 1] = ')';
2953 qs_append(',', txt);
2954 }
2955 (*txt)[txt->length() - 1] = ')';
2956 qs_append(',', txt);
2957 }
2958 txt->length(txt->length() - 1);
2959 qs_append(')', txt);
2960 return false;
2961 }
2962
get_mbr(MBR * mbr,wkb_parser * wkb) const2963 bool Gis_multi_polygon::get_mbr(MBR *mbr, wkb_parser *wkb) const {
2964 uint32 n_polygons;
2965
2966 if (wkb->scan_non_zero_uint4(&n_polygons)) return true;
2967
2968 while (n_polygons--) {
2969 uint32 n_linear_rings;
2970 if (wkb->skip_wkb_header() || wkb->scan_non_zero_uint4(&n_linear_rings))
2971 return true;
2972
2973 while (n_linear_rings--) {
2974 if (get_mbr_for_points(mbr, wkb, 0)) return true;
2975 }
2976 }
2977 return false;
2978 }
2979
num_geometries(uint32 * num) const2980 int Gis_multi_polygon::num_geometries(uint32 *num) const {
2981 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2982 return wkb.scan_non_zero_uint4(num) ? 1 : 0;
2983 }
2984
geometry_n(uint32 num,String * result) const2985 int Gis_multi_polygon::geometry_n(uint32 num, String *result) const {
2986 uint32 n_polygons;
2987 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
2988 const char *start_of_polygon = wkb.data();
2989
2990 if (wkb.scan_non_zero_uint4(&n_polygons)) return 1;
2991
2992 if (num > n_polygons || num < 1) return -1;
2993
2994 do {
2995 uint32 n_linear_rings;
2996 start_of_polygon = wkb.data();
2997
2998 if (wkb.skip_wkb_header() || wkb.scan_non_zero_uint4(&n_linear_rings))
2999 return 1;
3000
3001 while (n_linear_rings--) {
3002 uint32 n_points;
3003 if (wkb.scan_n_points_and_check_data(&n_points)) return 1;
3004 wkb.skip_unsafe(POINT_DATA_SIZE * n_points);
3005 }
3006 } while (--num);
3007 if (wkb.no_data(0)) // We must check last segment
3008 return 1;
3009 return result->append(start_of_polygon,
3010 (uint32)(wkb.data() - start_of_polygon),
3011 static_cast<size_t>(0));
3012 }
3013
reverse_coordinates()3014 bool Gis_multi_polygon::reverse_coordinates() {
3015 uint32 num_of_polygons;
3016 size_t current_data_offset = 4; // Skip num_polygons header size.
3017
3018 String str(get_cptr(), get_nbytes(), &my_charset_bin);
3019
3020 if (num_geometries(&num_of_polygons)) {
3021 return true;
3022 }
3023
3024 for (uint32 i = 1; i <= num_of_polygons; i++) {
3025 String result;
3026
3027 if (geometry_n(i, &result)) {
3028 return true;
3029 }
3030
3031 Geometry *g;
3032 Geometry_buffer buffer;
3033 if (!(g = Geometry::construct(&buffer, &result, false))) {
3034 return true;
3035 }
3036
3037 if (g->reverse_coordinates()) {
3038 return true;
3039 }
3040
3041 if (str.replace(current_data_offset, result.length(), result.ptr(),
3042 result.length())) {
3043 return true;
3044 }
3045
3046 current_data_offset += result.length();
3047 }
3048
3049 return false;
3050 }
3051
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)3052 bool Gis_multi_polygon::validate_coordinate_range(double srs_angular_unit,
3053 bool *long_out_of_range,
3054 bool *lat_out_of_range,
3055 double *out_of_range_value) {
3056 uint32 num_of_polygons;
3057 *long_out_of_range = false;
3058 *lat_out_of_range = false;
3059
3060 if (num_geometries(&num_of_polygons)) {
3061 return true; /* purecov: inspected */
3062 }
3063
3064 for (uint32 i = 1; i <= num_of_polygons; i++) {
3065 String result;
3066
3067 if (geometry_n(i, &result)) {
3068 return true; /* purecov: inspected */
3069 }
3070
3071 Geometry *g;
3072 Geometry_buffer buffer;
3073 if (!(g = Geometry::construct(&buffer, &result, false))) {
3074 return true; /* purecov: inspected */
3075 }
3076
3077 if (g->validate_coordinate_range(srs_angular_unit, long_out_of_range,
3078 lat_out_of_range, out_of_range_value)) {
3079 return true;
3080 }
3081 }
3082
3083 return false;
3084 }
3085
get_class_info() const3086 const Geometry::Class_info *Gis_multi_polygon::get_class_info() const {
3087 return &multipolygon_class;
3088 }
3089
3090 /************************* GeometryCollection ****************************/
3091
3092 /**
3093 Create a Geometry object from WKB.
3094
3095 This function creates an intermediate geometry object which may have wrong
3096 length property (longer than what's needed by the geometry), use it only
3097 within this class. Do not check for exact length here, caller will do
3098 that if necessary.
3099
3100 @param wkb The input WKB.
3101 @param buffer A buffer for the output geometry.
3102 @return NULL if the input WKB was found invalid. Otherwise, the constructed
3103 geometry.
3104 */
scan_header_and_create(wkb_parser * wkb,Geometry_buffer * buffer)3105 Geometry *Gis_geometry_collection::scan_header_and_create(
3106 wkb_parser *wkb, Geometry_buffer *buffer) {
3107 Geometry *geom;
3108 wkb_header header;
3109
3110 if (wkb->scan_wkb_header(&header) ||
3111 !(geom = create_by_typeid(buffer, header.wkb_type)))
3112 return nullptr;
3113 geom->set_data_ptr(wkb->data(), wkb->length());
3114
3115 /*
3116 The length in geom might be wrong, since it's set to the total length of
3117 the geometry collection's WKB. Such error is only allowed for temporary
3118 geometry objects created here.
3119
3120 Correct the length only for points because other types has known structure
3121 and can deduce the valid length. But for point we have to always require
3122 the exact length.
3123 */
3124 if (geom->get_type() == wkb_point) {
3125 if (geom->get_nbytes() < POINT_DATA_SIZE) return nullptr;
3126 geom->set_nbytes(POINT_DATA_SIZE);
3127 }
3128
3129 return geom;
3130 }
3131
3132 /**
3133 Append geometry into geometry collection which can be empty.
3134 @param geo geometry to be appended, it can't be empty.
3135 @param gcbuf this geometry collection's data buffer, it's of GEOMETRY format
3136 and is a separate String buffer.
3137 @return false if no error, otherwise true.
3138
3139 */
append_geometry(const Geometry * geo,String * gcbuf)3140 bool Gis_geometry_collection::append_geometry(const Geometry *geo,
3141 String *gcbuf) {
3142 uint32 collection_len = gcbuf->length(), geo_len = geo->get_data_size();
3143 if (geo_len == GET_SIZE_ERROR) return true;
3144 DBUG_ASSERT(collection_len == 0 ||
3145 get_data_size() == collection_len - GEOM_HEADER_SIZE);
3146 if (gcbuf->reserve((collection_len == 0 ? GEOM_HEADER_SIZE + 4 : 0) +
3147 geo_len + WKB_HEADER_SIZE,
3148 512))
3149 return true;
3150
3151 char *ptr = gcbuf->ptr();
3152 uint32 extra = 0;
3153 if (collection_len == 0) {
3154 collection_len = GEOM_HEADER_SIZE + 4;
3155 extra = GEOM_HEADER_SIZE;
3156 write_geometry_header(ptr, geo->get_srid(), wkb_geometrycollection, 0);
3157 set_srid(geo->get_srid());
3158 has_geom_header_space(true);
3159 }
3160
3161 // Skip GEOMETRY header.
3162 ptr += GEOM_HEADER_SIZE;
3163 const char *start = ptr;
3164
3165 int4store(ptr, uint4korr(ptr) + 1); // Increment object count.
3166 ptr += collection_len - GEOM_HEADER_SIZE;
3167 ptr = write_wkb_header(ptr, geo->get_type());
3168 memcpy(ptr, geo->get_data_ptr(), geo_len);
3169 gcbuf->length(collection_len + geo_len + WKB_HEADER_SIZE);
3170 set_data_ptr(start, extra + collection_len + geo_len - SRID_SIZE);
3171 return false;
3172 }
3173
3174 /**
3175 Append geometry into geometry collection, which can be empty. This object
3176 must be created from default constructor or below one:
3177 Gis_geometry_collection(gis::srid_t srid, wkbType gtype,
3178 const String *gbuf,
3179 String *gcbuf);
3180
3181 @param srid srid of geometry to be appended.
3182 @param gtype type of geometry to be appended.
3183 @param gbuf WKB data of geometry to be appended, gbuf->ptr isn't NULL and
3184 points right after the WKB header, this buffer can't be empty.
3185 @param gcbuf this geometry collection's data buffer, it's of GEOMETRY format
3186 and is a separate String buffer.
3187 @return false if no error, otherwise true.
3188
3189 */
append_geometry(gis::srid_t srid,wkbType gtype,const String * gbuf,String * gcbuf)3190 bool Gis_geometry_collection::append_geometry(gis::srid_t srid, wkbType gtype,
3191 const String *gbuf,
3192 String *gcbuf) {
3193 DBUG_ASSERT(gbuf != nullptr && gbuf->ptr() != nullptr && gbuf->length() > 0);
3194
3195 uint32 collection_len = gcbuf->length(), geo_len = gbuf->length();
3196 DBUG_ASSERT(collection_len == 0 ||
3197 get_data_size() == collection_len - GEOM_HEADER_SIZE);
3198 if (gcbuf->reserve((collection_len == 0 ? GEOM_HEADER_SIZE + 4 : 0) +
3199 geo_len + WKB_HEADER_SIZE,
3200 512))
3201 return true;
3202
3203 char *ptr = gcbuf->ptr();
3204 uint32 extra = 0;
3205 if (collection_len == 0) {
3206 collection_len = GEOM_HEADER_SIZE + 4;
3207 extra = GEOM_HEADER_SIZE;
3208 write_geometry_header(ptr, srid, wkb_geometrycollection, 0);
3209 set_srid(srid);
3210 has_geom_header_space(true);
3211 } else if (srid != get_srid())
3212 return true;
3213
3214 // Skip GEOMETRY header.
3215 ptr += GEOM_HEADER_SIZE;
3216 const char *start = ptr;
3217
3218 int4store(ptr, uint4korr(ptr) + 1); // Increment object count.
3219 ptr += collection_len - GEOM_HEADER_SIZE;
3220 ptr = write_wkb_header(ptr, gtype);
3221 memcpy(ptr, gbuf->ptr(), geo_len);
3222 gcbuf->length(collection_len + geo_len + WKB_HEADER_SIZE);
3223 set_data_ptr(start, extra + collection_len + geo_len - SRID_SIZE);
3224 return false;
3225 }
3226
3227 /**
3228 Create a geometry collection from a single geometry, and the created object
3229 refers to position right after the WKB header inside the 'gcbuf' buffer.
3230 @param srid the SRID of the first geometry to put into this
3231 geometry collection. Its SRID is used as the SRID of this
3232 geometry collection.
3233 @param gtype the type of the first geometry to put into this object.
3234 @param gbuf stores the WKB data of the first geometry to put into this object,
3235 not including its WKB header. if gbuf is NULL or gbuf->ptr is
3236 NULL, the created geometry collection is empty.
3237 @param gcbuf this geometry collection's data buffer in GEOMETRY format.
3238 */
Gis_geometry_collection(gis::srid_t srid,wkbType gtype,const String * gbuf,String * gcbuf)3239 Gis_geometry_collection::Gis_geometry_collection(gis::srid_t srid,
3240 wkbType gtype,
3241 const String *gbuf,
3242 String *gcbuf)
3243 : Geometry(nullptr, 0, Flags_t(wkb_geometrycollection, 0), srid) {
3244 uint32 geo_len = gbuf ? gbuf->length() : 0, total_len = 0;
3245 DBUG_ASSERT(
3246 (gbuf == nullptr || (gbuf->ptr() == nullptr && gbuf->length() == 0)) ||
3247 (gbuf->ptr() != nullptr && gbuf->length() > 0));
3248 total_len = geo_len + sizeof(uint32) /*NUM-objs*/ + SRID_SIZE +
3249 WKB_HEADER_SIZE + (geo_len > 0 ? WKB_HEADER_SIZE : 0);
3250
3251 // Reserve 512 bytes extra space for geometries to be appended later,
3252 // to avoid some reallocations.
3253 if (gcbuf->reserve(total_len + 512, 1024))
3254 my_error(ER_OUTOFMEMORY, total_len + 512);
3255
3256 char *ptr = gcbuf->ptr();
3257 const char *start = ptr + GEOM_HEADER_SIZE;
3258
3259 ptr = write_geometry_header(ptr, srid, Geometry::wkb_geometrycollection,
3260 geo_len ? 1 : 0);
3261 if (geo_len > 0) {
3262 ptr = write_wkb_header(ptr, gtype);
3263 memcpy(ptr, gbuf->ptr(), geo_len);
3264 }
3265
3266 gcbuf->length(total_len);
3267 set_data_ptr(start, total_len - GEOM_HEADER_SIZE);
3268 set_srid(srid);
3269 has_geom_header_space(true);
3270 }
3271
3272 /**
3273 Create a geometry collection from a single geometry, and this object refer
3274 to position right after the WKB header inside the 'gcbuf' buffer.
3275 @param geo the first valid geometry to put into this geometry collection.
3276 Its SRID is used as the SRID of this geometry collection. It must be
3277 a valid geometry.
3278 @param gcbuf this geometry collection's data buffer in GEOMETRY format.
3279 */
Gis_geometry_collection(Geometry * geo,String * gcbuf)3280 Gis_geometry_collection::Gis_geometry_collection(Geometry *geo, String *gcbuf)
3281 : Geometry(nullptr, 0, Flags_t(wkb_geometrycollection, 0),
3282 geo->get_srid()) {
3283 DBUG_ASSERT(geo != nullptr && geo->get_ptr() != nullptr);
3284 uint32 geo_len = geo->get_data_size(), total_len = 0;
3285 DBUG_ASSERT(geo_len != GET_SIZE_ERROR);
3286 total_len =
3287 geo_len + sizeof(uint32) /*NUM-objs*/ + SRID_SIZE + WKB_HEADER_SIZE * 2;
3288
3289 // Reserve 512 bytes extra space for geometries to be appended later,
3290 // to avoid some reallocations.
3291 if (gcbuf->reserve(total_len + 512, 1024))
3292 my_error(ER_OUTOFMEMORY, total_len + 512);
3293
3294 char *ptr = gcbuf->ptr();
3295 const char *start = ptr + GEOM_HEADER_SIZE;
3296
3297 ptr = write_geometry_header(ptr, geo->get_srid(),
3298 Geometry::wkb_geometrycollection, 1);
3299 ptr = write_wkb_header(ptr, geo->get_type());
3300
3301 memcpy(ptr, geo->get_data_ptr(), geo_len);
3302 gcbuf->length(total_len);
3303 set_data_ptr(start, total_len - GEOM_HEADER_SIZE);
3304 set_srid(geo->get_srid());
3305 has_geom_header_space(true);
3306 }
3307
get_data_size() const3308 uint32 Gis_geometry_collection::get_data_size() const {
3309 if (check_stack_overrun(current_thd, STACK_MIN_SIZE, nullptr))
3310 return GET_SIZE_ERROR;
3311
3312 uint32 n_objects = 0;
3313 uint32 len;
3314 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
3315 Geometry_buffer buffer;
3316 Geometry *geom;
3317
3318 if (is_length_verified()) return get_nbytes();
3319 /*
3320 We allow a geometry collection of 0 components, because this is how we
3321 define an 'empty collection', which is used as the result of set operation
3322 that returns an empty result, it's different from NULL value just in the
3323 same way an empty string is different from a NULL.
3324 */
3325 if (wkb.scan_non_zero_uint4(&n_objects) && n_objects != 0)
3326 return GET_SIZE_ERROR;
3327
3328 while (n_objects--) {
3329 if (!(geom = scan_header_and_create(&wkb, &buffer))) return GET_SIZE_ERROR;
3330
3331 uint32 object_size;
3332 /*
3333 'geom' is a temporary object whose length may be wrongly specified by
3334 scan_header_and_create() function, so here we don't require
3335 object_size + GEOM_HEADER_SIZE == wkb->length()
3336 */
3337 if ((object_size = geom->get_data_size()) == GET_SIZE_ERROR)
3338 return GET_SIZE_ERROR;
3339
3340 /*
3341 Use 'skip()' instead of 'skip_unsafe()' in case the object size is
3342 incorrect
3343 */
3344 if (wkb.skip(object_size)) {
3345 DBUG_ASSERT(false); // geom-get_data_size() did something wrong.
3346 return GET_SIZE_ERROR;
3347 }
3348 }
3349 len = static_cast<uint32>(wkb.data() - (const char *)get_data_ptr());
3350 if (len != get_nbytes()) set_nbytes(len);
3351 set_length_verified(true);
3352 return len;
3353 }
3354
init_from_wkt(Gis_read_stream * trs,String * wkb)3355 bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb) {
3356 if (check_stack_overrun(trs->thd(), STACK_MIN_SIZE, nullptr)) return true;
3357
3358 uint32 n_objects = 0;
3359 uint32 no_pos = wkb->length();
3360 Geometry_buffer buffer;
3361 Geometry *g;
3362
3363 if (wkb->reserve(4, 512)) return true;
3364 wkb->length(wkb->length() + 4); // Reserve space for points
3365
3366 if (trs->get_next_toc_type() == Gis_read_stream::word) {
3367 LEX_CSTRING empty;
3368 if (trs->get_next_word(&empty) || empty.length != 5 ||
3369 native_strncasecmp("EMPTY", empty.str, 5))
3370 return true;
3371 } else {
3372 if (trs->check_next_symbol('(')) return true;
3373 for (;;) {
3374 /*
3375 Allow specifying an empty geometry collection in this form:
3376 'geometrycollection()'.
3377 */
3378 if (n_objects == 0 && trs->get_next_toc_type() == Gis_read_stream::r_bra)
3379 break;
3380 if (!(g = create_from_wkt(&buffer, trs, wkb, true,
3381 false /* Allow trailing bytes. */)))
3382 return true;
3383 /*
3384 Allow g to be a nested geometry collection, nested ones are flatterned
3385 in BG_geometry_collection before sending to new BG based GIS algorithms.
3386 */
3387 n_objects++;
3388 if (trs->skip_char(',')) // Didn't find ','
3389 break;
3390 }
3391 if (trs->check_next_symbol(')')) return true;
3392 }
3393
3394 write_at_position(no_pos, n_objects, wkb);
3395 return false;
3396 }
3397
init_from_wkb(THD * thd,const char * wkb,uint len,wkbByteOrder bo,String * res)3398 uint Gis_geometry_collection::init_from_wkb(THD *thd, const char *wkb, uint len,
3399 wkbByteOrder bo, String *res) {
3400 if (check_stack_overrun(thd, STACK_MIN_SIZE, nullptr)) return 0;
3401
3402 uint32 n_geom = 0;
3403 const char *wkb_orig = wkb;
3404
3405 if (len < 4) return 0;
3406 n_geom = wkb_get_uint(wkb, bo);
3407
3408 if (res->reserve(4, 512)) return 0;
3409 q_append(n_geom, res);
3410
3411 wkb += 4;
3412 len -= 4;
3413
3414 /* Allow 0 components as an empty collection. */
3415 while (n_geom--) {
3416 Geometry_buffer buffer;
3417 Geometry *geom;
3418 uint g_len = 0;
3419 uint32 wkb_type;
3420
3421 if (len < WKB_HEADER_SIZE || (*wkb != wkb_xdr && *wkb != wkb_ndr) ||
3422 res->reserve(WKB_HEADER_SIZE, 512))
3423 return 0;
3424
3425 wkb_type = wkb_get_uint(wkb + 1, (wkbByteOrder)wkb[0]);
3426 write_wkb_header(res, static_cast<wkbType>(wkb_type));
3427
3428 if (!(geom = create_by_typeid(&buffer, wkb_type)) ||
3429 !(g_len = geom->init_from_wkb(thd, wkb + WKB_HEADER_SIZE,
3430 len - WKB_HEADER_SIZE,
3431 (wkbByteOrder)wkb[0], res)))
3432 return 0;
3433 g_len += WKB_HEADER_SIZE;
3434 wkb += g_len;
3435 DBUG_ASSERT(len >= g_len);
3436 len -= g_len;
3437 }
3438 return (uint)(wkb - wkb_orig);
3439 }
3440
get_data_as_wkt(String * txt,wkb_parser * wkb) const3441 bool Gis_geometry_collection::get_data_as_wkt(String *txt,
3442 wkb_parser *wkb) const {
3443 uint32 n_objects = 0;
3444 Geometry_buffer buffer;
3445 Geometry *geom;
3446 size_t nback = 1;
3447
3448 /* Allow 0 components as an empty collection. */
3449 if (wkb->scan_non_zero_uint4(&n_objects) && n_objects != 0) return true;
3450
3451 if (n_objects == 0) {
3452 txt->append(STRING_WITH_LEN(" EMPTY"));
3453 } else {
3454 txt->append('(');
3455 while (n_objects--) {
3456 if (!(geom = scan_header_and_create(wkb, &buffer)) ||
3457 geom->as_wkt(txt, wkb) || txt->append(STRING_WITH_LEN(","), 512))
3458 return true;
3459 }
3460 txt->length(txt->length() - nback);
3461 txt->append(')');
3462 }
3463 return false;
3464 }
3465
get_mbr(MBR * mbr,wkb_parser * wkb) const3466 bool Gis_geometry_collection::get_mbr(MBR *mbr, wkb_parser *wkb) const {
3467 uint32 n_objects;
3468 Geometry_buffer buffer;
3469 Geometry *geom;
3470
3471 /* An empty collection's MBR is NULL. */
3472 if (wkb->scan_non_zero_uint4(&n_objects)) return true;
3473
3474 bool found_one = false;
3475 while (n_objects--) {
3476 if (!(geom = scan_header_and_create(wkb, &buffer)) ||
3477 geom->get_mbr(mbr, wkb)) {
3478 /*
3479 An empty collection should be simply skipped, it may contain a tree
3480 of empty collections which is still empty.
3481 */
3482 if (geom != nullptr && geom->get_type() == wkb_geometrycollection)
3483 continue;
3484 return true;
3485 }
3486
3487 // Now we've found a solid component and updated the mbr.
3488 found_one = true;
3489 }
3490
3491 /* An collection containing only a few empty collections, the MBR is NULL. */
3492 if (!found_one) return true;
3493 return false;
3494 }
3495
num_geometries(uint32 * num) const3496 int Gis_geometry_collection::num_geometries(uint32 *num) const {
3497 *num = 0;
3498 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
3499 /* Should return 0 and *num=0 if called with an empty collection. */
3500 return (wkb.scan_non_zero_uint4(num) && *num != 0) ? 1 : 0;
3501 }
3502
geometry_n(uint32 num,String * result) const3503 int Gis_geometry_collection::geometry_n(uint32 num, String *result) const {
3504 uint32 n_objects, length;
3505 wkb_parser wkb(get_cptr(), get_cptr() + get_nbytes());
3506 Geometry_buffer buffer;
3507 Geometry *geom;
3508
3509 /* It's an error to call this with an empty collection. */
3510 if (wkb.scan_non_zero_uint4(&n_objects)) return 1;
3511
3512 if (num > n_objects || num < 1) return 1;
3513
3514 wkb_header header;
3515 do {
3516 if (wkb.scan_wkb_header(&header) ||
3517 !(geom = create_by_typeid(&buffer, header.wkb_type)))
3518 return 1;
3519
3520 // Must set precise length for points even for a temporary/intermediate one.
3521 if (geom->get_type() == wkb_point)
3522 geom->set_data_ptr(wkb.data(), POINT_DATA_SIZE);
3523 else
3524 geom->set_data_ptr(&wkb);
3525
3526 if ((length = geom->get_data_size()) == GET_SIZE_ERROR) return 1;
3527 wkb.skip_unsafe(length);
3528 } while (--num);
3529
3530 /* Copy found object to result */
3531 if (result->reserve(1 + 4 + length, 512)) return 1;
3532 q_append((char)wkb_ndr, result);
3533 q_append(header.wkb_type, result);
3534 q_append(wkb.data() - length, length, result); // data-length= start_of_data
3535 return 0;
3536 }
3537
3538 /*
3539 Return dimension for object
3540
3541 SYNOPSIS
3542 dimension()
3543 res_dim Result dimension
3544 end End of object will be stored here. May be 0 for
3545 simple objects!
3546 RETURN
3547 0 ok
3548 1 error
3549 */
3550
dimension(uint32 * res_dim,wkb_parser * wkb) const3551 bool Gis_geometry_collection::dimension(uint32 *res_dim,
3552 wkb_parser *wkb) const {
3553 uint32 n_objects;
3554 Geometry_buffer buffer;
3555 Geometry *geom;
3556
3557 if (wkb->scan_non_zero_uint4(&n_objects)) return true;
3558
3559 *res_dim = 0;
3560 while (n_objects--) {
3561 uint32 dim;
3562 if (!(geom = scan_header_and_create(wkb, &buffer)) ||
3563 geom->dimension(&dim, wkb))
3564 return true;
3565 *res_dim = std::max(*res_dim, dim);
3566 }
3567 return false;
3568 }
3569
reverse_coordinates()3570 bool Gis_geometry_collection::reverse_coordinates() {
3571 uint32 num_of_geometries;
3572 size_t current_data_offset =
3573 4; // Add num_of_geometries header size to offset.
3574
3575 String str(get_cptr(), get_nbytes(), &my_charset_bin);
3576
3577 if (num_geometries(&num_of_geometries)) {
3578 return true;
3579 }
3580
3581 for (uint32 i = 1; i <= num_of_geometries; i++) {
3582 String result;
3583
3584 if (geometry_n(i, &result)) {
3585 return true;
3586 }
3587
3588 Geometry *g;
3589 Geometry_buffer buffer;
3590 if (!(g = Geometry::construct(&buffer, &result, false))) {
3591 return true;
3592 }
3593
3594 if (g->reverse_coordinates()) {
3595 return true;
3596 }
3597
3598 if (str.replace(current_data_offset, result.length(), result.ptr(),
3599 result.length())) {
3600 return true;
3601 }
3602
3603 current_data_offset += result.length();
3604 }
3605
3606 return false;
3607 }
3608
validate_coordinate_range(double srs_angular_unit,bool * long_out_of_range,bool * lat_out_of_range,double * out_of_range_value)3609 bool Gis_geometry_collection::validate_coordinate_range(
3610 double srs_angular_unit, bool *long_out_of_range, bool *lat_out_of_range,
3611 double *out_of_range_value) {
3612 uint32 num_of_geometries;
3613 *long_out_of_range = false;
3614 *lat_out_of_range = false;
3615
3616 if (num_geometries(&num_of_geometries)) {
3617 return true; /* purecov: inspected */
3618 }
3619
3620 for (uint32 i = 1; i <= num_of_geometries; i++) {
3621 String result;
3622
3623 if (geometry_n(i, &result)) {
3624 return true; /* purecov: inspected */
3625 }
3626
3627 Geometry *g;
3628 Geometry_buffer buffer;
3629 if (!(g = Geometry::construct(&buffer, &result, false))) {
3630 return true; /* purecov: inspected */
3631 }
3632
3633 if (g->validate_coordinate_range(srs_angular_unit, long_out_of_range,
3634 lat_out_of_range, out_of_range_value)) {
3635 return true;
3636 }
3637 }
3638
3639 return false;
3640 }
3641
get_class_info() const3642 const Geometry::Class_info *Gis_geometry_collection::get_class_info() const {
3643 return &geometrycollection_class;
3644 }
3645
3646 /************************* Stepper classes ****************************/
3647
3648 /**
3649 Base class of all WKB parsers, which parse different types of geometries
3650 properly. All these classes assume the WKB input is valid and complete.
3651 */
3652 class Stepper_base {
3653 public:
3654 /**
3655 Constructor.
3656 @param dim dimension of points in the geometry to be stepped
3657 over(i.e. current geometry).
3658 @param bo current geometry's byte order
3659 @param has_wkb_hdr true for stepping through geometries within multiXXX and
3660 geometrycollection, false for other geometries.
3661 @param geotype current geometry's type
3662 */
Stepper_base(char dim,Geometry::wkbByteOrder bo,bool has_wkb_hdr,Geometry::wkbType geotype)3663 Stepper_base(char dim, Geometry::wkbByteOrder bo, bool has_wkb_hdr,
3664 Geometry::wkbType geotype) {
3665 m_dim = dim;
3666 DBUG_ASSERT(bo == Geometry::wkb_ndr);
3667 m_bo = bo;
3668 m_has_wkb_hdr = has_wkb_hdr;
3669 m_geotype = geotype;
3670 }
3671
get_current_byte_order() const3672 Geometry::wkbByteOrder get_current_byte_order() const {
3673 DBUG_ASSERT((m_bo == Geometry::wkb_xdr || m_bo == Geometry::wkb_ndr));
3674 return m_bo;
3675 }
3676
get_current_geotype() const3677 Geometry::wkbType get_current_geotype() const {
3678 DBUG_ASSERT(Geometry::is_valid_geotype(m_geotype));
3679 return m_geotype;
3680 }
3681
3682 protected:
3683 /// Current geometry dimension.
3684 char m_dim;
3685 /// Current geometry has a WKB header or not.
3686 bool m_has_wkb_hdr;
3687 /// Current geometry's byte order.
3688 Geometry::wkbByteOrder m_bo;
3689 /// Current geometry's type, e.g. polygon, linestring, etc.
3690 Geometry::wkbType m_geotype;
3691 };
3692
3693 /**
3694 For iterating points inside multipoint and linestring.
3695 Expected multipoint format: NUM-pts|WKB-HDR1 pt1|WKB-HDR2 pt2|...|WKB-HDRn ptn
3696 Expected linestring format doesn't have the WKB headers.
3697 */
3698 class Point_stepper : public Stepper_base {
3699 public:
Point_stepper(char dim,Geometry::wkbByteOrder bo,bool has_wkb_hdr)3700 Point_stepper(char dim, Geometry::wkbByteOrder bo, bool has_wkb_hdr)
3701 : Stepper_base(dim, bo, has_wkb_hdr, Geometry::wkb_point) {}
3702
3703 const char *operator()(const char *p);
3704 };
3705
3706 /**
3707 For iterating linestrings inside multilinestring and polygon.
3708 Expected multilinestring format: NUM-ls|WKB-HDR1 ls1|WKB-HDR2 ls2|....
3709 Expected polygon format doesn't have the WKB headers, and the 1st one is
3710 exterior ring, following if any are interior rings.
3711
3712 In both cases, the linestrX is of linestring format, with no WKB header
3713 in its each point.
3714 */
3715 class Linestring_stepper : public Stepper_base {
3716 public:
Linestring_stepper(char dim,Geometry::wkbByteOrder bo,bool has_wkb_hdr)3717 Linestring_stepper(char dim, Geometry::wkbByteOrder bo, bool has_wkb_hdr)
3718 : Stepper_base(dim, bo, has_wkb_hdr, Geometry::wkb_linestring) {}
3719
3720 const char *operator()(const char *p);
3721 };
3722
3723 /**
3724 For iterating polygons inside multipolygon or geometry collection.
3725 Expected multipolygon format: NUM-plgns|WKB-HDR1 plgn1|WKB-HDR2 plgn2|...
3726 This is also expected format for geometry collection.
3727 In both cases inside polygonX there is no more WKB headers.
3728 */
3729 class Polygon_stepper : public Stepper_base {
3730 public:
Polygon_stepper(char dim,Geometry::wkbByteOrder bo,bool has_wkb_hdr)3731 Polygon_stepper(char dim, Geometry::wkbByteOrder bo, bool has_wkb_hdr)
3732 : Stepper_base(dim, bo, has_wkb_hdr, Geometry::wkb_polygon) {}
3733
3734 const char *operator()(const char *p);
3735 };
3736
3737 /// Parsing operator. Note that the returned pointer may point past end of
3738 /// WKB string, and caller is responsible for stoping reading after last
3739 /// geometry is read, this is true for all parsing operator of all stepper
3740 /// classes.
3741 /// @param p points to the 1st byte of a point's wkb data, right after its
3742 /// wkb header if any; returns the next point's wkb data's 1st byte pointer,
3743 /// skipping its wkb header if any.
operator ()(const char * p)3744 const char *Point_stepper::operator()(const char *p) {
3745 p += SIZEOF_STORED_DOUBLE * m_dim;
3746
3747 // m_bo is latest byte order, which allows mixed byte orders in the same
3748 // wkb byte string.
3749 if (m_has_wkb_hdr) {
3750 Geometry::wkbByteOrder bo = get_byte_order(p);
3751
3752 // The next one can be other geo types, in a geometry collection.
3753 m_geotype = get_wkb_geotype(p + 1);
3754
3755 if (m_bo != bo) m_bo = bo;
3756 p += WKB_HEADER_SIZE;
3757 }
3758
3759 return p;
3760 }
3761
3762 /// Parsing operator.
3763 /// @param p points to the 1st byte of a linestring's wkb data, right after
3764 /// its wkb header if any;
3765 /// @return the next linestring's wkb data's 1st
3766 /// byte pointer, skipping its wkb header if any.
operator ()(const char * p)3767 const char *Linestring_stepper::operator()(const char *p) {
3768 uint32 npts = 0;
3769
3770 npts = uint4korr(p);
3771 p += sizeof(uint32);
3772 p += npts * SIZEOF_STORED_DOUBLE * m_dim;
3773
3774 // The m_bo is latest byte order, which allows mixed byte orders in the same
3775 // wkb byte string.
3776 if (m_has_wkb_hdr) {
3777 Geometry::wkbByteOrder bo = get_byte_order(p);
3778
3779 // The next one can be other geo types, in a geometry collection.
3780 m_geotype = get_wkb_geotype(p + 1);
3781
3782 if (m_bo != bo) m_bo = bo;
3783 p += WKB_HEADER_SIZE; // skip the wkb header if any
3784 }
3785
3786 return p;
3787 }
3788
3789 /// Parsing operator.
3790 /// @param p points to the 1st byte of a polygon's wkb data, right after its
3791 /// wkb header if any;
3792 /// @return the next polygon's wkb data's 1st byte pointer,
3793 /// skipping its wkb header if any.
operator ()(const char * p)3794 const char *Polygon_stepper::operator()(const char *p) {
3795 uint32 nls = 0;
3796
3797 // We pass false because a multilinestring's points don't have
3798 // wkb headers(5 bytes).
3799 Linestring_stepper lsstepper(m_dim, m_bo, false);
3800
3801 nls = uint4korr(p);
3802 p += sizeof(uint32);
3803
3804 for (uint32 i = 0; i < nls; i++) p = lsstepper(p);
3805
3806 // m_bo is latest byte order, which allows mixed byte orders in the same
3807 // wkb byte string.
3808 DBUG_ASSERT(m_has_wkb_hdr);
3809 Geometry::wkbByteOrder bo = get_byte_order(p);
3810
3811 // The next one can be other geo types, in a geometry collection.
3812 m_geotype = get_wkb_geotype(p + 1);
3813
3814 if (m_bo != bo) m_bo = bo;
3815 p += WKB_HEADER_SIZE; // skip the wkb header if any
3816
3817 return p;
3818 }
3819
3820 /**
3821 Get inner rings object from a geometry. Internally check that the argument
3822 is a polygon. This function is intended as a helper function and is called
3823 where we don't convert to a polygon pointer although it is a polygon.
3824
3825 @param g a geometry that must be a polygon.
3826 @return the polygon's inner rings object.
3827 */
3828 // SUPPRESS_UBSAN Wrong downcast. FIXME
3829 static inline Gis_polygon::inner_container_type *inner_rings(const Geometry *g)
3830 SUPPRESS_UBSAN;
inner_rings(const Geometry * g)3831 static inline Gis_polygon::inner_container_type *inner_rings(
3832 const Geometry *g) {
3833 DBUG_ASSERT(g->get_geotype() == Geometry::wkb_polygon);
3834 const Gis_polygon *p = static_cast<const Gis_polygon *>(g);
3835 return p->inner_rings();
3836 }
3837
3838 /**
3839 Set inner rings object to a geometry. Internally check that the argument
3840 is a polygon. This function is intended as a helper function and is called
3841 where we don't convert to a polygon pointer although it is a polygon.
3842
3843 @param g a geometry that must be a polygon.
3844 @param inns The interior rings
3845 */
3846 // SUPPRESS_UBSAN Wrong downcast. FIXME
3847 static inline void set_inner_rings(
3848 Geometry *g, Gis_polygon::inner_container_type *inns) SUPPRESS_UBSAN;
set_inner_rings(Geometry * g,Gis_polygon::inner_container_type * inns)3849 static inline void set_inner_rings(Geometry *g,
3850 Gis_polygon::inner_container_type *inns) {
3851 DBUG_ASSERT(g->get_geotype() == Geometry::wkb_polygon);
3852 Gis_polygon *p = static_cast<Gis_polygon *>(g);
3853 p->set_inner_rings(inns);
3854 }
3855
3856 /// Parse the wkb buffer to build the component vector m_geo_vect for geom.
3857 /// Set each geometry's wkb pointer into the Geometry objects inside m_geo_vect.
3858 /// Make it a standalone function in order to be able to access classes defined
3859 /// after class template Gis_wkb_vector.
3860 /// @param geom the geometry to analyze and parse.
3861 /// @param p points to the geometry's wkb data's 1st byte, right after its
3862 /// wkb header if any.
3863 /// @param num_geoms number of following geometries, to be used only when
3864 /// parsing the WKB of a polygon's inner rings because there is no WKB header
3865 /// for the inner rings only.
parse_wkb_data(Geometry * geom,const char * p,size_t num_geoms)3866 void parse_wkb_data(Geometry *geom, const char *p, size_t num_geoms) {
3867 const char *q = nullptr;
3868 size_t nbytes = 0;
3869 Geometry::wkbType geotype = geom->get_geotype();
3870 Geometry::wkbByteOrder mybo = geom->get_byte_order();
3871 char dim = geom->get_dimension();
3872
3873 DBUG_ASSERT(geotype != Geometry::wkb_polygon_inner_rings ||
3874 (geotype == Geometry::wkb_polygon_inner_rings && num_geoms != 0));
3875 geom->set_bg_adapter(true);
3876 if (p == nullptr) return;
3877
3878 switch (geotype) {
3879 case Geometry::wkb_point:
3880 // Point doesn't need this vector.
3881 DBUG_ASSERT(false);
3882 break;
3883 case Geometry::wkb_linestring: {
3884 uint32 npts = uint4korr(p);
3885 p += sizeof(uint32);
3886 Point_stepper ptstep(dim, mybo, false);
3887 const char *first = nullptr, *last = nullptr;
3888
3889 for (uint32 i = 0; i < npts; i++) {
3890 q = p;
3891 if (i == 0) first = p;
3892 if (i < npts - 1) {
3893 p = ptstep(p);
3894 nbytes = p - q;
3895 } else {
3896 nbytes = geom->get_cptr() + geom->get_nbytes() - p;
3897 last = p;
3898 }
3899
3900 DBUG_ASSERT(nbytes == dim * SIZEOF_STORED_DOUBLE);
3901 // Construct the geometry object as below to avoid unncesarrily
3902 // parsing its WKB data. Parsing will be done in shallow_copy.
3903 Gis_point ent;
3904 ent.Geometry::set_ptr(q);
3905 ent.set_nbytes(nbytes);
3906 ent.set_owner(geom);
3907 geom->shallow_push(&ent);
3908 }
3909
3910 /*
3911 Historically MySQL GIS require closed polygon rings from user input,
3912 and from now (5.7.4) on we close a polygon ring if it's open before
3913 storing/using it. So we should never see any open rings here.
3914
3915 However we can't compare memory because the two points may have tiny
3916 differences due to computing deviations.
3917 */
3918
3919 // Fix compiler warnings.
3920 first = last;
3921 last = first;
3922 break;
3923 }
3924 case Geometry::wkb_multipoint: {
3925 uint32 npts = uint4korr(p);
3926 p += sizeof(uint32);
3927
3928 Geometry::wkbByteOrder bo = ::get_byte_order(p);
3929 DBUG_ASSERT(get_wkb_geotype(p + 1) == Geometry::wkb_point);
3930 p += WKB_HEADER_SIZE;
3931
3932 Point_stepper ptstep(dim, bo, true);
3933
3934 for (uint32 i = 0; i < npts; i++) {
3935 q = p;
3936 if (i < npts - 1) {
3937 p = ptstep(p);
3938 nbytes = p - q - WKB_HEADER_SIZE;
3939 } else
3940 nbytes = geom->get_cptr() + geom->get_nbytes() - p;
3941
3942 // Construct the geometry object as below to avoid unncesarrily
3943 // parsing its WKB data. Parsing will be done in shallow_copy.
3944 Gis_point ent;
3945 ent.Geometry::set_ptr(q);
3946 ent.set_nbytes(nbytes);
3947 ent.set_owner(geom);
3948 geom->shallow_push(&ent);
3949 bo = ptstep.get_current_byte_order();
3950 DBUG_ASSERT(ptstep.get_current_geotype() == Geometry::wkb_point);
3951 }
3952
3953 break;
3954 }
3955 case Geometry::wkb_multilinestring: {
3956 uint32 nls = uint4korr(p);
3957 p += sizeof(uint32);
3958
3959 Geometry::wkbByteOrder bo = ::get_byte_order(p);
3960 DBUG_ASSERT(get_wkb_geotype(p + 1) == Geometry::wkb_linestring);
3961 p += WKB_HEADER_SIZE;
3962 Linestring_stepper lsstep(dim, bo, true);
3963
3964 for (uint32 i = 0; i < nls; i++) {
3965 q = p;
3966 if (i < nls - 1) {
3967 p = lsstep(p);
3968 nbytes = p - q - WKB_HEADER_SIZE;
3969 } else
3970 nbytes = geom->get_cptr() + geom->get_nbytes() - p;
3971
3972 // Construct the geometry object as below to avoid unncesarrily
3973 // parsing its WKB data. Parsing will be done in shallow_copy.
3974 Gis_line_string ent;
3975 ent.Geometry::set_ptr(q);
3976 ent.set_nbytes(nbytes);
3977 ent.set_owner(geom);
3978 geom->shallow_push(&ent);
3979 bo = lsstep.get_current_byte_order();
3980 DBUG_ASSERT(lsstep.get_current_geotype() == Geometry::wkb_linestring);
3981 }
3982
3983 break;
3984 }
3985 case Geometry::wkb_polygon_inner_rings: {
3986 /*
3987 There is no independent WKT for inner rings to parse. Inner rings is
3988 a component of a polygon thus can't have number of rings in its WKB.
3989 When inner rings are parsed here, it must already have been filled,
3990 and we are calling this in methods like push_back, etc,
3991 thus m_geo_vect has the right number of rings.
3992 */
3993 size_t nls = num_geoms;
3994 Linestring_stepper lsstep(dim, mybo, false);
3995
3996 for (size_t i = 0; i < nls; i++) {
3997 q = p;
3998 if (i < nls - 1) {
3999 p = lsstep(p);
4000 nbytes = p - q;
4001 } else
4002 nbytes = geom->get_cptr() + geom->get_nbytes() - p;
4003
4004 // Construct the geometry object as below to avoid unncesarrily
4005 // parsing its WKB data. Parsing will be done in shallow_copy.
4006 Gis_polygon_ring ent;
4007 ent.Geometry::set_ptr(q);
4008 ent.set_nbytes(nbytes);
4009 ent.set_owner(geom);
4010 geom->shallow_push(&ent);
4011 }
4012
4013 break;
4014 }
4015 case Geometry::wkb_polygon: {
4016 uint32 nls = uint4korr(p);
4017 const char *start = p;
4018
4019 p += sizeof(uint32);
4020
4021 Linestring_stepper lsstep(dim, mybo, false);
4022
4023 for (uint32 i = 0; i < nls; i++) {
4024 q = p;
4025 if (i < nls - 1) {
4026 p = lsstep(p);
4027 nbytes = p - q;
4028 } else
4029 nbytes = start + geom->get_nbytes() - p;
4030
4031 if (i == 0) {
4032 // Parse outer ring.
4033 Gis_polygon_ring *outer = outer_ring(geom);
4034 if (geom->get_ptr() == nullptr) {
4035 outer = new Gis_polygon_ring(
4036 q, nbytes, Geometry::Flags_t(Geometry::wkb_linestring, 0),
4037 geom->get_srid());
4038 outer->set_props(Geometry::POLYGON_OUTER_RING);
4039 geom->Geometry::set_ptr(outer);
4040 } else {
4041 outer->set_ptr(const_cast<char *>(q), nbytes);
4042 outer->set_byte_order(mybo);
4043 outer->set_dimension(dim);
4044 outer->set_geotype(Geometry::wkb_linestring);
4045 }
4046
4047 outer->set_owner(geom);
4048 /*
4049 When parsing data to get polygon, the memory is a continuous chunk
4050 belonging to the owner, or it's an existing WKB buffer. Also true
4051 for below inner rings.
4052 */
4053 outer->set_ownmem(false);
4054 } else {
4055 Gis_polygon::inner_container_type *inners = inner_rings(geom);
4056
4057 if (inners == nullptr) {
4058 inners = new Gis_polygon::inner_container_type();
4059 set_inner_rings(geom, inners);
4060 inners->set_byte_order(mybo);
4061 inners->set_dimension(dim);
4062 inners->set_geotype(Geometry::wkb_polygon_inner_rings);
4063 inners->set_owner(geom);
4064 inners->set_geo_vect(new Geometry_vector<Gis_polygon_ring>());
4065 inners->set_ptr(const_cast<char *>(q), 0 /* Accumulated below. */);
4066 inners->set_ownmem(false);
4067 }
4068
4069 // Construct the geometry object as below to avoid unncesarrily
4070 // parsing its WKB data. Parsing will be done in shallow_push.
4071 Gis_polygon_ring ent;
4072 ent.Geometry::set_ptr(q);
4073 ent.set_nbytes(nbytes);
4074 ent.set_owner(inners);
4075 ent.set_props(Geometry::POLYGON_INNER_RING);
4076 inners->shallow_push(&ent);
4077 inners->set_nbytes(inners->get_nbytes() + nbytes);
4078 }
4079 }
4080 geom->polygon_is_wkb_form(false);
4081
4082 break;
4083 }
4084 case Geometry::wkb_multipolygon: {
4085 uint32 nplgns = uint4korr(p);
4086 p += sizeof(uint32);
4087
4088 Geometry::wkbByteOrder bo = ::get_byte_order(p);
4089 DBUG_ASSERT(get_wkb_geotype(p + 1) == Geometry::wkb_polygon);
4090 p += WKB_HEADER_SIZE;
4091 Polygon_stepper plgn_step(dim, bo, true);
4092
4093 for (uint32 i = 0; i < nplgns; i++) {
4094 q = p;
4095 if (i < nplgns - 1) {
4096 p = plgn_step(p);
4097 nbytes = p - q - WKB_HEADER_SIZE;
4098 } else
4099 nbytes = geom->get_cptr() + geom->get_nbytes() - p;
4100
4101 /*
4102 Construct the geometry object as below to avoid unncesarrily
4103 parsing its WKB data. Parsing will be done in shallow_copy.
4104 */
4105 Gis_polygon ent;
4106 ent.Geometry::set_ptr(q);
4107 ent.set_nbytes(nbytes);
4108 ent.set_owner(geom);
4109 geom->shallow_push(&ent);
4110 // The object 'ent' doesn't have any data of its own.
4111 ent.donate_data();
4112 bo = plgn_step.get_current_byte_order();
4113 DBUG_ASSERT(plgn_step.get_current_geotype() == Geometry::wkb_polygon);
4114 }
4115
4116 break;
4117 }
4118 case Geometry::wkb_geometrycollection:
4119 /*
4120 We never create a Gis_wkb_vector using a geometry collection, because
4121 BG never uses such a type.
4122 */
4123 DBUG_ASSERT(false);
4124 break;
4125 default:
4126 DBUG_ASSERT(false);
4127 break;
4128 }
4129 }
4130
4131 /**
4132 In place normalize polygons' rings, making outer ring CCW and inner rings CW
4133 by reversing the ring's points in the WKB buffer inplace. This function can
4134 not be made a virtual function since BG adapter geometry objects may also
4135 need it.
4136
4137 @return the WKB buffer address of the geometry which contains the
4138 converted WKB data. If geometry data is invalid, returns NULL.
4139 */
normalize_ring_order()4140 const void *Geometry::normalize_ring_order() {
4141 Geometry *geo = this;
4142 bool inval = false;
4143
4144 if (geo->get_type() == Geometry::wkb_polygon) {
4145 Gis_polygon bgeo(geo->get_data_ptr(), geo->get_data_size(),
4146 geo->get_flags(), geo->get_srid());
4147 if (bgeo.set_polygon_ring_order()) inval = true;
4148 } else if (geo->get_type() == Geometry::wkb_multipolygon) {
4149 Gis_multi_polygon bgeo(geo->get_data_ptr(), geo->get_data_size(),
4150 geo->get_flags(), geo->get_srid());
4151
4152 for (size_t i = 0; i < bgeo.size(); i++)
4153 if (bgeo[i].set_polygon_ring_order()) {
4154 inval = true;
4155 break;
4156 }
4157 } else if (geo->get_type() == Geometry::wkb_geometrycollection) {
4158 /*
4159 This is impossible because BG doesn't use a geometry collection, and
4160 we can't create a Gis_wkb_vector<T> with a geometry collection.
4161 */
4162 DBUG_ASSERT(false);
4163 }
4164
4165 if (inval) return nullptr;
4166 return geo->get_data_ptr();
4167 }
4168
4169 /**
4170 Because of resize, a geometry's components may reside not in one chunk,
4171 some may in the m_ptr's chunk; others have their own memory and only exist
4172 in m_geo_vect vector, not in ptr's chunk. Also, a constructed polygon's
4173 data is always not in a chunk and needs to be so when it's pushed into a
4174 multipolygon/geometry collection.
4175 Thus in mysql before using the returned geometry, also inside the
4176 container classes before using the wkb data or clearing m_geo_vect,
4177 we need to make them inline, i.e. reside in one chunk of memory.
4178 Can only resize a topmost geometry, thus no recursive reassemling
4179 to do for now.
4180
4181 Algorithm:
4182
4183 Step 1. Structure analysis
4184
4185 Scan this geometry's components, see whether each of them has its own
4186 memory, if so it's 'out of line', otherwise it's 'inline'. Note down
4187 those owning memory in a map M1, for each entry X in the map M1, the
4188 component's index in the component vector m_geo_vect is used as key;
4189 The inline chunk of memory right before it which may have any number
4190 of inline components, and the inline chunk's start and end address pair
4191 is used as value of the inserted item X. If there is no inline chunk
4192 before the component, X's pointer range is (0, 0). The inline chunk's
4193 starting address is well maintained during the scan.
4194
4195
4196 Step 2. Reassembling
4197
4198 Allocate enough memory space (the length is accumulated in step 1) as WKB
4199 buffer and call it GBuf here, then copy the WKB of inline and out-of-line
4200 geometries into GBuf in original order:
4201 Go through the map by index order, for each item, copy the WKB chunk
4202 before it into the WKB buffer, then copy this out-of-line geometry's WKB
4203 into GBuf.
4204
4205 Special treatment of polygon: we have to pack its value and store their
4206 WKB separately into a map GP in step 1, and in step 2 for a polygon,
4207 get its WKB from GP, and at the end release WKB memory buffers held by
4208 items of GP.
4209 */
4210 template <typename T>
reassemble()4211 void Gis_wkb_vector<T>::reassemble() {
4212 set_bg_adapter(true);
4213 Geometry::wkbType geotype = get_geotype();
4214 if (geotype == Geometry::wkb_point || geotype == Geometry::wkb_polygon ||
4215 geotype == Geometry::wkb_multipoint || m_geo_vect == nullptr ||
4216 geotype == Geometry::wkb_linestring || m_geo_vect->size() == 0 ||
4217 !has_out_of_line_components())
4218 return;
4219
4220 if (m_geo_vect == nullptr) m_geo_vect = new Geo_vector;
4221 typedef std::map<size_t, std::pair<const char *, const char *>> segs_t;
4222 segs_t segs;
4223 size_t hdrsz = 0, num = m_geo_vect->size(), prev_in = 0, totlen = 0,
4224 segsz = 0;
4225 Geo_vector &vec = *m_geo_vect;
4226 const char *start = get_cptr(), *end = nullptr, *prev_start = get_cptr();
4227 std::map<size_t, std::pair<void *, size_t>> plgn_data;
4228 std::map<size_t, std::pair<void *, size_t>>::iterator plgn_data_itr;
4229 bool is_inns = (geotype == Geometry::wkb_polygon_inner_rings);
4230
4231 // True if just passed by a geometry having its own memory and not stored
4232 // inside owner's memory during the scan.
4233 bool out = false;
4234 if (geotype != Geometry::wkb_polygon_inner_rings) hdrsz = WKB_HEADER_SIZE;
4235
4236 uint32 none = 0; // Used when all components are out of line.
4237
4238 // Starting step one of the algorithm --- Structure Analysis.
4239 for (size_t i = 0; i < num; i++) {
4240 T *veci = &(vec[i]);
4241 // Polygons are always(almost) out of line. One with its own memory is
4242 // always out of line.
4243 if (veci->get_geotype() == Geometry::wkb_polygon || veci->get_ownmem()) {
4244 // In case of a polygon, see if it's already inline in a different
4245 // way from other types of geometries.
4246 if (veci->get_geotype() == Geometry::wkb_polygon &&
4247 polygon_is_packed(veci, this)) {
4248 if (out) {
4249 out = false;
4250 DBUG_ASSERT(prev_start == veci->get_ptr());
4251 }
4252 prev_in = i;
4253 continue;
4254 }
4255
4256 // Record the bytes before 1st geometry component.
4257 if (i == 0) {
4258 if (m_ptr) {
4259 start = get_cptr();
4260 end = start + sizeof(uint32) /* num geometrys*/;
4261 } else if (!is_inns) {
4262 start = reinterpret_cast<char *>(&none);
4263 end = start + sizeof(none);
4264 } else
4265 start = end = nullptr;
4266 }
4267 // The previous geometry is already out of line, or no m_ptr allocated.
4268 else if (out || !prev_start) {
4269 start = nullptr;
4270 end = nullptr;
4271 } else // The previous geometry is inline, note down the inline range.
4272 {
4273 start = prev_start;
4274 if (veci->get_geotype() == Geometry::wkb_polygon)
4275 end = get_packed_ptr(&(vec[prev_in])) + vec[prev_in].get_nbytes();
4276 else
4277 end = vec[prev_in].get_cptr() + vec[prev_in].get_nbytes();
4278 prev_start = end;
4279 // The 'end' points to the 1st byte of next geometry stored in its
4280 // owner's memory.
4281 }
4282
4283 if (veci->get_geotype() != Geometry::wkb_polygon) {
4284 // When this geometry is a geometry collection, we need to make its
4285 // components in one chunk first. Not gonna implement this yet since
4286 // BG doesn't use geometry collection yet, and consequently no
4287 // component can be a multipoint/multilinestring/multipolygon or a
4288 // geometrycollection. And multipoint components are already supported
4289 // so not forbidding them here.
4290 #if !defined(DBUG_OFF)
4291 Geometry::wkbType veci_gt = veci->get_geotype();
4292 #endif
4293 DBUG_ASSERT(veci_gt != wkb_geometrycollection &&
4294 veci_gt != wkb_multilinestring &&
4295 veci_gt != wkb_multipolygon);
4296 /* A point/multipoint/linestring is always in one memory chunk. */
4297 totlen += veci->get_nbytes() + hdrsz;
4298 } else {
4299 // Must be a polygon out of line.
4300 size_t nbytes = 0;
4301 void *plgn_base = get_packed_ptr(veci, &nbytes);
4302 DBUG_ASSERT(veci->get_nbytes() == 0 || veci->get_nbytes() == nbytes);
4303 veci->set_nbytes(nbytes);
4304 plgn_data.insert(std::make_pair(i, std::make_pair(plgn_base, nbytes)));
4305 totlen += nbytes + hdrsz;
4306 }
4307
4308 segs.insert(std::make_pair(i, std::make_pair(start, end)));
4309 out = true;
4310 } else {
4311 if (out) {
4312 out = false;
4313 DBUG_ASSERT(prev_start == veci->get_ptr());
4314 }
4315 prev_in = i;
4316 }
4317 }
4318
4319 segsz = segs.size();
4320 if (segsz == 0) {
4321 has_out_of_line_components(false);
4322 return;
4323 }
4324
4325 size_t nbytes = get_nbytes();
4326 DBUG_ASSERT((nbytes == 0 && m_ptr == nullptr && num == segsz) ||
4327 (nbytes > 0 && num >= segsz));
4328
4329 // If all are out of line, m_ptr is 0 and no room for ring count, otherwise
4330 // the space for ring count is already counted above.
4331 totlen += (nbytes ? nbytes : (is_inns ? 0 : sizeof(uint32)));
4332
4333 size_t len = 0, total_len = 0, last_i = 0, numgeoms = 0;
4334 // Allocate extra space as free space for the WKB buffer, and write it as
4335 // defined pattern.
4336 const size_t extra_wkb_free_space = 32;
4337 char *ptr = static_cast<char *>(gis_wkb_alloc(totlen + extra_wkb_free_space));
4338 // The header(object count) is already copied.
4339 char *q = ptr;
4340
4341 if (ptr == nullptr) {
4342 clear_wkb_data();
4343 m_ptr = nullptr;
4344 set_nbytes(0);
4345 set_ownmem(false);
4346 goto exit;
4347 }
4348 memset(ptr + totlen, 0xff, extra_wkb_free_space - 1);
4349 ptr[totlen + extra_wkb_free_space - 1] = '\0';
4350
4351 // Starting step two of the algorithm --- Reassembling.
4352 // Assemble the ins and outs into a single chunk.
4353 for (segs_t::iterator itr = segs.begin(); itr != segs.end(); ++itr) {
4354 size_t i = itr->first;
4355 start = itr->second.first;
4356 end = itr->second.second;
4357 const Geometry *veci = &(vec[i]);
4358 last_i = i;
4359
4360 // Copy the inline geometries before veci into buffer.
4361 if (start) {
4362 memcpy(q, start, len = end - start);
4363 q += len;
4364 total_len += len;
4365 }
4366
4367 // Set WKB header. This geometry must be one of multilinestring,
4368 // multipolygon or a polygon's inner rings.
4369 if (get_geotype() != Geometry::wkb_polygon_inner_rings) {
4370 q = write_wkb_header(q, veci->get_geotype());
4371 total_len += hdrsz;
4372 }
4373
4374 // Copy the out of line geometry into buffer. A polygon's data isn't
4375 // packed inside itself, we've packed it and recorded it in plgn_data.
4376 plgn_data_itr = plgn_data.find(i);
4377 if (veci->get_geotype() != Geometry::wkb_polygon) {
4378 DBUG_ASSERT(plgn_data_itr == plgn_data.end());
4379 len = veci->get_nbytes();
4380 memcpy(q, veci->get_ptr(), len);
4381 } else {
4382 DBUG_ASSERT(plgn_data_itr != plgn_data.end());
4383 len = plgn_data_itr->second.second;
4384 memcpy(q, plgn_data_itr->second.first, len);
4385 }
4386 q += len;
4387 total_len += len;
4388 }
4389
4390 // There may be trailing inline geometries to copy at old tail.
4391 if (last_i < vec.size() - 1) {
4392 len = get_cptr() + get_nbytes() - prev_start;
4393 memcpy(q, prev_start, len);
4394 total_len += len;
4395 }
4396 DBUG_ASSERT(total_len == totlen);
4397
4398 // Inner rings doesn't have ring count.
4399 if (!is_inns) {
4400 DBUG_ASSERT(segsz + uint4korr(ptr) <= 0xFFFFFFFF);
4401 int4store(reinterpret_cast<uchar *>(ptr),
4402 uint4korr(ptr) + static_cast<uint32>(segsz));
4403 }
4404
4405 numgeoms = m_geo_vect->size();
4406 clear_wkb_data();
4407 set_ptr(ptr, totlen);
4408 // An inner ring isn't parsed in set_ptr, has to parse separately since
4409 // we don't know its number of rings.
4410 if (is_inns) parse_wkb_data(this, get_cptr(), numgeoms);
4411 set_ownmem(true);
4412 exit:
4413 for (plgn_data_itr = plgn_data.begin(); plgn_data_itr != plgn_data.end();
4414 ++plgn_data_itr)
4415 gis_wkb_free(plgn_data_itr->second.first);
4416
4417 has_out_of_line_components(false);
4418 }
4419
4420 /// @brief Constructor.
4421 /// @param ptr points to the geometry's wkb data's 1st byte, right after its
4422 /// wkb header if any.
4423 /// @param nbytes the byte order indicated by @p ptr's wkb header.
4424 /// @param flags The geometry's flags
4425 /// @param srid The geometry's SRID
4426 /// @param is_bg_adapter Whether this object is created to be used by
4427 /// Boost Geometry, or to be only used in MySQL code.
4428 template <typename T>
Gis_wkb_vector(const void * ptr,size_t nbytes,const Flags_t & flags,gis::srid_t srid,bool is_bg_adapter)4429 Gis_wkb_vector<T>::Gis_wkb_vector(const void *ptr, size_t nbytes,
4430 const Flags_t &flags, gis::srid_t srid,
4431 bool is_bg_adapter)
4432 : Geometry(ptr, nbytes, flags, srid) {
4433 DBUG_ASSERT((ptr != nullptr && nbytes > 0) ||
4434 (ptr == nullptr && nbytes == 0));
4435 set_ownmem(false); // We use existing WKB data and don't own that memory.
4436 set_bg_adapter(is_bg_adapter);
4437 m_geo_vect = nullptr;
4438
4439 if (!is_bg_adapter) return;
4440
4441 std::unique_ptr<Geo_vector> guard;
4442
4443 wkbType geotype = get_geotype();
4444 // Points don't need it, polygon creates it when parsing.
4445 if (geotype != Geometry::wkb_point && geotype != Geometry::wkb_polygon &&
4446 ptr != nullptr)
4447 guard.reset(m_geo_vect = new Geo_vector());
4448 // For polygon parsing to work
4449 if (geotype == Geometry::wkb_polygon) m_ptr = nullptr;
4450
4451 // Why: wkb_polygon_inner_rings should parse in polygon as a whole.
4452 // Don't call get_cptr() here, it returns NULL.
4453 if (geotype != Geometry::wkb_polygon_inner_rings && ptr != nullptr)
4454 parse_wkb_data(this, static_cast<const char *>(ptr));
4455
4456 guard.release();
4457 }
4458
4459 template <typename T>
Gis_wkb_vector(const Gis_wkb_vector<T> & v)4460 Gis_wkb_vector<T>::Gis_wkb_vector(const Gis_wkb_vector<T> &v)
4461 : Geometry(v), m_geo_vect(nullptr) {
4462 DBUG_ASSERT(
4463 (v.get_ptr() != nullptr && v.get_nbytes() > 0) ||
4464 (v.get_ptr() == nullptr && !v.get_ownmem() && v.get_nbytes() == 0));
4465 if (!v.is_bg_adapter() || (v.get_ptr() == nullptr && v.m_geo_vect == nullptr))
4466 return;
4467 m_geo_vect = new Geo_vector();
4468 std::unique_ptr<Geo_vector> guard(m_geo_vect);
4469
4470 const_cast<self &>(v).reassemble();
4471 set_flags(v.get_flags());
4472 set_nbytes(v.get_nbytes());
4473 if (get_nbytes() > 0) {
4474 m_ptr = gis_wkb_alloc(v.get_nbytes() + 2);
4475 if (m_ptr == nullptr) {
4476 m_geo_vect = nullptr;
4477 set_ownmem(false);
4478 set_nbytes(0);
4479 return;
4480 }
4481 memcpy(m_ptr, v.get_ptr(), v.get_nbytes());
4482 /*
4483 The extra 2 bytes makes the buffer usable by get_nbytes_free.
4484 It's hard to know how many more space will be needed so let's
4485 allocate more later.
4486 */
4487 get_cptr()[get_nbytes()] = '\xff';
4488 get_cptr()[get_nbytes() + 1] = '\0';
4489 parse_wkb_data(this, get_cptr(), v.get_geo_vect()->size());
4490 set_ownmem(true);
4491 }
4492 guard.release();
4493 }
4494
4495 /**
4496 Deep assignment from vector 'rhs' to this object.
4497 @param rhs the Gis_wkb_vector<T> instance to duplicate from.
4498 */
4499 template <typename T>
operator =(const Gis_wkb_vector<T> & rhs)4500 Gis_wkb_vector<T> &Gis_wkb_vector<T>::operator=(const Gis_wkb_vector<T> &rhs) {
4501 if (this == &rhs) return *this;
4502 Geometry::operator=(rhs);
4503
4504 DBUG_ASSERT((m_ptr != nullptr && get_ownmem() && get_nbytes() > 0) ||
4505 (m_ptr == nullptr && !get_ownmem() && get_nbytes() == 0));
4506 DBUG_ASSERT(
4507 (rhs.get_ptr() != nullptr && rhs.get_nbytes() > 0) ||
4508 (rhs.get_ptr() == nullptr && !rhs.get_ownmem() && rhs.get_nbytes() == 0));
4509
4510 if (m_owner == nullptr) m_owner = rhs.get_owner();
4511
4512 size_t nbytes_free = get_nbytes_free();
4513 clear_wkb_data();
4514
4515 if (rhs.get_ptr() == nullptr) {
4516 if (m_ptr != nullptr) gis_wkb_free(m_ptr);
4517 m_ptr = nullptr;
4518 set_flags(rhs.get_flags());
4519 return *this;
4520 }
4521
4522 /*
4523 Geometry v may have out of line components, need to reassemble first.
4524 */
4525 const_cast<self &>(rhs).reassemble();
4526
4527 /*
4528 If have no enough space, reallocate with extra space padded with required
4529 bytes;
4530 */
4531 if (m_ptr == nullptr || get_nbytes() + nbytes_free < rhs.get_nbytes()) {
4532 gis_wkb_free(m_ptr);
4533 m_ptr = gis_wkb_alloc(rhs.get_nbytes() + 32 /* some extra space. */);
4534 if (m_ptr == nullptr) {
4535 /*
4536 This object in this case is valid although it doesn't have any data.
4537 */
4538 set_nbytes(0);
4539 set_ownmem(false);
4540 return *this;
4541 }
4542
4543 // Fill extra space with pattern defined by
4544 // Gis_wkb_vector<>::get_nbytes_free().
4545 char *cp = get_cptr();
4546 memset(cp + rhs.get_nbytes(), 0xFF, 32);
4547 cp[rhs.get_nbytes() + 31] = '\0';
4548 }
4549
4550 /*
4551 If need less space than before, set remaining bytes to 0xFF as requred
4552 by Gis_wkb_vector<>::get_nbytes_free.
4553 */
4554 if (get_nbytes() > rhs.get_nbytes())
4555 memset(get_cptr() + rhs.get_nbytes(), 0xFF,
4556 get_nbytes() - rhs.get_nbytes());
4557
4558 memcpy(m_ptr, rhs.get_ptr(), rhs.get_nbytes());
4559
4560 set_flags(rhs.get_flags());
4561 set_ownmem(true);
4562
4563 m_geo_vect = new Geo_vector();
4564 parse_wkb_data(this, get_cptr());
4565 return *this;
4566 }
4567
4568 /**
4569 The copy constructors of Geometry classes always do deep copy, but when
4570 pushing a Geometry object into its owner's geo.m_geo_vect, we want to do
4571 shallow copy because we want all elements in geo.m_geo_vect vector point
4572 into locations in the geo.m_ptr buffer. In such situations call this
4573 function.
4574 @param g The Geometry object to push into vec.
4575 */
4576 template <typename T>
shallow_push(const Geometry * g)4577 void Gis_wkb_vector<T>::shallow_push(const Geometry *g) {
4578 const T &geo = *(static_cast<const T *>(g));
4579 T *pgeo = nullptr;
4580
4581 if (m_geo_vect == nullptr) m_geo_vect = new Geo_vector();
4582 // Allocate space and create an object with its default constructor.
4583 pgeo = static_cast<T *>(m_geo_vect->append_object());
4584 DBUG_ASSERT(pgeo != nullptr);
4585 if (pgeo == nullptr) return;
4586
4587 pgeo->set_flags(geo.get_flags());
4588 pgeo->set_srid(geo.get_srid());
4589 pgeo->set_bg_adapter(true);
4590 // Such a shallow copied object never has its own memory regardless of geo.
4591 pgeo->set_ownmem(false);
4592
4593 // This will parse and set up pgeo->m_geo_vect properly.
4594 // Do not copy elements from geo.m_geo_vect into that of pgeo
4595 // otherwise STL does deep copy using the Geometry copy constructor.
4596 pgeo->set_ptr(geo.get_ptr(), geo.get_nbytes());
4597 pgeo->set_owner(geo.get_owner());
4598 }
4599
4600 template <typename T>
set_ptr(void * ptr,size_t len)4601 void Gis_wkb_vector<T>::set_ptr(void *ptr, size_t len) {
4602 DBUG_ASSERT(!(ptr == nullptr && len > 0));
4603 set_bg_adapter(true);
4604 if (get_geotype() != Geometry::wkb_polygon) {
4605 if (get_ownmem() && m_ptr != nullptr) gis_wkb_free(m_ptr);
4606 m_ptr = ptr;
4607 if (m_geo_vect) clear_wkb_data();
4608 }
4609 set_nbytes(len);
4610 /* When invoked, this object may or may not have its own memory. */
4611 if (get_geotype() != Geometry::wkb_polygon_inner_rings && m_ptr != nullptr) {
4612 if (m_geo_vect == nullptr) m_geo_vect = new Geo_vector();
4613 parse_wkb_data(this, get_cptr());
4614 }
4615 }
4616
4617 /**
4618 Update support
4619 We suppose updating a geometry can happen in the following ways:
4620 1. create an empty geo, then append components into it, the geo must
4621 be a topmost one; a complex geometry such as a multilinestring can be
4622 seen as a tree of geometry components, and the mlstr is the topmost
4623 geometry, i.e. the root of the tree, its lstrs are next layer of nodes,
4624 their points are the 3rd layer of tree nodes. Only the root owns the
4625 wkb buffer, other components point somewhere into the buffer, and can
4626 only read the data.
4627
4628 Polygons are only used by getting its exterior ring or inner rings and
4629 then work on that/those rings, never used as a whole.
4630
4631 2. *itr=value, each geo::m_owner can be used to track the topmost
4632 memory owner, and do reallocation to accormodate the value. This is
4633 for now not supported, will be if needed.
4634
4635 So far geometry assignment are only used for point objects in boost
4636 geometry, thus only Geometry and Gis_point have operator=, no other
4637 classes need so, and thus there is no need for reallocation.
4638 3. call resize() to append some objects at the end, then assign/append
4639 values to the added objects using push_back. Objects added this way
4640 are out of line(unless the object is a point), and user need to call
4641 reassemble() to make them inline, i.e. stored in its owner's memory.
4642 */
4643
4644 /// Clear geometry data of this object.
4645 template <typename T>
clear()4646 void Gis_wkb_vector<T>::clear() {
4647 if (!m_geo_vect) {
4648 DBUG_ASSERT(m_ptr == nullptr);
4649 return;
4650 }
4651
4652 DBUG_ASSERT(m_geo_vect && get_geotype() != Geometry::wkb_polygon);
4653
4654 // Keep the component vector because this object can be reused again.
4655 const void *ptr = get_ptr();
4656 set_bg_adapter(true);
4657
4658 if (ptr && get_ownmem()) {
4659 gis_wkb_free(const_cast<void *>(ptr));
4660 set_ownmem(false);
4661 }
4662
4663 m_ptr = nullptr;
4664 clear_wkb_data();
4665 set_nbytes(0);
4666 }
4667
4668 /// Returns payload number of bytes of the topmost geometry holding this
4669 /// geometry, i.e. the memory owner.
4670 template <typename T>
current_size() const4671 size_t Gis_wkb_vector<T>::current_size() const {
4672 // Polygon's data may not stay in a continuous chunk, and we update
4673 // its data using the outer/inner rings.
4674 DBUG_ASSERT(get_geotype() != Geometry::wkb_polygon);
4675 set_bg_adapter(true);
4676 if (m_geo_vect == nullptr || m_geo_vect->empty()) return 0;
4677
4678 return get_nbytes();
4679 }
4680
4681 /// Get number of free bytes in the buffer held by m_ptr. this object must be
4682 /// an topmost geometry which owns memory.
4683 template <typename T>
get_nbytes_free() const4684 size_t Gis_wkb_vector<T>::get_nbytes_free() const {
4685 DBUG_ASSERT((this->get_ownmem() && m_ptr) || (!get_ownmem() && !m_ptr));
4686
4687 size_t cap = current_size();
4688 if (cap == 0) {
4689 DBUG_ASSERT(m_ptr == nullptr);
4690 return 0;
4691 }
4692
4693 const char *p = nullptr, *ptr = get_cptr();
4694 DBUG_ASSERT(ptr != nullptr);
4695
4696 /*
4697 There will always be remaining free space because in push_back, when
4698 number of free bytes equals needed bytes we will do a realloc.
4699 */
4700 for (p = ptr + cap; *p != 0; p++)
4701 ;
4702
4703 return p - ptr - cap + 1;
4704 }
4705
4706 template <typename T>
push_back(const T & val)4707 void Gis_wkb_vector<T>::push_back(const T &val) {
4708 Geometry::wkbType geotype = get_geotype();
4709
4710 DBUG_ASSERT(geotype != Geometry::wkb_polygon &&
4711 ((m_ptr && get_ownmem()) || (!m_ptr && !get_ownmem())));
4712
4713 // Only three possible types of geometries for val, thus no need to
4714 // do val.reassemble().
4715 DBUG_ASSERT(val.get_geotype() == wkb_point ||
4716 val.get_geotype() == wkb_polygon ||
4717 val.get_geotype() == wkb_linestring);
4718
4719 DBUG_ASSERT(val.get_ptr() != nullptr);
4720
4721 size_t cap = 0, nalloc = 0;
4722 size_t vallen, needed;
4723 void *src_val = val.get_ptr();
4724
4725 if (m_geo_vect == nullptr) m_geo_vect = new Geo_vector;
4726 set_bg_adapter(true);
4727 vallen = val.get_nbytes();
4728 /*
4729 Often inside bg, a polygon is created with no data, then append points
4730 into outer ring and inner rings, such a polygon is a 'constructed'
4731 polygon, and in this case we need to assemble
4732 its data into a continuous chunk.
4733 */
4734 if (val.get_geotype() == Geometry::wkb_polygon)
4735 src_val = get_packed_ptr(&val, &vallen);
4736
4737 // The 4 types can be resized and have out-of-line components,
4738 // reassemble first in case we lose them when doing m_geo_vect->clear().
4739 if (geotype == Geometry::wkb_multilinestring ||
4740 geotype == Geometry::wkb_geometrycollection ||
4741 geotype == Geometry::wkb_polygon_inner_rings ||
4742 geotype == Geometry::wkb_multipolygon)
4743 reassemble();
4744
4745 // Get cap only after reassemble().
4746 cap = current_size();
4747
4748 needed = vallen + WKB_HEADER_SIZE;
4749 // Use >= instead of > because we always want to have trailing free bytes.
4750 if (needed >= this->get_nbytes_free()) {
4751 nalloc = cap + ((needed * 2 > 256) ? needed * 2 : 256);
4752 void *ptr = get_ptr();
4753 m_ptr = gis_wkb_realloc(m_ptr, nalloc);
4754 if (m_ptr == nullptr) {
4755 set_nbytes(0);
4756 set_ownmem(0);
4757 clear_wkb_data();
4758 return;
4759 }
4760
4761 // Set unused space to -1, and last unused byte to 0.
4762 // Function get_nbytes_free relies on this format.
4763 memset(get_cptr() + cap, 0xff, nalloc - cap);
4764 get_cptr()[nalloc - 1] = '\0';
4765 memset(get_cptr() + cap, 0, sizeof(uint32));
4766
4767 bool replaced = (ptr != m_ptr);
4768 set_ownmem(true);
4769 if (m_owner && m_owner->get_geotype() == Geometry::wkb_polygon)
4770 m_owner->set_ownmem(true);
4771
4772 // After reallocation we need to parse again.
4773 if (cap > 0 && replaced) {
4774 size_t ngeos = 0;
4775 if (geotype == Geometry::wkb_polygon_inner_rings) ngeos = size();
4776 clear_wkb_data();
4777 parse_wkb_data(this, get_cptr(), ngeos);
4778 }
4779 }
4780
4781 size_t wkb_header_size = 0;
4782 /* Offset for obj count, if needed. */
4783 size_t obj_count_len =
4784 ((cap == 0 && geotype != Geometry::wkb_polygon_inner_rings)
4785 ? sizeof(uint32)
4786 : 0);
4787 char *val_ptr = get_cptr() + cap + obj_count_len;
4788
4789 // Append WKB header first, if needed.
4790 if (geotype == Geometry::wkb_multipoint ||
4791 geotype == Geometry::wkb_multipolygon ||
4792 geotype == Geometry::wkb_multilinestring ||
4793 geotype == Geometry::wkb_geometrycollection) {
4794 Geometry::wkbType vgt = val.get_geotype();
4795 DBUG_ASSERT(
4796 (geotype == Geometry::wkb_multipoint && vgt == Geometry::wkb_point) ||
4797 (geotype == Geometry::wkb_multipolygon &&
4798 vgt == Geometry::wkb_polygon) ||
4799 (geotype == Geometry::wkb_multilinestring &&
4800 vgt == Geometry::wkb_linestring) ||
4801 geotype == Geometry::wkb_geometrycollection);
4802
4803 val_ptr = write_wkb_header(val_ptr, vgt);
4804 wkb_header_size = WKB_HEADER_SIZE;
4805 }
4806
4807 // Copy val's data into buffer, then parse it.
4808 memcpy(val_ptr, src_val, vallen);
4809 set_nbytes(get_nbytes() + wkb_header_size + obj_count_len + vallen);
4810
4811 // Append geometry component into m_geo_vect vector. Try to avoid
4812 // unnecessary parse by calling the right version of set_ptr. And do
4813 // shallow push so that the element in m_geo_vect point to WKB buffer
4814 // rather than have its own copy of the same WKB data.
4815 T val2;
4816 val2.set_flags(val.get_flags());
4817 val2.set_srid(val.get_srid());
4818 val2.Geometry::set_ptr(val_ptr);
4819 val2.set_nbytes(vallen);
4820 val2.set_owner(this);
4821 val2.set_ownmem(false);
4822
4823 shallow_push(&val2);
4824 val2.Geometry::set_ptr(nullptr);
4825
4826 if (val2.get_geotype() == Geometry::wkb_polygon)
4827 own_rings(&(m_geo_vect->back()));
4828 if (geotype != Geometry::wkb_polygon_inner_rings) {
4829 int4store(get_ucptr(), uint4korr(get_ucptr()) + 1);
4830 DBUG_ASSERT(uint4korr(get_ucptr()) == this->m_geo_vect->size());
4831 }
4832
4833 if (val.get_geotype() == Geometry::wkb_polygon) gis_wkb_free(src_val);
4834 }
4835
4836 /*
4837 Resize as in std::vector<>::resize().
4838
4839 Because resize can be called to append an empty geometry into its owner,
4840 we have to allow pushing into an empty geo and its memory will not
4841 be in the same chunk as its owner, which is OK for bg since the
4842 Boost Range concept doesn't forbid so. But inside MySQL we should
4843 reassemble the geometries into one chunk before using the WKB buffer
4844 directly, by calling reassemble().
4845 */
4846 template <typename T>
resize(size_t sz)4847 void Gis_wkb_vector<T>::resize(size_t sz) {
4848 if (m_geo_vect == nullptr) m_geo_vect = new Geo_vector;
4849 Geometry::wkbType geotype = get_geotype();
4850 size_t ngeo = m_geo_vect->size();
4851 size_t dim = GEOM_DIM;
4852 size_t ptsz = SIZEOF_STORED_DOUBLE * dim;
4853 bool is_mpt = (geotype == Geometry::wkb_multipoint);
4854
4855 // Can resize a topmost geometry or a out of line geometry which has
4856 // or will have its own memory(i.e. one that's not using others' memory).
4857 // Points are fixed size, polygon doesn't hold data directly.
4858 DBUG_ASSERT(!(m_ptr != nullptr && !get_ownmem()) &&
4859 geotype != Geometry::wkb_point &&
4860 geotype != Geometry::wkb_polygon);
4861 set_bg_adapter(true);
4862 if (sz == ngeo) return;
4863 // Shrinking the vector.
4864 if (sz < ngeo) {
4865 // Some elements may be out of line, must do so otherwise we don't
4866 // know how much to shrink in m_ptr.
4867 reassemble();
4868 size_t sublen = 0;
4869 for (size_t i = ngeo; i > sz; i--)
4870 sublen += (*m_geo_vect)[i - 1].get_nbytes();
4871
4872 // '\0' not allowed in middle and no need for ending '\0' because it's
4873 // at the end of the original free chunk which is right after this chunk.
4874 memset((get_cptr() + get_nbytes() - sublen), 0xff, sublen);
4875 set_nbytes(get_nbytes() - sublen);
4876
4877 #if !defined(DBUG_OFF)
4878 bool rsz_ret = m_geo_vect->resize(sz);
4879 DBUG_ASSERT(rsz_ret == false);
4880 #else
4881 m_geo_vect->resize(sz);
4882 #endif
4883 if (get_geotype() != Geometry::wkb_polygon_inner_rings) {
4884 DBUG_ASSERT(uint4korr(get_ucptr()) == ngeo);
4885 int4store(get_ucptr(), static_cast<uint32>(sz));
4886 }
4887 return;
4888 }
4889
4890 char *ptr = nullptr, *ptr2 = nullptr;
4891
4892 // We can store points directly into its owner, points are fixed length,
4893 // thus don't need its own memory.
4894 if (geotype == Geometry::wkb_linestring ||
4895 geotype == Geometry::wkb_multipoint) {
4896 size_t left = get_nbytes_free(),
4897 needed = (sz - ngeo) * (ptsz + (is_mpt ? WKB_HEADER_SIZE : 0)),
4898 nalloc, cap = get_nbytes();
4899
4900 if (left <= needed) {
4901 nalloc = cap + 32 * (left + needed);
4902 ptr = get_cptr();
4903 m_ptr = gis_wkb_realloc(m_ptr, nalloc);
4904 if (m_ptr == nullptr) {
4905 set_nbytes(0);
4906 set_ownmem(0);
4907 clear_wkb_data();
4908 return;
4909 }
4910 ptr2 = get_cptr();
4911 memset((ptr2 + cap), 0xff, nalloc - cap);
4912 ptr2[nalloc - 1] = '\0';
4913 /*
4914 Only set when cap is 0, otherwise after this call get_nbytes_free()
4915 will work wrong, this is different from push_back because push_back
4916 always put data here more than 4 bytes inside itself.
4917 */
4918 if (cap == 0) int4store(get_ucptr(), 0); // obj count
4919 set_ownmem(true);
4920
4921 if (cap > 0 && ptr != m_ptr) {
4922 clear_wkb_data();
4923 // Note: flags_.nbytes doesn't change.
4924 parse_wkb_data(this, get_cptr());
4925 }
4926 }
4927 ptr2 = get_cptr();
4928 ptr = ptr2 + (cap ? cap : sizeof(uint32) /* obj count */);
4929 if (cap == 0) set_nbytes(sizeof(uint32));
4930 } else
4931 has_out_of_line_components(true);
4932
4933 /*
4934 Because the pushed objects have their own memory, here we won't modify
4935 m_ptr memory at all.
4936 */
4937 for (size_t cnt = sz - ngeo; cnt; cnt--) {
4938 T tmp;
4939 tmp.set_owner(this);
4940 tmp.set_ownmem(false);
4941 // Points are directly put into owner's buffer, no need for own memory.
4942 if (tmp.get_geotype() == Geometry::wkb_point) {
4943 if (is_mpt) {
4944 ptr = write_wkb_header(ptr, Geometry::wkb_point);
4945 set_nbytes(get_nbytes() + WKB_HEADER_SIZE);
4946 }
4947 tmp.set_ptr(ptr, ptsz);
4948 set_nbytes(get_nbytes() + ptsz);
4949 ptr += ptsz;
4950 int4store(get_ucptr(), uint4korr(get_ucptr()) + 1);
4951 DBUG_ASSERT(uint4korr(get_ucptr()) == m_geo_vect->size() + 1);
4952 } else
4953 DBUG_ASSERT(ptr == nullptr && ptr2 == nullptr);
4954
4955 shallow_push(&tmp);
4956 if (tmp.get_geotype() == Geometry::wkb_polygon)
4957 own_rings(&(m_geo_vect->back()));
4958
4959 // tmp will be filled by push_back after this call, which will make
4960 // tmp own its own memory, different from other geos in m_geo_vect,
4961 // this is OK, users should call reassemble() to put them into
4962 // a single chunk of memory.
4963 }
4964 }
4965
4966 // Explicit template instantiation
4967 /// @cond
4968 template void Gis_wkb_vector<Gis_line_string>::clear();
4969 template void Gis_wkb_vector<Gis_point>::clear();
4970 template void Gis_wkb_vector<Gis_polygon>::clear();
4971 template void Gis_wkb_vector<Gis_polygon_ring>::clear();
4972
4973 template void Gis_wkb_vector<Gis_line_string>::push_back(
4974 Gis_line_string const &);
4975 template void Gis_wkb_vector<Gis_point>::push_back(Gis_point const &);
4976 template void Gis_wkb_vector<Gis_polygon>::push_back(Gis_polygon const &);
4977 template void Gis_wkb_vector<Gis_polygon_ring>::push_back(
4978 Gis_polygon_ring const &);
4979
4980 template void Gis_wkb_vector<Gis_line_string>::reassemble();
4981 template void Gis_wkb_vector<Gis_polygon>::reassemble();
4982
4983 template void Gis_wkb_vector<Gis_line_string>::resize(size_t);
4984 template void Gis_wkb_vector<Gis_point>::resize(size_t);
4985 template void Gis_wkb_vector<Gis_polygon>::resize(size_t);
4986 template void Gis_wkb_vector<Gis_polygon_ring>::resize(size_t);
4987
4988 template Gis_wkb_vector<Gis_line_string>::Gis_wkb_vector(
4989 const void *, size_t, const Geometry::Flags_t &, gis::srid_t, bool);
4990 template Gis_wkb_vector<Gis_polygon>::Gis_wkb_vector(const void *, size_t,
4991 const Geometry::Flags_t &,
4992 gis::srid_t, bool);
4993 template Gis_wkb_vector<Gis_point>::Gis_wkb_vector(const void *, size_t,
4994 const Geometry::Flags_t &,
4995 gis::srid_t, bool);
4996
4997 template Gis_wkb_vector<Gis_point> &Gis_wkb_vector<Gis_point>::operator=(
4998 Gis_wkb_vector<Gis_point> const &);
4999
5000 template Gis_wkb_vector<Gis_point>::Gis_wkb_vector(
5001 Gis_wkb_vector<Gis_point> const &);
5002 template Gis_wkb_vector<Gis_polygon>::Gis_wkb_vector(
5003 const Gis_wkb_vector<Gis_polygon> &);
5004 /// @endcond
5005