1 /* Copyright (c) 2003, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 /**
25 @file
26
27 @brief
28 This file defines all spatial functions
29 */
30 #include "item_geofunc.h"
31
32 #include "gstream.h" // Gis_read_stream
33 #include "sql_class.h" // THD
34 #include "gis_bg_traits.h"
35
36 #include "parse_tree_helpers.h"
37 #include "item_geofunc_internal.h"
38
39 #include <stack>
40
41 static int check_geometry_valid(Geometry *geom);
42
43 /**
44 Check whether all points in the sequence container are colinear.
45 @param ls a sequence of points with no duplicates.
46 @return true if the points are colinear, false otherwise.
47 */
48 template <typename Point_range>
is_colinear(const Point_range & ls)49 bool is_colinear(const Point_range &ls)
50 {
51 if (ls.size() < 3)
52 return true;
53
54 double x1, x2, x3, y1, y2, y3, X1, X2, Y1, Y2;
55
56 for (size_t i= 0; i < ls.size() - 2; i++)
57 {
58 x1= ls[i].template get<0>();
59 x2= ls[i + 1].template get<0>();
60 x3= ls[i + 2].template get<0>();
61
62 y1= ls[i].template get<1>();
63 y2= ls[i + 1].template get<1>();
64 y3= ls[i + 2].template get<1>();
65
66 X1= x2 - x1;
67 X2= x3 - x2;
68 Y1= y2 - y1;
69 Y2= y3 - y2;
70
71 if (X1 * Y2 - X2 * Y1 != 0)
72 return false;
73 }
74
75 return true;
76 }
77
78
Item_geometry_func(const POS & pos,PT_item_list * list)79 Item_geometry_func::Item_geometry_func(const POS &pos, PT_item_list *list)
80 :Item_str_func(pos, list)
81 {}
82
83
tmp_table_field(TABLE * t_arg)84 Field *Item_geometry_func::tmp_table_field(TABLE *t_arg)
85 {
86 Field *result;
87 if ((result= new Field_geom(max_length, maybe_null, item_name.ptr(), t_arg->s,
88 get_geometry_type())))
89 result->init(t_arg);
90 return result;
91 }
92
fix_length_and_dec()93 void Item_geometry_func::fix_length_and_dec()
94 {
95 collation.set(&my_charset_bin);
96 decimals=0;
97 max_length= 0xFFFFFFFFU;
98 maybe_null= 1;
99 }
100
101
itemize(Parse_context * pc,Item ** res)102 bool Item_func_geometry_from_text::itemize(Parse_context *pc, Item **res)
103 {
104 if (skip_itemize(res))
105 return false;
106 if (super::itemize(pc, res))
107 return true;
108 assert(arg_count == 1 || arg_count == 2);
109 if (arg_count == 1)
110 pc->thd->lex->set_uncacheable(pc->select, UNCACHEABLE_RAND);
111 return false;
112 }
113
114
115 /**
116 Parses a WKT string to produce a geometry encoded with an SRID prepending
117 its WKB bytes, namely a byte string of GEOMETRY format.
118 @param str buffer to hold result, may not be filled.
119 @return the buffer that hold the GEOMETRY byte string result, may or may
120 not be the same as 'str' parameter.
121 */
val_str(String * str)122 String *Item_func_geometry_from_text::val_str(String *str)
123 {
124 assert(fixed == 1);
125 Geometry_buffer buffer;
126 String arg_val;
127 String *wkt= args[0]->val_str_ascii(&arg_val);
128
129 if ((null_value= (!wkt || args[0]->null_value)))
130 return 0;
131
132 Gis_read_stream trs(wkt->charset(), wkt->ptr(), wkt->length());
133 uint32 srid= 0;
134
135 if (arg_count == 2)
136 {
137 if ((null_value= args[1]->null_value))
138 {
139 assert(maybe_null);
140 return NULL;
141 }
142 else
143 srid= (uint32)args[1]->val_int();
144 }
145
146 str->set_charset(&my_charset_bin);
147 if ((null_value= str->reserve(GEOM_HEADER_SIZE, 512)))
148 return 0;
149 str->length(0);
150 str->q_append(srid);
151 if (!Geometry::create_from_wkt(&buffer, &trs, str, 0))
152 {
153 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
154 return error_str();
155 }
156 return str;
157 }
158
159
itemize(Parse_context * pc,Item ** res)160 bool Item_func_geometry_from_wkb::itemize(Parse_context *pc, Item **res)
161 {
162 if (skip_itemize(res))
163 return false;
164 if (super::itemize(pc, res))
165 return true;
166 assert(arg_count == 1 || arg_count == 2);
167 if (arg_count == 1)
168 pc->thd->lex->set_uncacheable(pc->select, UNCACHEABLE_RAND);
169 return false;
170 }
171
172
173 /**
174 Parses a WKT string to produce a geometry encoded with an SRID prepending
175 its WKB bytes, namely a byte string of GEOMETRY format.
176 @param str buffer to hold result, may not be filled.
177 @return the buffer that hold the GEOMETRY byte string result, may or may
178 not be the same as 'str' parameter.
179 */
val_str(String * str)180 String *Item_func_geometry_from_wkb::val_str(String *str)
181 {
182 assert(fixed == 1);
183 String *wkb= NULL;
184 uint32 srid= 0;
185
186 if (arg_count == 2)
187 {
188 srid= static_cast<uint32>(args[1]->val_int());
189 if ((null_value= args[1]->null_value))
190 return NULL;
191 }
192
193 wkb= args[0]->val_str(&tmp_value);
194 if ((null_value= (!wkb || args[0]->null_value)))
195 return NULL;
196
197 /*
198 GeometryFromWKB(wkb [,srid]) understands both WKB (without SRID) and
199 Geometry (with SRID) values in the "wkb" argument.
200 In case if a Geometry type value is passed, we assume that the value
201 is well-formed and can directly return it without going through
202 Geometry::create_from_wkb(), and consequently such WKB data must be
203 MySQL standard (little) endian. Note that users can pass via client
204 any WKB/Geometry byte string, including those of big endianess.
205 */
206 if (args[0]->field_type() == MYSQL_TYPE_GEOMETRY)
207 {
208 if (arg_count == 1)
209 {
210 push_warning_printf(current_thd,
211 Sql_condition::SL_WARNING,
212 ER_WARN_USING_GEOMFROMWKB_TO_SET_SRID_ZERO,
213 ER_THD(current_thd, ER_WARN_USING_GEOMFROMWKB_TO_SET_SRID_ZERO),
214 func_name(), func_name());
215 }
216 else if (arg_count == 2)
217 {
218 push_warning_printf(current_thd,
219 Sql_condition::SL_WARNING,
220 ER_WARN_USING_GEOMFROMWKB_TO_SET_SRID,
221 ER_THD(current_thd, ER_WARN_USING_GEOMFROMWKB_TO_SET_SRID),
222 func_name(), func_name());
223 }
224
225 Geometry_buffer buff;
226 if (Geometry::construct(&buff, wkb->ptr(), wkb->length()) == NULL)
227 {
228 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
229 return error_str();
230 }
231
232
233 /*
234 Check if SRID embedded into the Geometry value differs
235 from the SRID value passed in the second argument.
236 */
237 if (srid == uint4korr(wkb->ptr()))
238 return wkb; // Do not differ
239
240 /*
241 Replace SRID to the one passed in the second argument.
242 Note, we cannot replace SRID directly in wkb->ptr(),
243 because wkb can point to some value that we should not touch,
244 e.g. to a SP variable value. So we need to copy to "str".
245 */
246 if ((null_value= str->copy(*wkb)))
247 return NULL;
248 str->write_at_position(0, srid);
249 return str;
250 }
251
252 str->set_charset(&my_charset_bin);
253 if (str->reserve(GEOM_HEADER_SIZE, 512))
254 {
255 null_value= true; /* purecov: inspected */
256 return NULL; /* purecov: inspected */
257 }
258 str->length(0);
259 str->q_append(srid);
260 Geometry_buffer buffer;
261 if (!Geometry::create_from_wkb(&buffer, wkb->ptr(), wkb->length(), str,
262 false/* Don't init stream. */))
263 {
264 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
265 return error_str();
266 }
267
268 return str;
269 }
270
271
272 /**
273 Definition of various string constants used for writing and reading
274 GeoJSON data.
275 */
276 const char *Item_func_geomfromgeojson::TYPE_MEMBER= "type";
277 const char *Item_func_geomfromgeojson::CRS_MEMBER= "crs";
278 const char *Item_func_geomfromgeojson::GEOMETRY_MEMBER= "geometry";
279 const char *Item_func_geomfromgeojson::PROPERTIES_MEMBER= "properties";
280 const char *Item_func_geomfromgeojson::FEATURES_MEMBER= "features";
281 const char *Item_func_geomfromgeojson::GEOMETRIES_MEMBER= "geometries";
282 const char *Item_func_geomfromgeojson::COORDINATES_MEMBER= "coordinates";
283 const char *Item_func_geomfromgeojson::CRS_NAME_MEMBER= "name";
284 const char *Item_func_geomfromgeojson::NAMED_CRS= "name";
285 const char *Item_func_geomfromgeojson::SHORT_EPSG_PREFIX= "EPSG:";
286 const char *Item_func_geomfromgeojson::POINT_TYPE= "Point";
287 const char *Item_func_geomfromgeojson::MULTIPOINT_TYPE= "MultiPoint";
288 const char *Item_func_geomfromgeojson::LINESTRING_TYPE= "LineString";
289 const char *Item_func_geomfromgeojson::MULTILINESTRING_TYPE= "MultiLineString";
290 const char *Item_func_geomfromgeojson::POLYGON_TYPE= "Polygon";
291 const char *Item_func_geomfromgeojson::MULTIPOLYGON_TYPE= "MultiPolygon";
292 const char *Item_func_geomfromgeojson::FEATURE_TYPE= "Feature";
293 const char *Item_func_geomfromgeojson::
294 FEATURECOLLECTION_TYPE= "FeatureCollection";
295 const char *Item_func_geomfromgeojson::
296 LONG_EPSG_PREFIX= "urn:ogc:def:crs:EPSG::";
297 const char *Item_func_geomfromgeojson::
298 CRS84_URN= "urn:ogc:def:crs:OGC:1.3:CRS84";
299 const char *Item_func_geomfromgeojson::
300 GEOMETRYCOLLECTION_TYPE= "GeometryCollection";
301
302
303 /**
304 <geometry> = ST_GEOMFROMGEOJSON(<string>[, <options>[, <srid>]])
305
306 Takes a GeoJSON input string and outputs a GEOMETRY.
307 This function supports both single GeoJSON objects and geometry collections.
308
309 In addition, feature objects and feature collections are supported (feature
310 collections are translated into GEOMETRYCOLLECTION).
311
312 It follows the standard described at http://geojson.org/geojson-spec.html
313 (revision 1.0).
314 */
val_str(String * buf)315 String *Item_func_geomfromgeojson::val_str(String *buf)
316 {
317 if (arg_count > 1)
318 {
319 // Check and parse the OPTIONS parameter.
320 longlong dimension_argument= args[1]->val_int();
321 if ((null_value= args[1]->null_value))
322 return NULL;
323
324 if (dimension_argument == 1)
325 {
326 m_handle_coordinate_dimension= Item_func_geomfromgeojson::reject_document;
327 }
328 else if (dimension_argument == 2)
329 {
330 m_handle_coordinate_dimension=
331 Item_func_geomfromgeojson::strip_now_accept_future;
332 }
333 else if (dimension_argument == 3)
334 {
335 m_handle_coordinate_dimension=
336 Item_func_geomfromgeojson::strip_now_reject_future;
337 }
338 else if (dimension_argument == 4)
339 {
340 m_handle_coordinate_dimension=
341 Item_func_geomfromgeojson::strip_now_strip_future;
342 }
343 else
344 {
345 char option_string[MAX_BIGINT_WIDTH + 1];
346 if (args[1]->unsigned_flag)
347 ullstr(dimension_argument, option_string);
348 else
349 llstr(dimension_argument, option_string);
350
351 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "option", option_string,
352 func_name());
353 return error_str();
354 }
355 }
356
357 if (arg_count > 2)
358 {
359 /*
360 Check and parse the SRID parameter. If this is set to a valid value,
361 any CRS member in the GeoJSON document will be ignored.
362 */
363 longlong srid_argument= args[2]->val_int();
364 if ((null_value= args[2]->null_value))
365 return NULL;
366
367 // Only allow unsigned 32 bits integer as SRID.
368 if (srid_argument < 0 || srid_argument > UINT_MAX32)
369 {
370 char srid_string[MAX_BIGINT_WIDTH + 1];
371 if (args[2]->unsigned_flag)
372 ullstr(srid_argument, srid_string);
373 else
374 llstr(srid_argument, srid_string);
375
376 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "SRID", srid_string,
377 func_name());
378 return error_str();
379 }
380 else
381 {
382 m_user_srid= static_cast<Geometry::srid_t>(srid_argument);
383 m_user_provided_srid= true;
384 }
385 }
386
387 Json_wrapper wr;
388 if (get_json_wrapper(args, 0, buf, func_name(), &wr, true))
389 return error_str();
390
391 /*
392 We will handle JSON NULL the same way as we handle SQL NULL. The reason
393 behind this is that we want the following SQL to return SQL NULL:
394
395 SELECT ST_GeomFromGeoJSON(
396 JSON_EXTRACT(
397 '{ "type": "Feature",
398 "geometry": null,
399 "properties": { "name": "Foo" }
400 }',
401 '$.geometry')
402 );
403
404 The function JSON_EXTRACT will return a JSON NULL, so if we don't handle
405 JSON NULL as SQL NULL the above SQL will raise an error since we would
406 expect a SQL NULL or a JSON object.
407 */
408 null_value= (args[0]->null_value || wr.type() == Json_dom::J_NULL);
409 if (null_value)
410 {
411 assert(maybe_null);
412 return NULL;
413 }
414
415 if (wr.type() != Json_dom::J_OBJECT)
416 {
417 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
418 return error_str();
419 }
420 const Json_object *root_obj= down_cast<const Json_object*>(wr.to_dom());
421
422 /*
423 Set the default SRID to 4326. This will be overwritten if a valid CRS is
424 found in the GeoJSON input, or if the user has specified a SRID as an
425 argument.
426
427 It would probably be smart to allocate a percentage of the length of the
428 input string (something like buf->realloc(json_string->length() * 0.2)).
429 This would save a lot of reallocations and boost performance, especially for
430 large inputs. But it is difficult to predict how much of the json input that
431 will be parsed into output data.
432 */
433 if (buf->reserve(GEOM_HEADER_SIZE, 512))
434 {
435 my_error(ER_OUTOFMEMORY, GEOM_HEADER_SIZE);
436 return error_str();
437 }
438 buf->set_charset(&my_charset_bin);
439 buf->length(0);
440 buf->q_append(static_cast<uint32>(4326));
441
442 /*
443 The rollback variable is used for detecting/accepting NULL objects inside
444 collections (a feature with NULL geometry is allowed, and thus we can have
445 a geometry collection with a NULL geometry translated into following WKT:
446 GEOMETRYCOLLECTION()).
447
448 parse_object() does a recursive parsing of the GeoJSON document.
449 */
450 String collection_buffer;
451 bool rollback= false;
452 Geometry *result_geometry= NULL;
453
454 m_srid_found_in_document = -1;
455 if (parse_object(root_obj, &rollback, &collection_buffer, false,
456 &result_geometry))
457 {
458 // Do a delete here, to be sure that we have no memory leaks.
459 delete result_geometry;
460 result_geometry= NULL;
461
462 if (rollback)
463 {
464 assert(maybe_null);
465 null_value= true;
466 return NULL;
467 }
468 return error_str();
469 }
470
471 // Set the correct SRID for the geometry data.
472 if (m_user_provided_srid)
473 buf->write_at_position(0, m_user_srid);
474 else if (m_srid_found_in_document > -1)
475 buf->write_at_position(0, static_cast<uint32>(m_srid_found_in_document));
476
477 bool return_result= result_geometry->as_wkb(buf, false);
478
479 delete result_geometry;
480 result_geometry= NULL;
481
482 if (return_result)
483 {
484 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
485 return error_str();
486 }
487 return buf;
488 }
489
490
491 /**
492 Case insensitive lookup of a member in a JSON object.
493
494 This is needed since the get()-method of the JSON object is case
495 sensitive.
496
497 @param v The object to look for the member in.
498 @param member_name Name of the member to look after
499
500 @return The member if one was found, NULL otherwise.
501 */
502 const Json_dom *Item_func_geomfromgeojson::
my_find_member_ncase(const Json_object * object,const char * member_name)503 my_find_member_ncase(const Json_object *object, const char *member_name)
504 {
505 Json_object::const_iterator itr;
506 for (itr= object->begin(); itr != object->end(); ++itr)
507 {
508 if (native_strcasecmp(member_name, itr->first.c_str()) == 0)
509 return itr->second;
510 }
511
512 return NULL;
513 }
514
515
516 /**
517 Takes a JSON object as input, and parses the data to a Geometry object.
518
519 The call stack will be no larger than the maximum depth of the GeoJSON
520 document, which is more or less equivalent to the number of nested
521 collections in the document.
522
523 @param object A JSON object object to parse.
524 @param rollback Pointer to a boolean indicating if parsed data should
525 be reverted/rolled back.
526 @param buffer A string buffer to be used by GeometryCollection
527 @param is_parent_featurecollection Indicating if the current geometry is a
528 child of a FeatureCollection.
529 @param[out] geometry A pointer to the parsed geometry.
530
531 @return true if the parsing failed, false otherwise. Note that if rollback is
532 set to true and true is returned, the parsing succeeded, but no
533 Geometry data could be parsed.
534 */
535 bool Item_func_geomfromgeojson::
parse_object(const Json_object * object,bool * rollback,String * buffer,bool is_parent_featurecollection,Geometry ** geometry)536 parse_object(const Json_object *object, bool *rollback, String *buffer,
537 bool is_parent_featurecollection, Geometry **geometry)
538 {
539 /*
540 A GeoJSON object MUST have a type member, which MUST
541 be of string type.
542 */
543 const Json_dom *type_member= my_find_member_ncase(object, TYPE_MEMBER);
544 if (!is_member_valid(type_member, TYPE_MEMBER, Json_dom::J_STRING, false,
545 NULL))
546 {
547 return true;
548 }
549
550 /*
551 Check if this object has a CRS member.
552 We allow the CRS member to be JSON NULL.
553 */
554 const Json_dom *crs_member= my_find_member_ncase(object, CRS_MEMBER);
555 if (crs_member != NULL)
556 {
557 if (crs_member->json_type() == Json_dom::J_OBJECT)
558 {
559 const Json_object *crs_obj= down_cast<const Json_object *>(crs_member);
560 if (parse_crs_object(crs_obj))
561 return true;
562 }
563 else if (crs_member->json_type() != Json_dom::J_NULL)
564 {
565 my_error(ER_INVALID_GEOJSON_WRONG_TYPE, MYF(0), func_name(),
566 CRS_MEMBER, "object");
567 return true;
568 }
569 }
570
571 // Handle feature objects and feature collection objects.
572 const Json_string *type_member_str=
573 down_cast<const Json_string*>(type_member);
574 if (strcmp(type_member_str->value().c_str(), FEATURE_TYPE) == 0)
575 {
576 /*
577 Check if this feature object has the required "geometry" and "properties"
578 member. Note that we do not use the member "properties" for anything else
579 than checking for valid GeoJSON document.
580 */
581 bool dummy;
582 const Json_dom *geometry_member= my_find_member_ncase(object,
583 GEOMETRY_MEMBER);
584 const Json_dom *properties_member= my_find_member_ncase(object,
585 PROPERTIES_MEMBER);
586 if (!is_member_valid(geometry_member, GEOMETRY_MEMBER, Json_dom::J_OBJECT,
587 true, rollback) ||
588 !is_member_valid(properties_member, PROPERTIES_MEMBER,
589 Json_dom::J_OBJECT, true, &dummy) || *rollback)
590 {
591 return true;
592 }
593
594 const Json_object *geometry_member_obj=
595 down_cast<const Json_object*>(geometry_member);
596
597 return parse_object(geometry_member_obj, rollback, buffer, false, geometry);
598 }
599 else if (strcmp(type_member_str->value().c_str(), FEATURECOLLECTION_TYPE) == 0)
600 {
601 // FeatureCollections cannot be nested according to GeoJSON spec.
602 if (is_parent_featurecollection)
603 {
604 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
605 return true;
606 }
607
608 // We will handle a FeatureCollection as a GeometryCollection.
609 const Json_dom *features= my_find_member_ncase(object, FEATURES_MEMBER);
610 if (!is_member_valid(features, FEATURES_MEMBER, Json_dom::J_ARRAY, false,
611 NULL))
612 {
613 return true;
614 }
615
616 const Json_array *features_array= down_cast<const Json_array*>(features);
617 return parse_object_array(features_array, Geometry::wkb_geometrycollection,
618 rollback, buffer, true, geometry);
619 }
620 else
621 {
622 Geometry::wkbType wkbtype= get_wkbtype(type_member_str->value().c_str());
623 if (wkbtype == Geometry::wkb_invalid_type)
624 {
625 // An invalid GeoJSON type was found.
626 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
627 return true;
628 }
629 else
630 {
631 /*
632 All objects except GeometryCollection MUST have a member "coordinates"
633 of type array. GeometryCollection MUST have a member "geometries" of
634 type array.
635 */
636 const char *member_name;
637 if (wkbtype == Geometry::wkb_geometrycollection)
638 member_name= GEOMETRIES_MEMBER;
639 else
640 member_name= COORDINATES_MEMBER;
641
642 const Json_dom *array_member= my_find_member_ncase(object, member_name);
643 if (!is_member_valid(array_member, member_name, Json_dom::J_ARRAY, false,
644 NULL))
645 {
646 return true;
647 }
648
649 const Json_array *array_member_array=
650 down_cast<const Json_array*>(array_member);
651 return parse_object_array(array_member_array, wkbtype, rollback, buffer,
652 false, geometry);
653 }
654 }
655
656 // Defensive code. This should never be reached.
657 /* purecov: begin inspected */
658 assert(false);
659 return true;
660 /* purecov: end inspected */
661 }
662
663
664 /**
665 Parse an array of coordinates to a Gis_point.
666
667 Parses an array of coordinates to a Gis_point. This function must handle
668 according to the handle_dimension parameter on how non 2D objects should be
669 handled.
670
671 According to the specification, a position array must have at least two
672 elements, but there is no upper limit.
673
674 @param coordinates A JSON array of coordinates.
675 @param[out] point A pointer to the parsed Gis_point.
676
677 @return true if the parsing failed, false otherwise.
678 */
679 bool Item_func_geomfromgeojson::
get_positions(const Json_array * coordinates,Gis_point * point)680 get_positions(const Json_array *coordinates, Gis_point *point)
681 {
682 /*
683 According to GeoJSON specification, a position array must have at least
684 two positions.
685 */
686 if (coordinates->size() < 2)
687 {
688 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
689 return true;
690 }
691
692 switch (m_handle_coordinate_dimension)
693 {
694 case Item_func_geomfromgeojson::reject_document:
695 if (coordinates->size() > GEOM_DIM)
696 {
697 my_error(ER_DIMENSION_UNSUPPORTED, MYF(0), func_name(),
698 coordinates->size(), GEOM_DIM);
699 return true;
700 }
701 break;
702 case Item_func_geomfromgeojson::strip_now_reject_future:
703 /*
704 The version in development as of writing, only supports 2 dimensions.
705 When dimension count is increased beyond 2, we want the function to fail.
706 */
707 if (GEOM_DIM > 2 && coordinates->size() > 2)
708 {
709 my_error(ER_DIMENSION_UNSUPPORTED, MYF(0), func_name(),
710 coordinates->size(), GEOM_DIM);
711 return true;
712 }
713 break;
714 case Item_func_geomfromgeojson::strip_now_strip_future:
715 case Item_func_geomfromgeojson::strip_now_accept_future:
716 if (GEOM_DIM > 2)
717 assert(false);
718 break;
719 default:
720 // Unspecified behaviour.
721 assert(false);
722 return true;
723 }
724
725 // Check if all array members are numbers.
726 for (size_t i= 0; i < coordinates->size(); ++i)
727 {
728 if (!(*coordinates)[i]->is_number())
729 {
730 my_error(ER_INVALID_GEOJSON_WRONG_TYPE, MYF(0), func_name(),
731 "array coordinate", "number");
732 return true;
733 }
734
735 /*
736 Even though we only need the two first coordinates, we check the rest of
737 them to ensure that the GeoJSON is valid.
738
739 Remember to call set_alias(), so that this wrapper does not take ownership
740 of the data.
741 */
742 Json_wrapper coord((*coordinates)[i]);
743 coord.set_alias();
744 if (i == 0)
745 point->set<0>(coord.coerce_real(""));
746 else if (i == 1)
747 point->set<1>(coord.coerce_real(""));
748 }
749
750 return false;
751 }
752
753
754 /**
755 Takes a JSON array as input, does a recursive parsing and returns a
756 Geometry object.
757
758 This function differs from parse_object() in that it takes an array as input
759 instead of a object. This is one of the members "coordinates" or "geometries"
760 of a GeoJSON object.
761
762 @param data_array A JSON array to parse.
763 @param type The type of the GeoJSON object this array belongs to.
764 @param rollback Pointer to a boolean indicating if parsed data should
765 be reverted/rolled back.
766 @param buffer A String buffer to be used by GeometryCollection.
767 @param[out] geometry A pointer to the parsed Geometry.
768
769 @return true on failure, false otherwise.
770 */
771 bool Item_func_geomfromgeojson::
parse_object_array(const Json_array * data_array,Geometry::wkbType type,bool * rollback,String * buffer,bool is_parent_featurecollection,Geometry ** geometry)772 parse_object_array(const Json_array *data_array, Geometry::wkbType type,
773 bool *rollback, String *buffer,
774 bool is_parent_featurecollection, Geometry **geometry)
775 {
776 switch (type)
777 {
778 case Geometry::wkb_geometrycollection:
779 {
780 /*
781 Ensure that the provided buffer is empty, and then create a empty
782 GeometryCollection using this buffer.
783 */
784 buffer->set_charset(&my_charset_bin);
785 buffer->length(0);
786 buffer->reserve(GEOM_HEADER_SIZE + SIZEOF_INT);
787 write_geometry_header(buffer, 0, Geometry::wkb_geometrycollection, 0);
788
789 Gis_geometry_collection *collection= new Gis_geometry_collection();
790 *geometry= collection;
791
792 collection->set_data_ptr(buffer->ptr() + GEOM_HEADER_SIZE, 4);
793 collection->has_geom_header_space(true);
794
795 for (size_t i= 0; i < data_array->size(); ++i)
796 {
797 if ((*data_array)[i]->json_type() != Json_dom::J_OBJECT)
798 {
799 my_error(ER_INVALID_GEOJSON_WRONG_TYPE, MYF(0), func_name(),
800 GEOMETRIES_MEMBER, "object array");
801 return true;
802 }
803
804 const Json_object *object=
805 down_cast<const Json_object*>((*data_array)[i]);
806
807 String geo_buffer;
808 Geometry *parsed_geometry= NULL;
809 if (parse_object(object, rollback, &geo_buffer,
810 is_parent_featurecollection, &parsed_geometry))
811 {
812 /*
813 This will happen if a feature object contains a NULL geometry
814 object (which is a perfectly valid GeoJSON object).
815 */
816 if (*rollback)
817 {
818 *rollback= false;
819 }
820 else
821 {
822 delete parsed_geometry;
823 parsed_geometry= NULL;
824
825 return true;
826 }
827 }
828 else
829 {
830 if (parsed_geometry->get_geotype() == Geometry::wkb_polygon)
831 {
832 // Make the Gis_polygon suitable for MySQL GIS code.
833 Gis_polygon *polygon= static_cast<Gis_polygon*>(parsed_geometry);
834 polygon->to_wkb_unparsed();
835 }
836 collection->append_geometry(parsed_geometry, buffer);
837 }
838 delete parsed_geometry;
839 parsed_geometry= NULL;
840 }
841 return false;
842 }
843 case Geometry::wkb_point:
844 {
845 Gis_point *point= new Gis_point(false);
846 *geometry= point;
847 return get_positions(data_array, point);
848 }
849 case Geometry::wkb_linestring:
850 {
851 Gis_line_string *linestring= new Gis_line_string(false);
852 *geometry= linestring;
853
854 if (get_linestring(data_array, linestring))
855 return true;
856 return false;
857 }
858 case Geometry::wkb_multipoint:
859 {
860 // Ensure that the MultiPoint has at least one Point.
861 if (data_array->size() == 0)
862 {
863 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
864 return true;
865 }
866
867 Gis_multi_point *multipoint= new Gis_multi_point(false);
868 *geometry= multipoint;
869
870 for (size_t i= 0; i < data_array->size(); ++i)
871 {
872 if ((*data_array)[i]->json_type() != Json_dom::J_ARRAY)
873 {
874 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
875 return true;
876 }
877 else
878 {
879 const Json_array *coords=
880 down_cast<const Json_array*>((*data_array)[i]);
881 Gis_point point;
882 if (get_positions(coords, &point))
883 return true;
884 multipoint->push_back(point);
885 }
886 }
887 return false;
888 }
889 case Geometry::wkb_multilinestring:
890 {
891 // Ensure that the MultiLineString has at least one LineString.
892 if (data_array->size() == 0)
893 {
894 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
895 return true;
896 }
897
898 Gis_multi_line_string *multilinestring= new Gis_multi_line_string(false);
899 *geometry= multilinestring;
900 for (size_t i= 0; i < data_array->size(); ++i)
901 {
902 if ((*data_array)[i]->json_type() != Json_dom::J_ARRAY)
903 {
904 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
905 return true;
906 }
907
908 const Json_array *coords=
909 down_cast<const Json_array*>((*data_array)[i]);
910 Gis_line_string linestring;
911 if (get_linestring(coords, &linestring))
912 return true;
913 multilinestring->push_back(linestring);
914 }
915 return false;
916 }
917 case Geometry::wkb_polygon:
918 {
919 Gis_polygon *polygon= new Gis_polygon(false);
920 *geometry= polygon;
921 return get_polygon(data_array, polygon);
922 }
923 case Geometry::wkb_multipolygon:
924 {
925 // Ensure that the MultiPolygon has at least one Polygon.
926 if (data_array->size() == 0)
927 {
928 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
929 return true;
930 }
931
932 Gis_multi_polygon *multipolygon= new Gis_multi_polygon(false);
933 *geometry= multipolygon;
934
935 for (size_t i= 0; i < data_array->size(); ++i)
936 {
937 if ((*data_array)[i]->json_type() != Json_dom::J_ARRAY)
938 {
939 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
940 return true;
941 }
942
943 const Json_array *coords=
944 down_cast<const Json_array*>((*data_array)[i]);
945 Gis_polygon polygon;
946 if (get_polygon(coords, &polygon))
947 return true;
948 multipolygon->push_back(polygon);
949 }
950 return false;
951 }
952 default:
953 {
954 assert(false);
955 return false;
956 }
957 }
958 }
959
960
961 /**
962 Create a Gis_line_string from a JSON array.
963
964 @param data_array A JSON array containing the coordinates.
965 @param linestring Pointer to a linestring to be filled with data.
966
967 @return true on failure, false otherwise.
968 */
969 bool
get_linestring(const Json_array * data_array,Gis_line_string * linestring)970 Item_func_geomfromgeojson::get_linestring(const Json_array *data_array,
971 Gis_line_string *linestring)
972 {
973 // Ensure that the linestring has at least one point.
974 if (data_array->size() < 2)
975 {
976 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
977 return true;
978 }
979
980 for (size_t i= 0; i < data_array->size(); ++i)
981 {
982 if ((*data_array)[i]->json_type() != Json_dom::J_ARRAY)
983 {
984 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
985 return true;
986 }
987 else
988 {
989 Gis_point point;
990 const Json_array *coords= down_cast<const Json_array*>((*data_array)[i]);
991 if (get_positions(coords, &point))
992 return true;
993 linestring->push_back(point);
994 }
995 }
996
997 return false;
998 }
999
1000
1001 /**
1002 Create a Gis_polygon from a JSON array.
1003
1004 @param data_array A JSON array containing the coordinates.
1005 @param polygon A pointer to a Polygon to be filled with data.
1006
1007 @return true on failure, false otherwise.
1008 */
get_polygon(const Json_array * data_array,Gis_polygon * polygon)1009 bool Item_func_geomfromgeojson::get_polygon(const Json_array *data_array,
1010 Gis_polygon *polygon)
1011 {
1012 // Ensure that the Polygon has at least one ring.
1013 if (data_array->size() == 0)
1014 {
1015 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1016 return true;
1017 }
1018
1019 for (size_t ring_count= 0; ring_count < data_array->size(); ++ring_count)
1020 {
1021 // Polygon rings must have at least four points, according to GeoJSON spec.
1022 if ((*data_array)[ring_count]->json_type() != Json_dom::J_ARRAY)
1023 {
1024 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1025 return true;
1026 }
1027
1028 const Json_array *polygon_ring=
1029 down_cast<const Json_array*>((*data_array)[ring_count]);
1030 if (polygon_ring->size() < 4)
1031 {
1032 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1033 return true;
1034 }
1035
1036 polygon->inners().resize(ring_count);
1037 for (size_t i= 0; i < polygon_ring->size(); ++i)
1038 {
1039 if ((*polygon_ring)[i]->json_type() != Json_dom::J_ARRAY)
1040 {
1041 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1042 return true;
1043 }
1044
1045 Gis_point point;
1046 const Json_array *coords=
1047 down_cast<const Json_array*>((*polygon_ring)[i]);
1048 if (get_positions(coords, &point))
1049 return true;
1050
1051 if (ring_count == 0)
1052 polygon->outer().push_back(point);
1053 else
1054 polygon->inners()[ring_count - 1].push_back(point);
1055 }
1056
1057 // Check if the ring is closed, which is must be according to GeoJSON spec.
1058 Gis_point first;
1059 Gis_point last;
1060 if (ring_count == 0)
1061 {
1062 first= polygon->outer()[0];
1063 last= polygon->outer().back();
1064 }
1065 else
1066 {
1067 first= polygon->inners()[ring_count - 1][0];
1068 last= polygon->inners()[ring_count - 1].back();
1069 }
1070
1071 if (!(first == last))
1072 {
1073 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1074 return true;
1075 }
1076 }
1077 return false;
1078 }
1079
1080
1081 /**
1082 Converts GeoJSON type string to a wkbType.
1083
1084 Convert a string from a "type" member in GeoJSON to its equivalent Geometry
1085 enumeration. The type names are case sensitive as stated in the specification:
1086
1087 The value of the type member must be one of: "Point", "MultiPoint",
1088 "LineString", "MultiLineString", "Polygon", "MultiPolygon",
1089 "GeometryCollection", "Feature", or "FeatureCollection". The case of the
1090 type member values must be as shown here.
1091
1092 Note that even though Feature and FeatureCollection are added here, these
1093 types will be handled before this function is called (in parse_object()).
1094
1095 @param typestring A GeoJSON type string.
1096
1097 @return The corresponding wkbType, or wkb_invalid_type if no matching
1098 type was found.
1099 */
get_wkbtype(const char * typestring)1100 Geometry::wkbType Item_func_geomfromgeojson::get_wkbtype(const char *typestring)
1101 {
1102 if (strcmp(typestring, POINT_TYPE) == 0)
1103 return Geometry::wkb_point;
1104 else if (strcmp(typestring, MULTIPOINT_TYPE) == 0)
1105 return Geometry::wkb_multipoint;
1106 else if (strcmp(typestring, LINESTRING_TYPE) == 0)
1107 return Geometry::wkb_linestring;
1108 else if (strcmp(typestring, MULTILINESTRING_TYPE) == 0)
1109 return Geometry::wkb_multilinestring;
1110 else if (strcmp(typestring, POLYGON_TYPE) == 0)
1111 return Geometry::wkb_polygon;
1112 else if (strcmp(typestring, MULTIPOLYGON_TYPE) == 0)
1113 return Geometry::wkb_multipolygon;
1114 else if (strcmp(typestring, GEOMETRYCOLLECTION_TYPE) == 0)
1115 return Geometry::wkb_geometrycollection;
1116 else
1117 return Geometry::wkb_invalid_type;
1118 }
1119
1120
1121 /**
1122 Takes a GeoJSON CRS object as input and parses it into a SRID.
1123
1124 If user has supplied a SRID, the parsing will be ignored.
1125
1126 GeoJSON support two types of CRS objects; named and linked. Linked CRS will
1127 force us to download CRS parameters from the web, which we do not allow.
1128 Thus, we will only parse named CRS URNs in the"urn:ogc:def:crs:EPSG::<srid>"
1129 and "EPSG:<srid>" namespaces. In addition, "urn:ogc:def:crs:OGC:1.3:CRS84"
1130 will be recognized as SRID 4326. Note that CRS object with value JSON null is
1131 valid.
1132
1133 @param crs_object A GeoJSON CRS object to parse.
1134 @param result The WKB string the result will be appended to.
1135
1136 @return false if the parsing was successful, or true if it didn't understand
1137 the CRS object provided.
1138 */
1139 bool Item_func_geomfromgeojson::
parse_crs_object(const Json_object * crs_object)1140 parse_crs_object(const Json_object *crs_object)
1141 {
1142 if (m_user_provided_srid)
1143 return false;
1144
1145 /*
1146 Check if required CRS members "type" and "properties" exists, and that they
1147 are of correct type according to GeoJSON specification.
1148 */
1149 const Json_dom *type_member= my_find_member_ncase(crs_object, TYPE_MEMBER);
1150 const Json_dom *properties_member= my_find_member_ncase(crs_object,
1151 PROPERTIES_MEMBER);
1152 if (!is_member_valid(type_member, TYPE_MEMBER, Json_dom::J_STRING, false,
1153 NULL) ||
1154 !is_member_valid(properties_member, PROPERTIES_MEMBER, Json_dom::J_OBJECT,
1155 false, NULL))
1156 {
1157 return true;
1158 }
1159
1160 // Check that this CRS is a named CRS, and not a linked CRS.
1161 const Json_string *type_member_str=
1162 down_cast<const Json_string*>(type_member);
1163 if (native_strcasecmp(type_member_str->value().c_str(), NAMED_CRS) != 0)
1164 {
1165 // CRS object is not a named CRS.
1166 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1167 return true;
1168 }
1169
1170 /*
1171 Check that CRS properties member has the required member "name"
1172 of type "string".
1173 */
1174 const Json_object *properties_member_obj=
1175 down_cast<const Json_object*>(properties_member);
1176 const Json_dom *crs_name_member= my_find_member_ncase(properties_member_obj,
1177 CRS_NAME_MEMBER);
1178 if (!is_member_valid(crs_name_member, CRS_NAME_MEMBER, Json_dom::J_STRING,
1179 false, NULL))
1180 {
1181 return true;
1182 }
1183 /*
1184 Now we can do the parsing of named CRS. The parsing happens as follows:
1185
1186 1) Check if the named CRS is equal to urn:ogc:def:crs:OGC:1.3:CRS84". If so,
1187 return SRID 4326.
1188 2) Otherwise, check if we have a short or long format CRS URN in the
1189 EPSG namespace.
1190 3) If we have a CRS URN in the EPSG namespace, check if the ending after the
1191 last ':' is a valid SRID ("EPSG:<srid>" or
1192 "urn:ogc:def:crs:EPSG::<srid>"). An valid SRID must be greater than zero,
1193 and less than or equal to UINT_MAX32.
1194 4) If a SRID was returned from the parsing, check if we already have found
1195 a valid CRS earlier in the parsing. If so, and the SRID from the earlier
1196 CRS was different than the current, return an error to the user.
1197
1198 If any of these fail, an error is returned to the user.
1199 */
1200 const Json_string *crs_name_member_str=
1201 down_cast<const Json_string*>(crs_name_member);
1202 longlong parsed_srid= -1;
1203 if (native_strcasecmp(crs_name_member_str->value().c_str(), CRS84_URN) == 0)
1204 {
1205 parsed_srid= 4326;
1206 }
1207 else
1208 {
1209 size_t start_index;
1210 size_t name_length= crs_name_member_str->size();
1211 const char *crs_name= crs_name_member_str->value().c_str();
1212 if (native_strncasecmp(crs_name, SHORT_EPSG_PREFIX, 5) == 0)
1213 {
1214 start_index= 5;
1215 }
1216 else if (native_strncasecmp(crs_name, LONG_EPSG_PREFIX, 22) == 0)
1217 {
1218 start_index= 22;
1219 }
1220 else
1221 {
1222 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1223 return true;
1224 }
1225
1226 char *end_of_parse;
1227 longlong parsed_value= strtoll(crs_name + start_index, &end_of_parse, 10);
1228
1229 /*
1230 Check that the whole ending got parsed, and that the value is within
1231 valid SRID range.
1232 */
1233 if (end_of_parse == (crs_name + name_length) && parsed_value > 0 &&
1234 parsed_value <= UINT_MAX32)
1235 {
1236 parsed_srid= static_cast<uint32>(parsed_value);
1237 }
1238 else
1239 {
1240 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1241 return true;
1242 }
1243 }
1244
1245 if (parsed_srid > 0)
1246 {
1247 if (m_srid_found_in_document > 0 && parsed_srid != m_srid_found_in_document)
1248 {
1249 // A SRID has already been found, which had a different value.
1250 my_error(ER_INVALID_GEOJSON_UNSPECIFIED, MYF(0), func_name());
1251 return true;
1252 }
1253 else
1254 {
1255 m_srid_found_in_document= parsed_srid;
1256 }
1257 }
1258 return false;
1259 }
1260
1261
1262 /**
1263 Checks if a JSON member is valid based on input criteria.
1264
1265 This function checks if the provided member exists, and if it's of the
1266 expected type. If it fails ome of the test, my_error() is called and false is
1267 returned from the function.
1268
1269 @param member The member to validate.
1270 @param member_name Name of the member we are validating, so that the error
1271 returned to the user is more informative.
1272 @param expected_type Expected type of the member.
1273 @param allow_null If we shold allow the member to have JSON null value.
1274 @param[out] was_null This will be set to true if the provided member had a
1275 JSON null value. Is only affected if allow_null is set to true.
1276
1277 @return true if the member is valid, false otherwise.
1278 */
1279 bool Item_func_geomfromgeojson::
is_member_valid(const Json_dom * member,const char * member_name,Json_dom::enum_json_type expected_type,bool allow_null,bool * was_null)1280 is_member_valid(const Json_dom *member, const char *member_name,
1281 Json_dom::enum_json_type expected_type, bool allow_null,
1282 bool *was_null)
1283 {
1284 if (member == NULL)
1285 {
1286 my_error(ER_INVALID_GEOJSON_MISSING_MEMBER, MYF(0), func_name(),
1287 member_name);
1288 return false;
1289 }
1290
1291 if (allow_null)
1292 {
1293 assert(was_null != NULL);
1294 *was_null= member->json_type() == Json_dom::J_NULL;
1295 if (*was_null)
1296 return true;
1297 }
1298
1299 const char *type_name;
1300 if (member->json_type() != expected_type)
1301 {
1302 switch (expected_type)
1303 {
1304 case Json_dom::J_OBJECT:
1305 type_name= "object";
1306 break;
1307 case Json_dom::J_ARRAY:
1308 type_name= "array";
1309 break;
1310 case Json_dom::J_STRING:
1311 type_name= "string";
1312 break;
1313 default:
1314 /* purecov: begin deadcode */
1315 assert(false);
1316 return false;
1317 /* purecov: end */
1318 }
1319
1320 my_error(ER_INVALID_GEOJSON_WRONG_TYPE, MYF(0), func_name(), member_name,
1321 type_name);
1322 return false;
1323 }
1324 return true;
1325 }
1326
1327
fix_length_and_dec()1328 void Item_func_geomfromgeojson::fix_length_and_dec()
1329 {
1330 Item_geometry_func::fix_length_and_dec();
1331 }
1332
1333
1334 /**
1335 Checks if the supplied argument is a valid integer type.
1336
1337 The function will fail if the supplied data is binary data. It will accept
1338 strings as integer type. Used for checking SRID and OPTIONS argument.
1339
1340 @param argument The argument to check.
1341
1342 @return true if the argument is a valid integer type, false otherwise.
1343 */
check_argument_valid_integer(Item * argument)1344 bool Item_func_geomfromgeojson::check_argument_valid_integer(Item *argument)
1345 {
1346 bool is_binary_charset= (argument->collation.collation == &my_charset_bin);
1347 bool is_parameter_marker= (argument->type() == PARAM_ITEM);
1348
1349 switch (argument->field_type())
1350 {
1351 case MYSQL_TYPE_NULL:
1352 return true;
1353 case MYSQL_TYPE_STRING:
1354 case MYSQL_TYPE_VARCHAR:
1355 case MYSQL_TYPE_VAR_STRING:
1356 return (!is_binary_charset || is_parameter_marker);
1357 case MYSQL_TYPE_INT24:
1358 case MYSQL_TYPE_LONG:
1359 case MYSQL_TYPE_LONGLONG:
1360 case MYSQL_TYPE_SHORT:
1361 case MYSQL_TYPE_TINY:
1362 return true;
1363 default:
1364 return false;
1365 }
1366 }
1367
1368
1369 /**
1370 Do type checking on all provided arguments, as well as settings maybe_null to
1371 the appropriate value.
1372 */
fix_fields(THD * thd,Item ** ref)1373 bool Item_func_geomfromgeojson::fix_fields(THD *thd, Item **ref)
1374 {
1375 if (Item_geometry_func::fix_fields(thd, ref))
1376 return true;
1377
1378 switch (arg_count)
1379 {
1380 case 3:
1381 {
1382 // Validate SRID argument
1383 if (!check_argument_valid_integer(args[2]))
1384 {
1385 my_error(ER_INCORRECT_TYPE, MYF(0), "SRID", func_name());
1386 return true;
1387 }
1388 maybe_null= (args[0]->maybe_null || args[1]->maybe_null ||
1389 args[2]->maybe_null);
1390 }
1391 // Fall through.
1392 case 2:
1393 {
1394 // Validate options argument
1395 if (!check_argument_valid_integer(args[1]))
1396 {
1397 my_error(ER_INCORRECT_TYPE, MYF(0), "options", func_name());
1398 return true;
1399 }
1400 maybe_null= (args[0]->maybe_null || args[1]->maybe_null);
1401 }
1402 // Fall through.
1403 case 1:
1404 {
1405 /*
1406 Validate GeoJSON argument type. We do not allow binary data as GeoJSON
1407 argument.
1408 */
1409 bool is_binary_charset= (args[0]->collation.collation == &my_charset_bin);
1410 bool is_parameter_marker= (args[0]->type() == PARAM_ITEM);
1411 switch (args[0]->field_type())
1412 {
1413 case MYSQL_TYPE_NULL:
1414 break;
1415 case MYSQL_TYPE_JSON:
1416 case MYSQL_TYPE_VARCHAR:
1417 case MYSQL_TYPE_VAR_STRING:
1418 case MYSQL_TYPE_BLOB:
1419 case MYSQL_TYPE_TINY_BLOB:
1420 case MYSQL_TYPE_MEDIUM_BLOB:
1421 case MYSQL_TYPE_LONG_BLOB:
1422 if (is_binary_charset && !is_parameter_marker)
1423 {
1424 my_error(ER_INCORRECT_TYPE, MYF(0), "geojson", func_name());
1425 return true;
1426 }
1427 break;
1428 default:
1429 my_error(ER_INCORRECT_TYPE, MYF(0), "geojson", func_name());
1430 return true;
1431 }
1432 maybe_null= args[0]->maybe_null;
1433 break;
1434 }
1435 }
1436
1437 /*
1438 Set maybe_null always to true. This is because the following GeoJSON input
1439 will return SQL NULL:
1440
1441 {
1442 "type": "Feature",
1443 "geometry": null,
1444 "properties": { "name": "Foo Bar" }
1445 }
1446 */
1447 maybe_null= true;
1448 return false;
1449 }
1450
1451
1452 /**
1453 Converts a wkbType to the corresponding GeoJSON type.
1454
1455 @param type The WKB Type to convert.
1456
1457 @return The corresponding GeoJSON type, or NULL if no such type exists.
1458 */
1459 const char *
wkbtype_to_geojson_type(Geometry::wkbType type)1460 wkbtype_to_geojson_type(Geometry::wkbType type)
1461 {
1462 switch (type)
1463 {
1464 case Geometry::wkb_geometrycollection:
1465 return Item_func_geomfromgeojson::GEOMETRYCOLLECTION_TYPE;
1466 case Geometry::wkb_point:
1467 return Item_func_geomfromgeojson::POINT_TYPE;
1468 case Geometry::wkb_multipoint:
1469 return Item_func_geomfromgeojson::MULTIPOINT_TYPE;
1470 case Geometry::wkb_linestring:
1471 return Item_func_geomfromgeojson::LINESTRING_TYPE;
1472 case Geometry::wkb_multilinestring:
1473 return Item_func_geomfromgeojson::MULTILINESTRING_TYPE;
1474 case Geometry::wkb_polygon:
1475 return Item_func_geomfromgeojson::POLYGON_TYPE;
1476 case Geometry::wkb_multipolygon:
1477 return Item_func_geomfromgeojson::MULTIPOLYGON_TYPE;
1478 case Geometry::wkb_invalid_type:
1479 case Geometry::wkb_polygon_inner_rings:
1480 default:
1481 return NULL;
1482 }
1483 }
1484
1485
1486 /**
1487 Append a GeoJSON array with coordinates to the writer at the current position.
1488
1489 The WKB parser must be positioned at the beginning of the coordinates.
1490 There must exactly two coordinates in the array (x and y). The coordinates are
1491 rounded to the number of decimals specified in the variable
1492 max_decimal_digits.:
1493
1494 max_decimal_digits == 2: 12.789 => 12.79
1495 10 => 10.00
1496
1497 @param parser WKB parser with position set to the beginning of the
1498 coordinates.
1499 @param array JSON array to append the result to.
1500 @param mbr A bounding box, which will be updated with data from the
1501 coordinates.
1502 @param calling_function Name of user-invoked function
1503 @param max_decimal_digits Max length of decimal numbers
1504 @param add_bounding_box True if a bounding box should be added
1505 @param add_short_crs_urn True if we should add short format CRS URN
1506 @param add_long_crs_urn True if we should add long format CRS URN
1507 @param geometry_srid Spacial Reference System Identifier being used
1508
1509 @return false on success, true otherwise.
1510 */
1511 bool
append_coordinates(Geometry::wkb_parser * parser,Json_array * array,MBR * mbr,const char * calling_function,int max_decimal_digits,bool add_bounding_box,bool add_short_crs_urn,bool add_long_crs_urn,uint32 geometry_srid)1512 append_coordinates(Geometry::wkb_parser *parser, Json_array *array, MBR *mbr,
1513 const char *calling_function,
1514 int max_decimal_digits,
1515 bool add_bounding_box,
1516 bool add_short_crs_urn,
1517 bool add_long_crs_urn,
1518 uint32 geometry_srid)
1519 {
1520 point_xy coordinates;
1521 if (parser->scan_xy(&coordinates))
1522 {
1523 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1524 return true;
1525 }
1526
1527 double x_value=
1528 my_double_round(coordinates.x, max_decimal_digits, true, false);
1529 double y_value=
1530 my_double_round(coordinates.y, max_decimal_digits, true, false);
1531
1532 if (array->append_alias(new (std::nothrow) Json_double(x_value)) ||
1533 array->append_alias(new (std::nothrow) Json_double(y_value)))
1534 {
1535 return true;
1536 }
1537
1538 if (add_bounding_box)
1539 mbr->add_xy(x_value, y_value);
1540 return false;
1541 }
1542
1543
1544 /**
1545 Append a GeoJSON LineString object to the writer at the current position.
1546
1547 The parser must be positioned after the LineString header, and there must be
1548 at least one coordinate array in the linestring.
1549
1550 @param parser WKB parser with position set to after the LineString header.
1551 @param points JSON array to append the result to.
1552 @param mbr A bounding box, which will be updated with data from the
1553 LineString.
1554 @param calling_function Name of user-invoked function
1555 @param max_decimal_digits Max length of decimal numbers
1556 @param add_bounding_box True if a bounding box should be added
1557 @param add_short_crs_urn True if we should add short format CRS URN
1558 @param add_long_crs_urn True if we should add long format CRS URN
1559 @param geometry_srid Spacial Reference System Identifier being used
1560
1561 @return false on success, true otherwise.
1562 */
1563 bool
append_linestring(Geometry::wkb_parser * parser,Json_array * points,MBR * mbr,const char * calling_function,int max_decimal_digits,bool add_bounding_box,bool add_short_crs_urn,bool add_long_crs_urn,uint32 geometry_srid)1564 append_linestring(Geometry::wkb_parser *parser, Json_array *points, MBR *mbr,
1565 const char *calling_function,
1566 int max_decimal_digits,
1567 bool add_bounding_box,
1568 bool add_short_crs_urn,
1569 bool add_long_crs_urn,
1570 uint32 geometry_srid)
1571 {
1572 uint32 num_points= 0;
1573 if (parser->scan_non_zero_uint4(&num_points))
1574 {
1575 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1576 return true;
1577 }
1578
1579 while (num_points--)
1580 {
1581 Json_array *point= new (std::nothrow) Json_array();
1582 if (point == NULL || points->append_alias(point) ||
1583 append_coordinates(parser, point, mbr, calling_function,
1584 max_decimal_digits,
1585 add_bounding_box,
1586 add_short_crs_urn,
1587 add_long_crs_urn,
1588 geometry_srid))
1589 {
1590 return true;
1591 }
1592 }
1593
1594 return false;
1595 }
1596
1597
1598 /**
1599 Append a GeoJSON Polygon object to the writer at the current position.
1600
1601 The parser must be positioned after the Polygon header, and all coordinate
1602 arrays must contain at least one value.
1603
1604 @param parser WKB parser with position set to after the Polygon header.
1605 @param polygon_rings JSON array to append the result to.
1606 @param mbr A bounding box, which will be updated with data from the Polygon.
1607 @param calling_function Name of user-invoked function
1608 @param max_decimal_digits Max length of decimal numbers
1609 @param add_bounding_box True if a bounding box should be added
1610 @param add_short_crs_urn True if we should add short format CRS URN
1611 @param add_long_crs_urn True if we should add long format CRS URN
1612 @param geometry_srid Spacial Reference System Identifier being used
1613
1614 @return false on success, true otherwise.
1615 */
append_polygon(Geometry::wkb_parser * parser,Json_array * polygon_rings,MBR * mbr,const char * calling_function,int max_decimal_digits,bool add_bounding_box,bool add_short_crs_urn,bool add_long_crs_urn,uint32 geometry_srid)1616 bool append_polygon(Geometry::wkb_parser *parser,
1617 Json_array *polygon_rings, MBR *mbr, const char *calling_function,
1618 int max_decimal_digits,
1619 bool add_bounding_box,
1620 bool add_short_crs_urn,
1621 bool add_long_crs_urn,
1622 uint32 geometry_srid)
1623 {
1624 uint32 num_inner_rings= 0;
1625 if (parser->scan_non_zero_uint4(&num_inner_rings))
1626 {
1627 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1628 return true;
1629 }
1630
1631 while (num_inner_rings--)
1632 {
1633 Json_array *polygon_ring= new (std::nothrow) Json_array();
1634 if (polygon_ring == NULL || polygon_rings->append_alias(polygon_ring))
1635 return true;
1636
1637 uint32 num_points= 0;
1638 if (parser->scan_non_zero_uint4(&num_points))
1639 {
1640 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1641 return true;
1642 }
1643
1644 while (num_points--)
1645 {
1646 Json_array *point = new (std::nothrow) Json_array();
1647 if (point == NULL || polygon_ring->append_alias(point) ||
1648 append_coordinates(parser, point, mbr, calling_function,
1649 max_decimal_digits,
1650 add_bounding_box,
1651 add_short_crs_urn,
1652 add_long_crs_urn,
1653 geometry_srid))
1654 {
1655 return true;
1656 }
1657 }
1658 }
1659
1660 return false;
1661 }
1662
1663
1664 /**
1665 Appends a GeoJSON bounding box to the rapidjson output buffer.
1666
1667 @param mbr Bounding box to write.
1668 @param geometry The JSON object to append the bounding box to.
1669
1670 @return false on success, true otherwise.
1671 */
append_bounding_box(MBR * mbr,Json_object * geometry)1672 bool append_bounding_box(MBR *mbr, Json_object *geometry)
1673 {
1674 assert(GEOM_DIM == 2);
1675
1676 Json_array *bbox_array= new (std::nothrow) Json_array();
1677 if (bbox_array == NULL ||
1678 geometry->add_alias("bbox", bbox_array) ||
1679 bbox_array->append_alias(new (std::nothrow) Json_double(mbr->xmin)) ||
1680 bbox_array->append_alias(new (std::nothrow) Json_double(mbr->ymin)) ||
1681 bbox_array->append_alias(new (std::nothrow) Json_double(mbr->xmax)) ||
1682 bbox_array->append_alias(new (std::nothrow) Json_double(mbr->ymax)))
1683 {
1684 return true;
1685 }
1686
1687 return false;
1688 }
1689
1690
1691 /**
1692 Appends a GeoJSON CRS object to the rapidjson output buffer.
1693
1694 If both add_long_crs_urn and add_short_crs_urn is specified, the long CRS URN
1695 is preferred as mentioned in the GeoJSON specification:
1696
1697 "OGC CRS URNs such as "urn:ogc:def:crs:OGC:1.3:CRS84" shall be preferred
1698 over legacy identifiers such as "EPSG:4326""
1699
1700 @param geometry The JSON object to append the CRS object to.
1701 @param max_decimal_digits Max length of decimal numbers
1702 @param add_bounding_box True if a bounding box should be added
1703 @param add_short_crs_urn True if we should add short format CRS URN
1704 @param add_long_crs_urn True if we should add long format CRS URN
1705 @param geometry_srid Spacial Reference System Identifier being used
1706
1707 @return false on success, true otherwise.
1708 */
append_crs(Json_object * geometry,int max_decimal_digits,bool add_bounding_box,bool add_short_crs_urn,bool add_long_crs_urn,uint32 geometry_srid)1709 bool append_crs(Json_object *geometry,
1710 int max_decimal_digits,
1711 bool add_bounding_box,
1712 bool add_short_crs_urn,
1713 bool add_long_crs_urn,
1714 uint32 geometry_srid)
1715 {
1716 assert(add_long_crs_urn || add_short_crs_urn);
1717 assert(geometry_srid > 0);
1718
1719 Json_object *crs_object= new (std::nothrow) Json_object();
1720 if (crs_object == NULL || geometry->add_alias("crs", crs_object) ||
1721 crs_object->add_alias("type", new (std::nothrow) Json_string("name")))
1722 {
1723 return true;
1724 }
1725
1726 Json_object *crs_properties= new (std::nothrow) Json_object();
1727 if (crs_properties == NULL ||
1728 crs_object->add_alias("properties", crs_properties))
1729 {
1730 return true;
1731 }
1732
1733 // Max width of SRID + '\0'
1734 char srid_string[MAX_INT_WIDTH + 1];
1735 llstr(geometry_srid, srid_string);
1736
1737 char crs_name[MAX_CRS_WIDTH];
1738 if (add_long_crs_urn)
1739 strcpy(crs_name, Item_func_geomfromgeojson::LONG_EPSG_PREFIX);
1740 else if (add_short_crs_urn)
1741 strcpy(crs_name, Item_func_geomfromgeojson::SHORT_EPSG_PREFIX);
1742
1743 strcat(crs_name, srid_string);
1744 if (crs_properties->add_alias("name",
1745 new (std::nothrow) Json_string(crs_name)))
1746 {
1747 return true;
1748 }
1749
1750 return false;
1751 }
1752
1753
1754 /**
1755 Reads a WKB GEOMETRY from input and writes the equivalent GeoJSON to the
1756 output. If a GEOMETRYCOLLECTION is found, this function will call itself for
1757 each GEOMETRY in the collection.
1758
1759 @param parser The WKB input to read from, positioned at the start of
1760 GEOMETRY header.
1761 @param geometry JSON object to append the result to.
1762 @param is_root_object Indicating if the current GEOMETRY is the root object
1763 in the output GeoJSON.
1764 @param mbr A bounding box, which will be updated with data from all the
1765 GEOMETRIES found in the input.
1766 @param calling_function Name of the user-invoked function
1767 @param max_decimal_digits Max length of decimal numbers
1768 @param add_bounding_box True if a bounding box should be added
1769 @param add_short_crs_urn True if we should add short format CRS URN
1770 @param add_long_crs_urn True if we should add long format CRS URN
1771 @param geometry_srid Spacial Reference System Identifier being used
1772
1773 @return false on success, true otherwise.
1774 */
1775 bool
append_geometry(Geometry::wkb_parser * parser,Json_object * geometry,bool is_root_object,MBR * mbr,const char * calling_function,int max_decimal_digits,bool add_bounding_box,bool add_short_crs_urn,bool add_long_crs_urn,uint32 geometry_srid)1776 append_geometry(Geometry::wkb_parser *parser, Json_object *geometry,
1777 bool is_root_object, MBR *mbr, const char *calling_function,
1778 int max_decimal_digits,
1779 bool add_bounding_box,
1780 bool add_short_crs_urn,
1781 bool add_long_crs_urn,
1782 uint32 geometry_srid)
1783 {
1784 // Check of wkb_type is within allowed range.
1785 wkb_header header;
1786 if (parser->scan_wkb_header(&header) ||
1787 header.wkb_type < Geometry::wkb_first ||
1788 header.wkb_type > Geometry::wkb_geometrycollection)
1789 {
1790 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1791 return true;
1792 }
1793
1794 const char *geojson_type=
1795 wkbtype_to_geojson_type(static_cast<Geometry::wkbType>(header.wkb_type));
1796 if (geometry->add_alias("type", new (std::nothrow) Json_string(geojson_type)))
1797 return true;
1798
1799 /*
1800 Use is_mbr_empty to check if we encounter any empty GEOMETRY collections.
1801 In that case, we don't want to write a bounding box to the GeoJSON output.
1802 */
1803 bool is_mbr_empty= false;
1804
1805 switch (header.wkb_type)
1806 {
1807 case Geometry::wkb_point:
1808 {
1809 Json_array *point= new (std::nothrow) Json_array();
1810 if (point == NULL || geometry->add_alias("coordinates", point) ||
1811 append_coordinates(parser, point, mbr, calling_function,
1812 max_decimal_digits,
1813 add_bounding_box,
1814 add_short_crs_urn,
1815 add_long_crs_urn,
1816 geometry_srid))
1817 {
1818 return true;
1819 }
1820 break;
1821 }
1822 case Geometry::wkb_linestring:
1823 {
1824 Json_array *points= new (std::nothrow) Json_array();
1825 if (points == NULL || geometry->add_alias("coordinates", points) ||
1826 append_linestring(parser, points, mbr, calling_function,
1827 max_decimal_digits,
1828 add_bounding_box,
1829 add_short_crs_urn,
1830 add_long_crs_urn,
1831 geometry_srid))
1832 {
1833 return true;
1834 }
1835 break;
1836 }
1837 case Geometry::wkb_polygon:
1838 {
1839 Json_array *polygon_rings= new (std::nothrow) Json_array();
1840 if (polygon_rings == NULL ||
1841 geometry->add_alias("coordinates", polygon_rings) ||
1842 append_polygon(parser, polygon_rings, mbr, calling_function,
1843 max_decimal_digits,
1844 add_bounding_box,
1845 add_short_crs_urn,
1846 add_long_crs_urn,
1847 geometry_srid))
1848 {
1849 return true;
1850 }
1851 break;
1852 }
1853 case Geometry::wkb_multipoint:
1854 case Geometry::wkb_multipolygon:
1855 case Geometry::wkb_multilinestring:
1856 {
1857 uint32 num_items= 0;
1858 if (parser->scan_non_zero_uint4(&num_items))
1859 {
1860 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1861 return true;
1862 }
1863
1864 Json_array *collection= new (std::nothrow) Json_array();
1865 if (collection == NULL || geometry->add_alias("coordinates", collection))
1866 return true;
1867
1868 while (num_items--)
1869 {
1870 if (parser->skip_wkb_header())
1871 {
1872 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1873 return true;
1874 }
1875 else
1876 {
1877 bool result= false;
1878 Json_array *points= new (std::nothrow) Json_array();
1879 if (points == NULL || collection->append_alias(points))
1880 return true;
1881
1882 if (header.wkb_type == Geometry::wkb_multipoint)
1883 result= append_coordinates(parser, points, mbr, calling_function,
1884 max_decimal_digits,
1885 add_bounding_box,
1886 add_short_crs_urn,
1887 add_long_crs_urn,
1888 geometry_srid);
1889 else if (header.wkb_type == Geometry::wkb_multipolygon)
1890 result= append_polygon(parser, points, mbr, calling_function,
1891 max_decimal_digits,
1892 add_bounding_box,
1893 add_short_crs_urn,
1894 add_long_crs_urn,
1895 geometry_srid);
1896 else if (header.wkb_type == Geometry::wkb_multilinestring)
1897 result= append_linestring(parser, points, mbr, calling_function,
1898 max_decimal_digits,
1899 add_bounding_box,
1900 add_short_crs_urn,
1901 add_long_crs_urn,
1902 geometry_srid);
1903 else
1904 assert(false);
1905
1906 if (result)
1907 return true;
1908 }
1909 }
1910 break;
1911 }
1912 case Geometry::wkb_geometrycollection:
1913 {
1914 uint32 num_geometries= 0;
1915 if (parser->scan_uint4(&num_geometries))
1916 {
1917 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1918 return true;
1919 }
1920
1921 is_mbr_empty= (num_geometries == 0);
1922
1923 Json_array *collection= new (std::nothrow) Json_array();
1924 if (collection == NULL || geometry->add_alias("geometries", collection))
1925 return true;
1926
1927 while (num_geometries--)
1928 {
1929 // Create a new MBR for the collection.
1930 MBR subcollection_mbr;
1931 Json_object *sub_geometry= new (std::nothrow) Json_object();
1932 if (sub_geometry == NULL || collection->append_alias(sub_geometry) ||
1933 append_geometry(parser, sub_geometry, false, &subcollection_mbr, calling_function,
1934 max_decimal_digits,
1935 add_bounding_box,
1936 add_short_crs_urn,
1937 add_long_crs_urn,
1938 geometry_srid))
1939 {
1940 return true;
1941 }
1942
1943 if (add_bounding_box)
1944 mbr->add_mbr(&subcollection_mbr);
1945 }
1946 break;
1947 }
1948 default:
1949 {
1950 // This should not happen, since we did a check on wkb_type earlier.
1951 /* purecov: begin inspected */
1952 assert(false);
1953 return true;
1954 /* purecov: end inspected */
1955 }
1956 }
1957
1958 // Only add a CRS object if the SRID of the GEOMETRY is not 0.
1959 if (is_root_object && (add_long_crs_urn || add_short_crs_urn) &&
1960 geometry_srid > 0)
1961 {
1962 append_crs(geometry,
1963 max_decimal_digits,
1964 add_bounding_box,
1965 add_short_crs_urn,
1966 add_long_crs_urn,
1967 geometry_srid);
1968 }
1969
1970 if (add_bounding_box && !is_mbr_empty)
1971 append_bounding_box(mbr, geometry);
1972
1973 return false;
1974 }
1975
1976 /** The contract for this function is found in item_json_func.h */
geometry_to_json(Json_wrapper * wr,Item * geometry_arg,const char * calling_function,int max_decimal_digits,bool add_bounding_box,bool add_short_crs_urn,bool add_long_crs_urn,uint32 * geometry_srid)1977 bool geometry_to_json(Json_wrapper *wr, Item *geometry_arg, const char *calling_function,
1978 int max_decimal_digits,
1979 bool add_bounding_box,
1980 bool add_short_crs_urn,
1981 bool add_long_crs_urn,
1982 uint32 *geometry_srid)
1983 {
1984 String arg_val;
1985 String *swkb= geometry_arg->val_str(&arg_val);
1986 if ((geometry_arg->null_value))
1987 return false;
1988
1989 Geometry::wkb_parser parser(swkb->ptr(), swkb->ptr() + swkb->length());
1990 if (parser.scan_uint4(geometry_srid))
1991 {
1992 my_error(ER_GIS_INVALID_DATA, MYF(0), calling_function);
1993 return true;
1994 }
1995
1996 /*
1997 append_geometry() will go through the WKB and call itself recursivly if
1998 geometry collections are encountered. For each recursive call, a new MBR
1999 is created. The function will fail if it encounters invalid data in the
2000 WKB input.
2001 */
2002 MBR mbr;
2003 Json_object *geojson_object= new (std::nothrow) Json_object();
2004
2005 if (geojson_object == NULL ||
2006 append_geometry(&parser, geojson_object, true, &mbr, calling_function,
2007 max_decimal_digits,
2008 add_bounding_box,
2009 add_short_crs_urn,
2010 add_long_crs_urn,
2011 *geometry_srid))
2012 {
2013 delete geojson_object;
2014 return true;
2015 }
2016
2017 Json_wrapper w(geojson_object);
2018 wr->steal(&w);
2019 return false;
2020 }
2021
2022 /**
2023 Create a GeoJSON object, according to GeoJSON specification revison 1.0.
2024 */
val_json(Json_wrapper * wr)2025 bool Item_func_as_geojson::val_json(Json_wrapper *wr)
2026 {
2027 assert(fixed == TRUE);
2028
2029 if ((arg_count > 1 && parse_maxdecimaldigits_argument()) ||
2030 (arg_count > 2 && parse_options_argument()))
2031 {
2032 if (null_value && !current_thd->is_error())
2033 return false;
2034 else
2035 return error_json();
2036 }
2037
2038 /*
2039 Set maximum number of decimal digits. If maxdecimaldigits argument was not
2040 specified, set unlimited number of decimal digits.
2041 */
2042 if (arg_count < 2)
2043 m_max_decimal_digits= INT_MAX32;
2044
2045 if (geometry_to_json(wr, args[0], func_name(),
2046 m_max_decimal_digits,
2047 m_add_bounding_box,
2048 m_add_short_crs_urn,
2049 m_add_long_crs_urn,
2050 &m_geometry_srid))
2051 {
2052 if (null_value && !current_thd->is_error())
2053 return false;
2054 else
2055 return error_json();
2056 }
2057
2058 null_value= args[0]->null_value;
2059 return false;
2060 }
2061
2062
2063 /**
2064 Parse the value in options argument.
2065
2066 Options is a 3-bit bitmask with the following options:
2067
2068 0 No options (default values).
2069 1 Add a bounding box to the output.
2070 2 Add a short CRS URN to the output. The default format is a
2071 short format ("EPSG:<srid>").
2072 4 Add a long format CRS URN ("urn:ogc:def:crs:EPSG::<srid>"). This
2073 will override option 2. E.g., bitmask 5 and 7 mean the
2074 same: add a bounding box and a long format CRS URN.
2075
2076 If value is out of range (below zero or greater than seven), an error will be
2077 raised. This function expects that the options argument is the third argument
2078 in the function call.
2079
2080 @return false on success, true otherwise (value out of range or similar).
2081 */
parse_options_argument()2082 bool Item_func_as_geojson::parse_options_argument()
2083 {
2084 assert(arg_count > 2);
2085 longlong options_argument= args[2]->val_int();
2086 if ((null_value= args[2]->null_value))
2087 return true;
2088
2089 if (options_argument < 0 || options_argument > 7)
2090 {
2091 char options_string[MAX_BIGINT_WIDTH + 1];
2092 if (args[2]->unsigned_flag)
2093 ullstr(options_argument, options_string);
2094 else
2095 llstr(options_argument, options_string);
2096
2097 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "options", options_string,
2098 func_name());
2099 return true;
2100 }
2101
2102 m_add_bounding_box= options_argument & (1 << 0);
2103 m_add_short_crs_urn= options_argument & (1 << 1);
2104 m_add_long_crs_urn= options_argument & (1 << 2);
2105
2106 if (m_add_long_crs_urn)
2107 m_add_short_crs_urn= false;
2108 return false;
2109 }
2110
2111
2112 /**
2113 Parse the value in maxdecimaldigits argument.
2114
2115 This value MUST be a positive integer. If value is out of range (negative
2116 value or greater than INT_MAX), an error will be raised. This function expects
2117 that the maxdecimaldigits argument is the second argument in the function
2118 call.
2119
2120 @return false on success, true otherwise (negative value or similar).
2121 */
parse_maxdecimaldigits_argument()2122 bool Item_func_as_geojson::parse_maxdecimaldigits_argument()
2123 {
2124 assert(arg_count > 1);
2125 longlong max_decimal_digits_argument = args[1]->val_int();
2126 if ((null_value= args[1]->null_value))
2127 return true;
2128
2129 if (max_decimal_digits_argument < 0 ||
2130 max_decimal_digits_argument > INT_MAX32)
2131 {
2132 char max_decimal_digits_string[MAX_BIGINT_WIDTH + 1];
2133 if (args[1]->unsigned_flag)
2134 ullstr(max_decimal_digits_argument, max_decimal_digits_string);
2135 else
2136 llstr(max_decimal_digits_argument, max_decimal_digits_string);
2137
2138 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "max decimal digits",
2139 max_decimal_digits_string, func_name());
2140 return true;
2141 }
2142
2143 m_max_decimal_digits= static_cast<int>(max_decimal_digits_argument);
2144 return false;
2145 }
2146
2147
2148 /**
2149 Perform type checking on all arguments:
2150
2151 <geometry> argument must be a geometry.
2152 <maxdecimaldigits> must be an integer value.
2153 <options> must be an integer value.
2154
2155 Set maybe_null to the correct value.
2156 */
fix_fields(THD * thd,Item ** ref)2157 bool Item_func_as_geojson::fix_fields(THD *thd, Item **ref)
2158 {
2159 if (Item_json_func::fix_fields(thd, ref))
2160 return true;
2161
2162 /*
2163 We must set maybe_null to true, since the GeoJSON string may be longer than
2164 the packet size.
2165 */
2166 maybe_null= true;
2167
2168 // Check if geometry argument is a geometry type.
2169 bool is_parameter_marker= (args[0]->type() == PARAM_ITEM);
2170 switch (args[0]->field_type())
2171 {
2172 case MYSQL_TYPE_GEOMETRY:
2173 case MYSQL_TYPE_NULL:
2174 break;
2175 default:
2176 {
2177 if (!is_parameter_marker)
2178 {
2179 my_error(ER_INCORRECT_TYPE, MYF(0), "geojson", func_name());
2180 return true;
2181 }
2182 }
2183 }
2184
2185 if (arg_count > 1)
2186 {
2187 if (!Item_func_geomfromgeojson::check_argument_valid_integer(args[1]))
2188 {
2189 my_error(ER_INCORRECT_TYPE, MYF(0), "max decimal digits", func_name());
2190 return true;
2191 }
2192 }
2193
2194 if (arg_count > 2)
2195 {
2196 if (!Item_func_geomfromgeojson::check_argument_valid_integer(args[2]))
2197 {
2198 my_error(ER_INCORRECT_TYPE, MYF(0), "options", func_name());
2199 return true;
2200 }
2201 }
2202 return false;
2203 }
2204
2205
val_str_ascii(String * str)2206 String *Item_func_as_wkt::val_str_ascii(String *str)
2207 {
2208 assert(fixed == 1);
2209 String arg_val;
2210 String *swkb= args[0]->val_str(&arg_val);
2211 Geometry_buffer buffer;
2212 Geometry *geom= NULL;
2213
2214 if ((null_value= (!swkb || args[0]->null_value)))
2215 return 0;
2216
2217 if (!(geom= Geometry::construct(&buffer, swkb)))
2218 {
2219 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2220 return error_str();
2221 }
2222
2223 str->length(0);
2224 if ((null_value= geom->as_wkt(str)))
2225 return 0;
2226
2227 return str;
2228 }
2229
2230
fix_length_and_dec()2231 void Item_func_as_wkt::fix_length_and_dec()
2232 {
2233 collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
2234 max_length=MAX_BLOB_WIDTH;
2235 maybe_null= 1;
2236 }
2237
2238
val_str(String * str)2239 String *Item_func_as_wkb::val_str(String *str)
2240 {
2241 assert(fixed == 1);
2242 String arg_val;
2243 String *swkb= args[0]->val_str(&arg_val);
2244 Geometry_buffer buffer;
2245
2246 if ((null_value= (!swkb || args[0]->null_value)))
2247 return 0;
2248
2249 if (!(Geometry::construct(&buffer, swkb)))
2250 {
2251 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2252 return error_str();
2253 }
2254
2255 str->copy(swkb->ptr() + SRID_SIZE, swkb->length() - SRID_SIZE,
2256 &my_charset_bin);
2257 return str;
2258 }
2259
2260
val_str_ascii(String * str)2261 String *Item_func_geometry_type::val_str_ascii(String *str)
2262 {
2263 assert(fixed == 1);
2264 String *swkb= args[0]->val_str(str);
2265 Geometry_buffer buffer;
2266 Geometry *geom= NULL;
2267
2268 if ((null_value= (!swkb || args[0]->null_value)))
2269 return 0;
2270
2271 if (!(geom= Geometry::construct(&buffer, swkb)))
2272 {
2273 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2274 return error_str();
2275 }
2276 /* String will not move */
2277 str->copy(geom->get_class_info()->m_name.str,
2278 geom->get_class_info()->m_name.length,
2279 &my_charset_latin1);
2280 return str;
2281 }
2282
2283
val_str(String * str)2284 String *Item_func_validate::val_str(String *str)
2285 {
2286 assert(fixed == 1);
2287 String *swkb= args[0]->val_str(&arg_val);
2288 Geometry_buffer buffer;
2289 Geometry *geom= NULL;
2290
2291 if ((null_value= (!swkb || args[0]->null_value)))
2292 return error_str();
2293 if (!(geom= Geometry::construct(&buffer, swkb)))
2294 return error_str();
2295
2296 if (geom->get_srid() != 0)
2297 {
2298 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
2299 return error_str();
2300 }
2301 int isvalid= 0;
2302
2303 try
2304 {
2305 isvalid= check_geometry_valid(geom);
2306 }
2307 catch (...)
2308 {
2309 null_value= true;
2310 handle_gis_exception("ST_Validate");
2311 }
2312
2313 return isvalid ? swkb : error_str();
2314 }
2315
2316
get_geometry_type() const2317 Field::geometry_type Item_func_make_envelope::get_geometry_type() const
2318 {
2319 return Field::GEOM_POLYGON;
2320 }
2321
2322
val_str(String * str)2323 String *Item_func_make_envelope::val_str(String *str)
2324 {
2325 assert(fixed == 1);
2326 String arg_val1, arg_val2;
2327 String *pt1= args[0]->val_str(&arg_val1);
2328 String *pt2= args[1]->val_str(&arg_val2);
2329 Geometry_buffer buffer1, buffer2;
2330 Geometry *geom1= NULL, *geom2= NULL;
2331 uint32 srid;
2332
2333 if ((null_value= (!pt1 || !pt2 ||
2334 args[0]->null_value || args[1]->null_value)))
2335 return error_str();
2336 if ((null_value= (!(geom1= Geometry::construct(&buffer1, pt1)) ||
2337 !(geom2= Geometry::construct(&buffer2, pt2)))))
2338 {
2339 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2340 return error_str();
2341 }
2342
2343 if (geom1->get_type() != Geometry::wkb_point ||
2344 geom2->get_type() != Geometry::wkb_point ||
2345 geom1->get_srid() != 0 ||
2346 geom2->get_srid() != 0)
2347 {
2348 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
2349 return error_str();
2350 }
2351
2352 if (geom1->get_srid() != geom2->get_srid())
2353 {
2354 my_error(ER_GIS_DIFFERENT_SRIDS, MYF(0), func_name(),
2355 geom1->get_srid(), geom2->get_srid());
2356 return error_str();
2357 }
2358
2359 Gis_point *gpt1= static_cast<Gis_point *>(geom1);
2360 Gis_point *gpt2= static_cast<Gis_point *>(geom2);
2361 double x1= gpt1->get<0>(), y1= gpt1->get<1>();
2362 double x2= gpt2->get<0>(), y2= gpt2->get<1>();
2363
2364 if (!my_isfinite(x1) || !my_isfinite(x2) ||
2365 !my_isfinite(y1) || !my_isfinite(y2))
2366 {
2367 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2368 return error_str();
2369 }
2370
2371 MBR mbr;
2372
2373 if (x1 < x2)
2374 {
2375 mbr.xmin= x1;
2376 mbr.xmax= x2;
2377 }
2378 else
2379 {
2380 mbr.xmin= x2;
2381 mbr.xmax= x1;
2382 }
2383
2384 if (y1 < y2)
2385 {
2386 mbr.ymin= y1;
2387 mbr.ymax= y2;
2388 }
2389 else
2390 {
2391 mbr.ymin= y2;
2392 mbr.ymax= y1;
2393 }
2394
2395 int dim= mbr.dimension();
2396 assert(dim >= 0);
2397
2398 /*
2399 Use default invalid SRID because we are computing the envelope on an
2400 "abstract plain", that is, we are not aware of any detailed information
2401 of the coordinate system, we simply suppose the points given to us
2402 are in the abstract cartesian coordinate system, so we always use the
2403 point with minimum coordinates of all dimensions and the point with maximum
2404 coordinates of all dimensions and combine the two groups of coordinate
2405 values to get 2^N points for the N dimension points.
2406 */
2407 srid= 0;
2408 str->set_charset(&my_charset_bin);
2409 str->length(0);
2410 if (str->reserve(GEOM_HEADER_SIZE + 4 + 4 + 5 * POINT_DATA_SIZE, 128))
2411 return error_str();
2412
2413 str->q_append(srid);
2414 str->q_append(static_cast<char>(Geometry::wkb_ndr));
2415
2416 if (dim == 0)
2417 {
2418 str->q_append(static_cast<uint32>(Geometry::wkb_point));
2419 str->q_append(mbr.xmin);
2420 str->q_append(mbr.ymin);
2421 }
2422 else if (dim == 1)
2423 {
2424
2425 str->q_append(static_cast<uint32>(Geometry::wkb_linestring));
2426 str->q_append(static_cast<uint32>(2));
2427 str->q_append(mbr.xmin);
2428 str->q_append(mbr.ymin);
2429 str->q_append(mbr.xmax);
2430 str->q_append(mbr.ymax);
2431 }
2432 else
2433 {
2434 assert(dim == 2);
2435 str->q_append(static_cast<uint32>(Geometry::wkb_polygon));
2436 str->q_append(static_cast<uint32>(1));
2437 str->q_append(static_cast<uint32>(5));
2438 str->q_append(mbr.xmin);
2439 str->q_append(mbr.ymin);
2440 str->q_append(mbr.xmax);
2441 str->q_append(mbr.ymin);
2442 str->q_append(mbr.xmax);
2443 str->q_append(mbr.ymax);
2444 str->q_append(mbr.xmin);
2445 str->q_append(mbr.ymax);
2446 str->q_append(mbr.xmin);
2447 str->q_append(mbr.ymin);
2448 }
2449
2450 return str;
2451 }
2452
2453
get_geometry_type() const2454 Field::geometry_type Item_func_envelope::get_geometry_type() const
2455 {
2456 return Field::GEOM_GEOMETRY;
2457 }
2458
2459
val_str(String * str)2460 String *Item_func_envelope::val_str(String *str)
2461 {
2462 assert(fixed == 1);
2463 String arg_val;
2464 String *swkb= args[0]->val_str(&arg_val);
2465 Geometry_buffer buffer;
2466 Geometry *geom= NULL;
2467 uint32 srid;
2468
2469 if ((null_value= (!swkb || args[0]->null_value)))
2470 {
2471 assert(!swkb && args[0]->null_value);
2472 return NULL;
2473 }
2474
2475 if (!(geom= Geometry::construct(&buffer, swkb)))
2476 {
2477 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2478 return error_str();
2479 }
2480
2481 srid= uint4korr(swkb->ptr());
2482 str->set_charset(&my_charset_bin);
2483 str->length(0);
2484 if (str->reserve(SRID_SIZE, 512))
2485 return error_str();
2486 str->q_append(srid);
2487 if ((null_value= geom->envelope(str)))
2488 {
2489 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2490 return error_str();
2491 }
2492
2493 return str;
2494 }
2495
2496
get_geometry_type() const2497 Field::geometry_type Item_func_centroid::get_geometry_type() const
2498 {
2499 return Field::GEOM_POINT;
2500 }
2501
2502
val_str(String * str)2503 String *Item_func_centroid::val_str(String *str)
2504 {
2505 assert(fixed == 1);
2506 String arg_val;
2507 String *swkb= args[0]->val_str(&arg_val);
2508 Geometry_buffer buffer;
2509 Geometry *geom= NULL;
2510
2511 if ((null_value= (!swkb || args[0]->null_value)))
2512 return NULL;
2513 if (!(geom= Geometry::construct(&buffer, swkb)))
2514 {
2515 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2516 return error_str();
2517 }
2518
2519 str->length(0);
2520 str->set_charset(&my_charset_bin);
2521
2522 if (geom->get_geotype() != Geometry::wkb_geometrycollection &&
2523 geom->normalize_ring_order() == NULL)
2524 {
2525 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2526 return error_str();
2527 }
2528
2529 null_value= bg_centroid<bgcs::cartesian>(geom, str);
2530 if (null_value)
2531 return error_str();
2532 return str;
2533 }
2534
2535
2536 /**
2537 Accumulate a geometry's all vertex points into a multipoint.
2538 It implements the WKB_scanner_event_handler interface so as to be registered
2539 into wkb_scanner and be notified of WKB data events.
2540 */
2541 class Point_accumulator : public WKB_scanner_event_handler
2542 {
2543 Gis_multi_point *m_mpts;
2544 const void *pt_start;
2545 public:
Point_accumulator(Gis_multi_point * mpts)2546 explicit Point_accumulator(Gis_multi_point *mpts)
2547 :m_mpts(mpts), pt_start(NULL)
2548 {
2549 }
2550
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void * wkb,uint32 len,bool has_hdr)2551 virtual void on_wkb_start(Geometry::wkbByteOrder bo,
2552 Geometry::wkbType geotype,
2553 const void *wkb, uint32 len, bool has_hdr)
2554 {
2555 if (geotype == Geometry::wkb_point)
2556 {
2557 Gis_point pt(wkb, POINT_DATA_SIZE,
2558 Geometry::Flags_t(Geometry::wkb_point, len),
2559 m_mpts->get_srid());
2560 m_mpts->push_back(pt);
2561 pt_start= wkb;
2562 }
2563 }
2564
2565
on_wkb_end(const void * wkb)2566 virtual void on_wkb_end(const void *wkb)
2567 {
2568 if (pt_start)
2569 assert(static_cast<const char *>(pt_start) + POINT_DATA_SIZE == wkb);
2570
2571 pt_start= NULL;
2572 }
2573 };
2574
2575
2576 /**
2577 Retrieve from a geometry collection geometries of the same base type into
2578 a multi-xxx geometry object. For example, group all points and multipoints
2579 into a single multipoint object, where the base type is point.
2580
2581 @tparam Base_type the base type to group.
2582 */
2583 template <typename Base_type>
2584 class Geometry_grouper : public WKB_scanner_event_handler
2585 {
2586 std::vector<Geometry::wkbType> m_types;
2587 std::vector<const void *> m_ptrs;
2588
2589 typedef Gis_wkb_vector<Base_type> Group_type;
2590 Group_type *m_group;
2591 Gis_geometry_collection *m_collection;
2592 String *m_gcbuf;
2593 Geometry::wkbType m_target_type;
2594
2595 public:
Geometry_grouper(Group_type * out)2596 explicit Geometry_grouper(Group_type *out)
2597 :m_group(out), m_collection(NULL), m_gcbuf(NULL)
2598 {
2599 switch (out->get_type())
2600 {
2601 case Geometry::wkb_multipoint:
2602 m_target_type= Geometry::wkb_point;
2603 break;
2604 case Geometry::wkb_multilinestring:
2605 m_target_type= Geometry::wkb_linestring;
2606 break;
2607 case Geometry::wkb_multipolygon:
2608 m_target_type= Geometry::wkb_polygon;
2609 break;
2610 default:
2611 assert(false);
2612 break;
2613 }
2614 }
2615
2616 /*
2617 Group polygons and multipolygons into a geometry collection.
2618 */
Geometry_grouper(Gis_geometry_collection * out,String * gcbuf)2619 Geometry_grouper(Gis_geometry_collection *out, String *gcbuf)
2620 :m_group(NULL), m_collection(out), m_gcbuf(gcbuf)
2621 {
2622 m_target_type= Geometry::wkb_polygon;
2623 assert(out != NULL && gcbuf != NULL);
2624 }
2625
2626
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void * wkb,uint32 len,bool has_hdr)2627 virtual void on_wkb_start(Geometry::wkbByteOrder bo,
2628 Geometry::wkbType geotype,
2629 const void *wkb, uint32 len, bool has_hdr)
2630 {
2631 m_types.push_back(geotype);
2632 m_ptrs.push_back(wkb);
2633
2634 if (m_types.size() == 1)
2635 assert(geotype == Geometry::wkb_geometrycollection);
2636 }
2637
2638
on_wkb_end(const void * wkb_end)2639 virtual void on_wkb_end(const void *wkb_end)
2640 {
2641 Geometry::wkbType geotype= m_types.back();
2642 m_types.pop_back();
2643
2644 const void *wkb_start= m_ptrs.back();
2645 m_ptrs.pop_back();
2646
2647 if (geotype != m_target_type || m_types.size() == 0)
2648 return;
2649
2650 Geometry::wkbType ptype= m_types.back();
2651 size_t len= static_cast<const char *>(wkb_end) -
2652 static_cast<const char *>(wkb_start);
2653
2654 /*
2655 We only group independent geometries, points in linestrings or polygons
2656 are not independent, nor are linestrings in polygons.
2657 */
2658 if (m_target_type == geotype && m_group != NULL &&
2659 ((m_target_type == Geometry::wkb_point &&
2660 (ptype == Geometry::wkb_geometrycollection ||
2661 ptype == Geometry::wkb_multipoint)) ||
2662 (m_target_type == Geometry::wkb_linestring &&
2663 (ptype == Geometry::wkb_geometrycollection ||
2664 ptype == Geometry::wkb_multilinestring)) ||
2665 (m_target_type == Geometry::wkb_polygon &&
2666 (ptype == Geometry::wkb_geometrycollection ||
2667 ptype == Geometry::wkb_multipolygon))))
2668 {
2669 Base_type g(wkb_start, len, Geometry::Flags_t(m_target_type, 0), 0);
2670 m_group->push_back(g);
2671 assert(m_collection == NULL && m_gcbuf == NULL);
2672 }
2673
2674 if (m_collection != NULL && (geotype == Geometry::wkb_polygon ||
2675 geotype == Geometry::wkb_multipolygon))
2676 {
2677 assert(m_group == NULL && m_gcbuf != NULL);
2678 String str(static_cast<const char *>(wkb_start), len, &my_charset_bin);
2679 m_collection->append_geometry(m_collection->get_srid(), geotype,
2680 &str, m_gcbuf);
2681 }
2682 }
2683 };
2684
2685
2686 /*
2687 Compute a geometry collection's centroid in demension decreasing order:
2688 If it has polygons, make them a multipolygon and compute its centroid as the
2689 result; otherwise compose a multilinestring and compute its centroid as the
2690 result; otherwise compose a multipoint and compute its centroid as the result.
2691 @param geom the geometry collection.
2692 @param[out] respt takes out the centroid point result.
2693 @param[out] null_value returns whether the result is NULL.
2694 @return whether got error, true if got error and false if successful.
2695 */
2696 template <typename Coordsys>
geometry_collection_centroid(const Geometry * geom,typename BG_models<Coordsys>::Point * respt,my_bool * null_value)2697 bool geometry_collection_centroid(const Geometry *geom,
2698 typename BG_models<Coordsys>::
2699 Point *respt, my_bool *null_value)
2700 {
2701 typename BG_models<Coordsys>::Multipolygon mplgn;
2702 Geometry_grouper<typename BG_models<Coordsys>::Polygon>
2703 plgn_grouper(&mplgn);
2704
2705 const char *wkb_start= geom->get_cptr();
2706 uint32 wkb_len0, wkb_len= geom->get_data_size();
2707 *null_value= false;
2708
2709 /*
2710 The geometries with largest dimension determine the centroid, because
2711 components of lower dimensions weighs nothing in comparison.
2712 */
2713 wkb_len0= wkb_len;
2714 wkb_scanner(wkb_start, &wkb_len,
2715 Geometry::wkb_geometrycollection, false, &plgn_grouper);
2716 if (mplgn.size() > 0)
2717 {
2718 if (mplgn.normalize_ring_order() == NULL)
2719 return true;
2720
2721 boost::geometry::centroid(mplgn, *respt);
2722 }
2723 else
2724 {
2725 typename BG_models<Coordsys>::Multilinestring mls;
2726 wkb_len= wkb_len0;
2727 Geometry_grouper<typename BG_models<Coordsys>::Linestring>
2728 ls_grouper(&mls);
2729 wkb_scanner(wkb_start, &wkb_len,
2730 Geometry::wkb_geometrycollection, false, &ls_grouper);
2731 if (mls.size() > 0)
2732 boost::geometry::centroid(mls, *respt);
2733 else
2734 {
2735 typename BG_models<Coordsys>::Multipoint mpts;
2736 wkb_len= wkb_len0;
2737 Geometry_grouper<typename BG_models<Coordsys>::Point>
2738 pt_grouper(&mpts);
2739 wkb_scanner(wkb_start, &wkb_len,
2740 Geometry::wkb_geometrycollection, false, &pt_grouper);
2741 if (mpts.size() > 0)
2742 boost::geometry::centroid(mpts, *respt);
2743 else
2744 *null_value= true;
2745 }
2746 }
2747
2748 return false;
2749 }
2750
2751
2752 template <typename Coordsys>
bg_centroid(const Geometry * geom,String * ptwkb)2753 bool Item_func_centroid::bg_centroid(const Geometry *geom, String *ptwkb)
2754 {
2755 typename BG_models<Coordsys>::Point respt;
2756
2757 // Release last call's result buffer.
2758 bg_resbuf_mgr.free_result_buffer();
2759
2760 try
2761 {
2762 switch (geom->get_type())
2763 {
2764 case Geometry::wkb_point:
2765 {
2766 typename BG_models<Coordsys>::Point
2767 geo(geom->get_data_ptr(), geom->get_data_size(),
2768 geom->get_flags(), geom->get_srid());
2769 boost::geometry::centroid(geo, respt);
2770 }
2771 break;
2772 case Geometry::wkb_multipoint:
2773 {
2774 typename BG_models<Coordsys>::Multipoint
2775 geo(geom->get_data_ptr(), geom->get_data_size(),
2776 geom->get_flags(), geom->get_srid());
2777 boost::geometry::centroid(geo, respt);
2778 }
2779 break;
2780 case Geometry::wkb_linestring:
2781 {
2782 typename BG_models<Coordsys>::Linestring
2783 geo(geom->get_data_ptr(), geom->get_data_size(),
2784 geom->get_flags(), geom->get_srid());
2785 boost::geometry::centroid(geo, respt);
2786 }
2787 break;
2788 case Geometry::wkb_multilinestring:
2789 {
2790 typename BG_models<Coordsys>::Multilinestring
2791 geo(geom->get_data_ptr(), geom->get_data_size(),
2792 geom->get_flags(), geom->get_srid());
2793 boost::geometry::centroid(geo, respt);
2794 }
2795 break;
2796 case Geometry::wkb_polygon:
2797 {
2798 typename BG_models<Coordsys>::Polygon
2799 geo(geom->get_data_ptr(), geom->get_data_size(),
2800 geom->get_flags(), geom->get_srid());
2801 boost::geometry::centroid(geo, respt);
2802 }
2803 break;
2804 case Geometry::wkb_multipolygon:
2805 {
2806 typename BG_models<Coordsys>::Multipolygon
2807 geo(geom->get_data_ptr(), geom->get_data_size(),
2808 geom->get_flags(), geom->get_srid());
2809 boost::geometry::centroid(geo, respt);
2810 }
2811 break;
2812 case Geometry::wkb_geometrycollection:
2813 if (geometry_collection_centroid<Coordsys>(geom, &respt, &null_value))
2814 {
2815 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2816 null_value= true;
2817 }
2818 break;
2819 default:
2820 assert(false);
2821 break;
2822 }
2823
2824 respt.set_srid(geom->get_srid());
2825 if (!null_value)
2826 null_value= post_fix_result(&bg_resbuf_mgr, respt, ptwkb);
2827 if (!null_value)
2828 bg_resbuf_mgr.set_result_buffer(const_cast<char *>(ptwkb->ptr()));
2829 }
2830 catch (...)
2831 {
2832 null_value= true;
2833 handle_gis_exception("st_centroid");
2834 }
2835
2836 return null_value;
2837 }
2838
get_geometry_type() const2839 Field::geometry_type Item_func_convex_hull::get_geometry_type() const
2840 {
2841 return Field::GEOM_GEOMETRY;
2842 }
2843
val_str(String * str)2844 String *Item_func_convex_hull::val_str(String *str)
2845 {
2846 String arg_val;
2847 String *swkb= args[0]->val_str(&arg_val);
2848 Geometry_buffer buffer;
2849 Geometry *geom= NULL;
2850
2851 if ((null_value= (!swkb || args[0]->null_value)))
2852 return NULL;
2853
2854 if (!(geom= Geometry::construct(&buffer, swkb)))
2855 {
2856 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2857 return error_str();
2858 }
2859
2860 assert(geom->get_coordsys() == Geometry::cartesian);
2861 str->set_charset(&my_charset_bin);
2862 str->length(0);
2863
2864 if (geom->get_geotype() != Geometry::wkb_geometrycollection &&
2865 geom->normalize_ring_order() == NULL)
2866 {
2867 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2868 return error_str();
2869 }
2870
2871 if (bg_convex_hull<bgcs::cartesian>(geom, str))
2872 return error_str();
2873
2874 // By taking over, str owns swkt->ptr and the memory will be released when
2875 // str points to another buffer in next call of this function
2876 // (done in post_fix_result), or when str's owner Item_xxx node is destroyed.
2877 if (geom->get_type() == Geometry::wkb_point)
2878 str->takeover(*swkb);
2879
2880 return str;
2881 }
2882
2883
2884 template <typename Coordsys>
bg_convex_hull(const Geometry * geom,String * res_hull)2885 bool Item_func_convex_hull::bg_convex_hull(const Geometry *geom,
2886 String *res_hull)
2887 {
2888 typename BG_models<Coordsys>::Polygon hull;
2889 typename BG_models<Coordsys>::Linestring line_hull;
2890 Geometry::wkbType geotype= geom->get_type();
2891
2892 // Release last call's result buffer.
2893 bg_resbuf_mgr.free_result_buffer();
2894
2895 try
2896 {
2897 if (geotype == Geometry::wkb_multipoint ||
2898 geotype == Geometry::wkb_linestring ||
2899 geotype == Geometry::wkb_multilinestring ||
2900 geotype == Geometry::wkb_geometrycollection)
2901 {
2902 /*
2903 It's likely that the multilinestring, linestring, geometry collection
2904 and multipoint have all colinear points so the final hull is a
2905 linear hull. If so we must get the linear hull otherwise we will get
2906 an invalid polygon hull.
2907 */
2908 typename BG_models<Coordsys>::Multipoint mpts;
2909 Point_accumulator pt_acc(&mpts);
2910 const char *wkb_start= geom->get_cptr();
2911 uint32 wkb_len= geom->get_data_size();
2912 wkb_scanner(wkb_start, &wkb_len, geotype, false, &pt_acc);
2913 bool isdone= true;
2914 if (mpts.size() == 0)
2915 return (null_value= true);
2916
2917 // Make mpts unique as required by the is_colinear() algorithm.
2918 typename BG_models<Coordsys>::Multipoint distinct_pts;
2919 distinct_pts.resize(mpts.size());
2920 std::sort(mpts.begin(), mpts.end(), bgpt_lt());
2921 typename BG_models<Coordsys>::Multipoint::iterator itr=
2922 std::unique_copy(mpts.begin(), mpts.end(),
2923 distinct_pts.begin(), bgpt_eq());
2924 distinct_pts.resize(itr - distinct_pts.begin());
2925
2926 if (is_colinear(distinct_pts))
2927 {
2928 if (distinct_pts.size() == 1)
2929 {
2930 // Here we have to create a brand new point because res_hull will
2931 // take over its memory, which can't be done to distinct_pts[0].
2932 typename BG_models<Coordsys>::Point pt_hull= distinct_pts[0];
2933 pt_hull.set_srid(geom->get_srid());
2934 null_value= post_fix_result(&bg_resbuf_mgr, pt_hull, res_hull);
2935 }
2936 else
2937 {
2938 boost::geometry::convex_hull(distinct_pts, line_hull);
2939 line_hull.set_srid(geom->get_srid());
2940 // The linestring consists of 4 or more points, but only the
2941 // first two contain real data, so we need to trim it down.
2942 line_hull.resize(2);
2943 null_value= post_fix_result(&bg_resbuf_mgr, line_hull, res_hull);
2944 }
2945 }
2946 else if (geotype == Geometry::wkb_geometrycollection)
2947 {
2948 boost::geometry::convex_hull(mpts, hull);
2949 hull.set_srid(geom->get_srid());
2950 null_value= post_fix_result(&bg_resbuf_mgr, hull, res_hull);
2951 }
2952 else
2953 isdone= false;
2954
2955 if (isdone)
2956 {
2957 if (!null_value)
2958 bg_resbuf_mgr.set_result_buffer(const_cast<char *>(res_hull->ptr()));
2959 return null_value;
2960 }
2961 }
2962
2963 /*
2964 From here on we don't have to consider linear hulls, it's impossible.
2965
2966 In theory we can use above multipoint to get convex hull for all 7 types
2967 of geometries, however we'd better use BG standard logic for each type,
2968 a tricky example would be: imagine an invalid polygon whose inner ring is
2969 completely contains its outer ring inside, BG might return the outer ring
2970 but if using the multipoint to get convexhull, we would get the
2971 inner ring as result instead.
2972 */
2973 switch (geotype)
2974 {
2975 case Geometry::wkb_point:
2976 {
2977 /*
2978 A point's convex hull is the point itself, directly use the point's
2979 WKB buffer, set its header info correctly.
2980 */
2981 assert(geom->get_ownmem() == false &&
2982 geom->has_geom_header_space());
2983 char *p= geom->get_cptr() - GEOM_HEADER_SIZE;
2984 write_geometry_header(p, geom->get_srid(), geom->get_geotype());
2985 return false;
2986 }
2987 break;
2988 case Geometry::wkb_multipoint:
2989 {
2990 typename BG_models<Coordsys>::Multipoint
2991 geo(geom->get_data_ptr(), geom->get_data_size(),
2992 geom->get_flags(), geom->get_srid());
2993 boost::geometry::convex_hull(geo, hull);
2994 }
2995 break;
2996 case Geometry::wkb_linestring:
2997 {
2998 typename BG_models<Coordsys>::Linestring
2999 geo(geom->get_data_ptr(), geom->get_data_size(),
3000 geom->get_flags(), geom->get_srid());
3001 boost::geometry::convex_hull(geo, hull);
3002 }
3003 break;
3004 case Geometry::wkb_multilinestring:
3005 {
3006 typename BG_models<Coordsys>::Multilinestring
3007 geo(geom->get_data_ptr(), geom->get_data_size(),
3008 geom->get_flags(), geom->get_srid());
3009 boost::geometry::convex_hull(geo, hull);
3010 }
3011 break;
3012 case Geometry::wkb_polygon:
3013 {
3014 typename BG_models<Coordsys>::Polygon
3015 geo(geom->get_data_ptr(), geom->get_data_size(),
3016 geom->get_flags(), geom->get_srid());
3017 boost::geometry::convex_hull(geo, hull);
3018 }
3019 break;
3020 case Geometry::wkb_multipolygon:
3021 {
3022 typename BG_models<Coordsys>::Multipolygon
3023 geo(geom->get_data_ptr(), geom->get_data_size(),
3024 geom->get_flags(), geom->get_srid());
3025 boost::geometry::convex_hull(geo, hull);
3026 }
3027 break;
3028 case Geometry::wkb_geometrycollection:
3029 // Handled above.
3030 assert(false);
3031 break;
3032 default:
3033 break;
3034 }
3035
3036 hull.set_srid(geom->get_srid());
3037 null_value= post_fix_result(&bg_resbuf_mgr, hull, res_hull);
3038 if (!null_value)
3039 bg_resbuf_mgr.set_result_buffer(const_cast<char *>(res_hull->ptr()));
3040 }
3041 catch (...)
3042 {
3043 null_value= true;
3044 handle_gis_exception("st_convexhull");
3045 }
3046
3047 return null_value;
3048 }
3049
3050
val_str(String * str)3051 String *Item_func_simplify::val_str(String *str)
3052 {
3053 assert(fixed == 1);
3054 String *swkb= args[0]->val_str(&arg_val);
3055 double max_dist= args[1]->val_real();
3056 Geometry_buffer buffer;
3057 Geometry *geom= NULL;
3058
3059 // Release last call's result buffer.
3060 bg_resbuf_mgr.free_result_buffer();
3061
3062 if ((null_value= (!swkb || args[0]->null_value || args[1]->null_value)))
3063 return error_str();
3064 if (!(geom= Geometry::construct(&buffer, swkb)))
3065 {
3066 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3067 return error_str();
3068 }
3069
3070 if (max_dist <= 0 || boost::math::isnan(max_dist))
3071 {
3072 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
3073 return error_str();
3074 }
3075
3076 Geometry::wkbType gtype= geom->get_type();
3077
3078 try
3079 {
3080 if (gtype == Geometry::wkb_geometrycollection)
3081 {
3082 BG_geometry_collection bggc;
3083 bggc.fill(geom);
3084 Gis_geometry_collection gc(geom->get_srid(),
3085 Geometry::wkb_invalid_type, NULL, str);
3086 for (BG_geometry_collection::Geometry_list::iterator
3087 i= bggc.get_geometries().begin();
3088 i != bggc.get_geometries().end(); ++i)
3089 {
3090 String gbuf;
3091 if ((null_value= simplify_basic<bgcs::cartesian>
3092 (*i, max_dist, &gbuf, &gc, str)))
3093 return error_str();
3094 }
3095 }
3096 else
3097 {
3098 if ((null_value= simplify_basic<bgcs::cartesian>(geom, max_dist, str)))
3099 return error_str();
3100 else
3101 bg_resbuf_mgr.set_result_buffer(const_cast<char *>(str->ptr()));
3102 }
3103 }
3104 catch (...)
3105 {
3106 null_value= true;
3107 handle_gis_exception("ST_Simplify");
3108 }
3109
3110 return str;
3111 }
3112
3113
3114 template <typename Coordsys>
3115 int Item_func_simplify::
simplify_basic(Geometry * geom,double max_dist,String * str,Gis_geometry_collection * gc,String * gcbuf)3116 simplify_basic(Geometry *geom, double max_dist, String *str,
3117 Gis_geometry_collection *gc, String *gcbuf)
3118 {
3119 assert((gc == NULL && gcbuf == NULL) || (gc != NULL && gcbuf != NULL));
3120 Geometry::wkbType geotype= geom->get_type();
3121
3122 switch (geotype)
3123 {
3124 case Geometry::wkb_point:
3125 {
3126 typename BG_models<Coordsys>::Point
3127 geo(geom->get_data_ptr(), geom->get_data_size(),
3128 geom->get_flags(), geom->get_srid()), out;
3129 boost::geometry::simplify(geo, out, max_dist);
3130 if ((null_value= post_fix_result(&bg_resbuf_mgr, out, str)))
3131 return null_value;
3132 if (gc && (null_value= gc->append_geometry(&out, gcbuf)))
3133 return null_value;
3134 }
3135 break;
3136 case Geometry::wkb_multipoint:
3137 {
3138 typename BG_models<Coordsys>::Multipoint
3139 geo(geom->get_data_ptr(), geom->get_data_size(),
3140 geom->get_flags(), geom->get_srid()), out;
3141 boost::geometry::simplify(geo, out, max_dist);
3142 if ((null_value= post_fix_result(&bg_resbuf_mgr, out, str)))
3143 return null_value;
3144 if (gc && (null_value= gc->append_geometry(&out, gcbuf)))
3145 return null_value;
3146 }
3147 break;
3148 case Geometry::wkb_linestring:
3149 {
3150 typename BG_models<Coordsys>::Linestring
3151 geo(geom->get_data_ptr(), geom->get_data_size(),
3152 geom->get_flags(), geom->get_srid()), out;
3153 boost::geometry::simplify(geo, out, max_dist);
3154 if ((null_value= post_fix_result(&bg_resbuf_mgr, out, str)))
3155 return null_value;
3156 if (gc && (null_value= gc->append_geometry(&out, gcbuf)))
3157 return null_value;
3158 }
3159 break;
3160 case Geometry::wkb_multilinestring:
3161 {
3162 typename BG_models<Coordsys>::Multilinestring
3163 geo(geom->get_data_ptr(), geom->get_data_size(),
3164 geom->get_flags(), geom->get_srid()), out;
3165 boost::geometry::simplify(geo, out, max_dist);
3166 if ((null_value= post_fix_result(&bg_resbuf_mgr, out, str)))
3167 return null_value;
3168 if (gc && (null_value= gc->append_geometry(&out, gcbuf)))
3169 return null_value;
3170 }
3171 break;
3172 case Geometry::wkb_polygon:
3173 {
3174 typename BG_models<Coordsys>::Polygon
3175 geo(geom->get_data_ptr(), geom->get_data_size(),
3176 geom->get_flags(), geom->get_srid()), out;
3177 boost::geometry::simplify(geo, out, max_dist);
3178 if ((null_value= post_fix_result(&bg_resbuf_mgr, out, str)))
3179 return null_value;
3180 if (gc && (null_value= gc->append_geometry(&out, gcbuf)))
3181 return null_value;
3182 }
3183 break;
3184 case Geometry::wkb_multipolygon:
3185 {
3186 typename BG_models<Coordsys>::Multipolygon
3187 geo(geom->get_data_ptr(), geom->get_data_size(),
3188 geom->get_flags(), geom->get_srid()), out;
3189 boost::geometry::simplify(geo, out, max_dist);
3190 if ((null_value= post_fix_result(&bg_resbuf_mgr, out, str)))
3191 return null_value;
3192 if (gc && (null_value= gc->append_geometry(&out, gcbuf)))
3193 return null_value;
3194 }
3195 break;
3196 case Geometry::wkb_geometrycollection:
3197 default:
3198 assert(false);
3199 break;
3200 }
3201
3202 return 0;
3203 }
3204
3205
3206 /*
3207 Spatial decomposition functions
3208 */
3209
val_str(String * str)3210 String *Item_func_spatial_decomp::val_str(String *str)
3211 {
3212 assert(fixed == 1);
3213 String arg_val;
3214 String *swkb= args[0]->val_str(&arg_val);
3215 Geometry_buffer buffer;
3216 Geometry *geom= NULL;
3217 uint32 srid;
3218
3219 if ((null_value= (!swkb || args[0]->null_value)))
3220 return NULL;
3221 if (!(geom= Geometry::construct(&buffer, swkb)))
3222 {
3223 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3224 return error_str();
3225 }
3226
3227 srid= uint4korr(swkb->ptr());
3228 str->set_charset(&my_charset_bin);
3229 if (str->reserve(SRID_SIZE, 512))
3230 goto err;
3231 str->length(0);
3232 str->q_append(srid);
3233 switch (decomp_func) {
3234 case SP_STARTPOINT:
3235 if (geom->start_point(str))
3236 goto err;
3237 break;
3238
3239 case SP_ENDPOINT:
3240 if (geom->end_point(str))
3241 goto err;
3242 break;
3243
3244 case SP_EXTERIORRING:
3245 if (geom->exterior_ring(str))
3246 goto err;
3247 break;
3248
3249 default:
3250 goto err;
3251 }
3252 return str;
3253
3254 err:
3255 null_value= 1;
3256 return 0;
3257 }
3258
3259
val_str(String * str)3260 String *Item_func_spatial_decomp_n::val_str(String *str)
3261 {
3262 assert(fixed == 1);
3263 String arg_val;
3264 String *swkb= args[0]->val_str(&arg_val);
3265 long n= (long) args[1]->val_int();
3266 Geometry_buffer buffer;
3267 Geometry *geom= NULL;
3268 uint32 srid;
3269
3270 if ((null_value= (!swkb || args[0]->null_value || args[1]->null_value)))
3271 return NULL;
3272 if (!(geom= Geometry::construct(&buffer, swkb)))
3273 {
3274 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3275 return error_str();
3276 }
3277
3278 str->set_charset(&my_charset_bin);
3279 if (str->reserve(SRID_SIZE, 512))
3280 goto err;
3281 srid= uint4korr(swkb->ptr());
3282 str->length(0);
3283 str->q_append(srid);
3284 switch (decomp_func_n)
3285 {
3286 case SP_POINTN:
3287 if (geom->point_n(n,str))
3288 goto err;
3289 break;
3290
3291 case SP_GEOMETRYN:
3292 if (geom->geometry_n(n,str))
3293 goto err;
3294 break;
3295
3296 case SP_INTERIORRINGN:
3297 if (geom->interior_ring_n(n,str))
3298 goto err;
3299 break;
3300
3301 default:
3302 goto err;
3303 }
3304 return str;
3305
3306 err:
3307 null_value=1;
3308 return 0;
3309 }
3310
3311
3312 /*
3313 Functions to concatenate various spatial objects
3314 */
3315
3316
3317 /*
3318 * Concatenate doubles into Point
3319 */
3320
3321
get_geometry_type() const3322 Field::geometry_type Item_func_point::get_geometry_type() const
3323 {
3324 return Field::GEOM_POINT;
3325 }
3326
3327
val_str(String * str)3328 String *Item_func_point::val_str(String *str)
3329 {
3330 assert(fixed == 1);
3331
3332 /*
3333 The coordinates of a point can't be another geometry, but other types
3334 are allowed as before.
3335 */
3336 if ((null_value= (args[0]->field_type() == MYSQL_TYPE_GEOMETRY ||
3337 args[1]->field_type() == MYSQL_TYPE_GEOMETRY)))
3338 {
3339 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
3340 return error_str();
3341 }
3342
3343 double x= args[0]->val_real();
3344 double y= args[1]->val_real();
3345 uint32 srid= 0;
3346
3347 if ((null_value= (args[0]->null_value ||
3348 args[1]->null_value ||
3349 str->mem_realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2))))
3350 return 0;
3351
3352 str->set_charset(&my_charset_bin);
3353 str->length(0);
3354 str->q_append(srid);
3355 str->q_append((char)Geometry::wkb_ndr);
3356 str->q_append((uint32)Geometry::wkb_point);
3357 str->q_append(x);
3358 str->q_append(y);
3359 return str;
3360 }
3361
3362
3363 /// This will check if arguments passed (geohash and SRID) are of valid types.
fix_fields(THD * thd,Item ** ref)3364 bool Item_func_pointfromgeohash::fix_fields(THD *thd, Item **ref)
3365 {
3366 if (Item_geometry_func::fix_fields(thd, ref))
3367 return true;
3368
3369 maybe_null= (args[0]->maybe_null || args[1]->maybe_null);
3370
3371 // Check for valid type in geohash argument.
3372 if (!Item_func_latlongfromgeohash::check_geohash_argument_valid_type(args[0]))
3373 {
3374 my_error(ER_INCORRECT_TYPE, MYF(0), "geohash", func_name());
3375 return true;
3376 }
3377
3378 /*
3379 Check for valid type in SRID argument.
3380
3381 We will allow all integer types, and strings since some connectors will
3382 covert integers to strings. Binary data is not allowed.
3383
3384 PARAM_ITEM and INT_ITEM checks are to allow prepared statements and usage of
3385 user-defined variables respectively.
3386 */
3387 if (Item_func_geohash::is_item_null(args[1]))
3388 return false;
3389
3390 if (args[1]->collation.collation == &my_charset_bin &&
3391 args[1]->type() != PARAM_ITEM && args[1]->type() != INT_ITEM)
3392 {
3393 my_error(ER_INCORRECT_TYPE, MYF(0), "SRID", func_name());
3394 return true;
3395 }
3396
3397 switch (args[1]->field_type())
3398 {
3399 case MYSQL_TYPE_STRING:
3400 case MYSQL_TYPE_VARCHAR:
3401 case MYSQL_TYPE_VAR_STRING:
3402 case MYSQL_TYPE_INT24:
3403 case MYSQL_TYPE_LONG:
3404 case MYSQL_TYPE_LONGLONG:
3405 case MYSQL_TYPE_SHORT:
3406 case MYSQL_TYPE_TINY:
3407 break;
3408 default:
3409 my_error(ER_INCORRECT_TYPE, MYF(0), "SRID", func_name());
3410 return true;
3411 }
3412 return false;
3413 }
3414
3415
val_str(String * str)3416 String *Item_func_pointfromgeohash::val_str(String *str)
3417 {
3418 assert(fixed == TRUE);
3419
3420 String argument_value;
3421 String *geohash= args[0]->val_str_ascii(&argument_value);
3422 longlong srid_input= args[1]->val_int();
3423
3424 // Return null if one or more of the input arguments is null.
3425 if ((null_value= (args[0]->null_value || args[1]->null_value)))
3426 return NULL;
3427
3428 // Only allow unsigned 32 bits integer as SRID.
3429 if (srid_input < 0 || srid_input > UINT_MAX32)
3430 {
3431 char srid_string[MAX_BIGINT_WIDTH + 1];
3432 llstr(srid_input, srid_string);
3433 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "SRID", srid_string, func_name());
3434 return error_str();
3435 }
3436
3437 if (str->mem_realloc(GEOM_HEADER_SIZE + POINT_DATA_SIZE))
3438 return make_empty_result();
3439
3440 if (geohash->length() == 0)
3441 {
3442 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "geohash", geohash->c_ptr_safe(),
3443 func_name());
3444 return error_str();
3445 }
3446
3447 double latitude= 0.0;
3448 double longitude= 0.0;
3449 uint32 srid= static_cast<uint32>(srid_input);
3450 if (Item_func_latlongfromgeohash::decode_geohash(geohash, upper_latitude,
3451 lower_latitude,
3452 upper_longitude,
3453 lower_longitude, &latitude,
3454 &longitude))
3455 {
3456 my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "geohash", geohash->c_ptr_safe(),
3457 func_name());
3458 return error_str();
3459 }
3460
3461 str->set_charset(&my_charset_bin);
3462 str->length(0);
3463 write_geometry_header(str, srid, Geometry::wkb_point);
3464 str->q_append(longitude);
3465 str->q_append(latitude);
3466 return str;
3467 }
3468
3469
func_name() const3470 const char *Item_func_spatial_collection::func_name() const
3471 {
3472 const char *str= NULL;
3473
3474 switch (coll_type)
3475 {
3476 case Geometry::wkb_multipoint:
3477 str= "multipoint";
3478 break;
3479 case Geometry::wkb_multilinestring:
3480 str= "multilinestring";
3481 break;
3482 case Geometry::wkb_multipolygon:
3483 str= "multipolygon";
3484 break;
3485 case Geometry::wkb_linestring:
3486 str= "linestring";
3487 break;
3488 case Geometry::wkb_polygon:
3489 str= "polygon";
3490 break;
3491 case Geometry::wkb_geometrycollection:
3492 str= "geometrycollection";
3493 break;
3494 default:
3495 assert(false);
3496 break;
3497 }
3498
3499 return str;
3500 }
3501
3502
3503 /**
3504 Concatenates various items into various collections
3505 with checkings for valid wkb type of items.
3506 For example, multipoint can be a collection of points only.
3507 coll_type contains wkb type of target collection.
3508 item_type contains a valid wkb type of items.
3509 In the case when coll_type is wkbGeometryCollection,
3510 we do not check wkb type of items, any is valid.
3511 */
3512
val_str(String * str)3513 String *Item_func_spatial_collection::val_str(String *str)
3514 {
3515 assert(fixed == 1);
3516 String arg_value;
3517 uint i;
3518 uint32 srid= 0;
3519
3520 str->set_charset(&my_charset_bin);
3521 str->length(0);
3522 if (str->reserve(4/*SRID*/ + 1 + 4 + 4, 512))
3523 goto err;
3524
3525 str->q_append(srid);
3526 str->q_append((char) Geometry::wkb_ndr);
3527 str->q_append((uint32) coll_type);
3528 str->q_append((uint32) arg_count);
3529
3530 // We can construct an empty geometry by calling GeometryCollection().
3531 if (arg_count == 0)
3532 return str;
3533
3534 for (i= 0; i < arg_count; ++i)
3535 {
3536 String *res= args[i]->val_str(&arg_value);
3537 size_t len;
3538
3539 if (args[i]->null_value || ((len= res->length()) < WKB_HEADER_SIZE))
3540 goto err;
3541
3542 if (coll_type == Geometry::wkb_geometrycollection)
3543 {
3544 /*
3545 In the case of GeometryCollection we don't need any checkings
3546 for item types, so just copy them into target collection
3547 */
3548 if (str->append(res->ptr() + 4/*SRID*/, len - 4/*SRID*/, (uint32) 512))
3549 goto err;
3550 }
3551 else
3552 {
3553 enum Geometry::wkbType wkb_type;
3554 const uint data_offset= 4/*SRID*/ + 1;
3555 if (res->length() < data_offset + sizeof(uint32))
3556 goto err;
3557 const char *data= res->ptr() + data_offset;
3558
3559 /*
3560 In the case of named collection we must check that items
3561 are of specific type, let's do this checking now
3562 */
3563
3564 wkb_type= get_wkb_geotype(data);
3565 data+= 4;
3566 len-= 5 + 4/*SRID*/;
3567 if (wkb_type != item_type)
3568 {
3569 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
3570 goto err;
3571 }
3572
3573 switch (coll_type) {
3574 case Geometry::wkb_multipoint:
3575 case Geometry::wkb_multilinestring:
3576 case Geometry::wkb_multipolygon:
3577 if (len < WKB_HEADER_SIZE ||
3578 str->append(data-WKB_HEADER_SIZE, len+WKB_HEADER_SIZE, 512))
3579 goto err;
3580 break;
3581
3582 case Geometry::wkb_linestring:
3583 if (len < POINT_DATA_SIZE || str->append(data, POINT_DATA_SIZE, 512))
3584 goto err;
3585 break;
3586 case Geometry::wkb_polygon:
3587 {
3588 uint32 n_points;
3589 double x1, y1, x2, y2;
3590 const char *org_data= data;
3591
3592 if (len < 4)
3593 goto err;
3594
3595 n_points= uint4korr(data);
3596 data+= 4;
3597
3598 // A ring must have at least 4 points.
3599 if (n_points < 4 || len != 4 + n_points * POINT_DATA_SIZE)
3600 {
3601 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3602 return error_str();
3603 }
3604
3605 float8get(&x1, data);
3606 data+= SIZEOF_STORED_DOUBLE;
3607 float8get(&y1, data);
3608 data+= SIZEOF_STORED_DOUBLE;
3609
3610 data+= (n_points - 2) * POINT_DATA_SIZE;
3611
3612 float8get(&x2, data);
3613 float8get(&y2, data + SIZEOF_STORED_DOUBLE);
3614
3615 // A ring must be closed.
3616 if ((x1 != x2) || (y1 != y2))
3617 {
3618 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3619 return error_str();
3620 }
3621
3622 if (str->append(org_data, len, 512))
3623 goto err;
3624 }
3625 break;
3626
3627 default:
3628 goto err;
3629 }
3630 }
3631 }
3632 if (str->length() > current_thd->variables.max_allowed_packet)
3633 {
3634 push_warning_printf(current_thd, Sql_condition::SL_WARNING,
3635 ER_WARN_ALLOWED_PACKET_OVERFLOWED,
3636 ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
3637 func_name(), current_thd->variables.max_allowed_packet);
3638 goto err;
3639 }
3640
3641 if (coll_type == Geometry::wkb_linestring && arg_count < 2)
3642 {
3643 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3644 return error_str();
3645 }
3646
3647 /*
3648 The construct() call parses the string to make sure it's a valid
3649 WKB byte string instead of some arbitrary trash bytes. Above code assumes
3650 so and doesn't further completely validate the string's content.
3651
3652 There are several goto statements above so we have to construct the
3653 geom_buff object in a scope, this is more of C++ style than defining it at
3654 start of this function.
3655 */
3656 {
3657 Geometry_buffer geom_buff;
3658 if (Geometry::construct(&geom_buff, str) == NULL)
3659 {
3660 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3661 return error_str();
3662 }
3663 }
3664
3665 null_value= 0;
3666 return str;
3667
3668 err:
3669 null_value= 1;
3670 return 0;
3671 }
3672
3673
3674 /**
3675 Convert this into a Gis_geometry_collection object.
3676 @param geodata Stores the result object's WKB data.
3677 @return The Gis_geometry_collection object created from this object.
3678 */
3679 Gis_geometry_collection *
as_geometry_collection(String * geodata) const3680 BG_geometry_collection::as_geometry_collection(String *geodata) const
3681 {
3682 if (m_geos.size() == 0)
3683 return empty_collection(geodata, m_srid);
3684
3685 Gis_geometry_collection *gc= NULL;
3686
3687 for (Geometry_list::const_iterator i= m_geos.begin();
3688 i != m_geos.end(); ++i)
3689 {
3690 if (gc == NULL)
3691 gc= new Gis_geometry_collection(*i, geodata);
3692 else
3693 gc->append_geometry(*i, geodata);
3694 }
3695
3696 return gc;
3697 }
3698
3699
3700 /**
3701 Store a Geometry object into this collection. If it's a geometry collection,
3702 flatten it and store its components into this collection, so that no
3703 component is a geometry collection.
3704 @param geo The Geometry object to put into this collection. We duplicate
3705 geo's data rather than directly using it.
3706 @param break_multi_geom whether break a multipoint or multilinestring or
3707 multipolygon so as to store its components separately into this object.
3708 @return true if error occured, false if no error(successful).
3709 */
store_geometry(const Geometry * geo,bool break_multi_geom)3710 bool BG_geometry_collection::store_geometry(const Geometry *geo,
3711 bool break_multi_geom)
3712 {
3713 Geometry::wkbType geo_type= geo->get_type();
3714
3715 if ((geo_type == Geometry::wkb_geometrycollection) ||
3716 (break_multi_geom && (geo_type == Geometry::wkb_multipoint ||
3717 geo_type == Geometry::wkb_multipolygon ||
3718 geo_type == Geometry::wkb_multilinestring)))
3719 {
3720 uint32 ngeom= 0;
3721
3722 if (geo->num_geometries(&ngeom))
3723 return true;
3724
3725 /*
3726 Get its components and store each of them separately, if a component
3727 is also a collection, recursively disintegrate and store its
3728 components in the same way.
3729 */
3730 for (uint32 i= 1; i <= ngeom; i++)
3731 {
3732 String *pres= m_geosdata.append_object();
3733 if (pres == NULL || pres->reserve(GEOM_HEADER_SIZE, 512))
3734 return true;
3735
3736 pres->q_append(geo->get_srid());
3737 if (geo->geometry_n(i, pres))
3738 return true;
3739
3740 Geometry_buffer *pgeobuf= m_geobufs.append_object();
3741 if (pgeobuf == NULL)
3742 return true;
3743 Geometry *geo2= Geometry::construct(pgeobuf, pres->ptr(),
3744 pres->length());
3745 if (geo2 == NULL)
3746 {
3747 // The geometry data already pass such checks, it's always valid here.
3748 assert(false);
3749 return true;
3750 }
3751 else if (geo2->get_type() == Geometry::wkb_geometrycollection)
3752 {
3753 if (store_geometry(geo2, break_multi_geom))
3754 return true;
3755 }
3756 else
3757 {
3758 geo2->has_geom_header_space(true);
3759 m_geos.push_back(geo2);
3760 }
3761 }
3762
3763 /*
3764 GCs with no-overlapping components can only be returned by
3765 combine_sub_results, which combines geometries from BG set operations,
3766 so no nested GCs or other user defined GCs are really set to true here.
3767 */
3768 set_comp_no_overlapped(geo->is_components_no_overlapped() || ngeom == 1);
3769 }
3770 else if (store(geo) == NULL)
3771 return true;
3772
3773 return false;
3774 }
3775
3776
3777 /**
3778 Store a geometry of GEOMETRY format into this collection.
3779 @param geo a geometry object whose data of GEOMETRY format is to be duplicated
3780 and stored into this collection. It's not a geometry collection.
3781 @return a duplicated Geometry object created from geo.
3782 */
store(const Geometry * geo)3783 Geometry *BG_geometry_collection::store(const Geometry *geo)
3784 {
3785 String *pres= NULL;
3786 Geometry *geo2= NULL;
3787 Geometry_buffer *pgeobuf= NULL;
3788 size_t geosize= geo->get_data_size();
3789
3790 assert(geo->get_type() != Geometry::wkb_geometrycollection);
3791 pres= m_geosdata.append_object();
3792 if (pres == NULL || pres->reserve(GEOM_HEADER_SIZE + geosize, 256))
3793 return NULL;
3794 write_geometry_header(pres, geo->get_srid(), geo->get_type());
3795 pres->q_append(geo->get_cptr(), geosize);
3796
3797 pgeobuf= m_geobufs.append_object();
3798 if (pgeobuf == NULL)
3799 return NULL;
3800 geo2= Geometry::construct(pgeobuf, pres->ptr(), pres->length());
3801 // The geometry data already pass such checks, it's always valid here.
3802 assert(geo2 != NULL);
3803
3804 if (geo2 != NULL && geo2->get_type() != Geometry::wkb_geometrycollection)
3805 m_geos.push_back(geo2);
3806
3807 return geo2;
3808 }
3809
3810
val_int()3811 longlong Item_func_isempty::val_int()
3812 {
3813 assert(fixed == 1);
3814 String tmp;
3815 String *swkb= args[0]->val_str(&tmp);
3816 Geometry_buffer buffer;
3817 Geometry *g= NULL;
3818
3819 if ((null_value= (!swkb || args[0]->null_value)))
3820 return 0;
3821 if (!(g= Geometry::construct(&buffer, swkb)))
3822 {
3823 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3824 return error_int();
3825 }
3826
3827 return (null_value || is_empty_geocollection(g)) ? 1 : 0;
3828 }
3829
3830
val_int()3831 longlong Item_func_issimple::val_int()
3832 {
3833 DBUG_ENTER("Item_func_issimple::val_int");
3834 assert(fixed == 1);
3835
3836 tmp.length(0);
3837 String *arg_wkb= args[0]->val_str(&tmp);
3838 if ((null_value= args[0]->null_value))
3839 {
3840 assert(maybe_null);
3841 DBUG_RETURN(0);
3842 }
3843 if (arg_wkb == NULL)
3844 {
3845 // Invalid geometry.
3846 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3847 DBUG_RETURN(error_int());
3848 }
3849
3850 Geometry_buffer buffer;
3851 Geometry *arg= Geometry::construct(&buffer, arg_wkb);
3852 if (arg == NULL)
3853 {
3854 // Invalid geometry.
3855 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3856 DBUG_RETURN(error_int());
3857 }
3858
3859 DBUG_RETURN(issimple(arg));
3860 }
3861
3862
3863 /**
3864 Evaluate if a geometry object is simple according to the OGC definition.
3865
3866 @param g The geometry to evaluate.
3867 @return True if the geometry is simple, false otherwise.
3868 */
issimple(Geometry * g)3869 bool Item_func_issimple::issimple(Geometry *g)
3870 {
3871 bool res= false;
3872
3873 try
3874 {
3875 switch (g->get_type())
3876 {
3877 case Geometry::wkb_point:
3878 {
3879 Gis_point arg(g->get_data_ptr(), g->get_data_size(),
3880 g->get_flags(), g->get_srid());
3881 res= boost::geometry::is_simple(arg);
3882 }
3883 break;
3884 case Geometry::wkb_linestring:
3885 {
3886 Gis_line_string arg(g->get_data_ptr(), g->get_data_size(),
3887 g->get_flags(), g->get_srid());
3888 res= boost::geometry::is_simple(arg);
3889 }
3890 break;
3891 case Geometry::wkb_polygon:
3892 {
3893 const void *arg_wkb= g->normalize_ring_order();
3894 if (arg_wkb == NULL)
3895 {
3896 // Invalid polygon.
3897 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3898 return error_bool();
3899 }
3900 Gis_polygon arg(arg_wkb, g->get_data_size(),
3901 g->get_flags(), g->get_srid());
3902 res= boost::geometry::is_simple(arg);
3903 }
3904 break;
3905 case Geometry::wkb_multipoint:
3906 {
3907 Gis_multi_point arg(g->get_data_ptr(), g->get_data_size(),
3908 g->get_flags(), g->get_srid());
3909 res= boost::geometry::is_simple(arg);
3910 }
3911 break;
3912 case Geometry::wkb_multilinestring:
3913 {
3914 Gis_multi_line_string arg(g->get_data_ptr(), g->get_data_size(),
3915 g->get_flags(), g->get_srid());
3916 res= boost::geometry::is_simple(arg);
3917 }
3918 break;
3919 case Geometry::wkb_multipolygon:
3920 {
3921 const void *arg_wkb= g->normalize_ring_order();
3922 if (arg_wkb == NULL)
3923 {
3924 // Invalid multipolygon.
3925 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3926 return error_bool();
3927 }
3928 Gis_multi_polygon arg(arg_wkb, g->get_data_size(),
3929 g->get_flags(), g->get_srid());
3930 res= boost::geometry::is_simple(arg);
3931 }
3932 break;
3933 case Geometry::wkb_geometrycollection:
3934 {
3935 BG_geometry_collection collection;
3936 collection.fill(g);
3937
3938 res= true;
3939 for (BG_geometry_collection::Geometry_list::iterator i=
3940 collection.get_geometries().begin();
3941 i != collection.get_geometries().end();
3942 ++i)
3943 {
3944 res= issimple(*i);
3945 if (current_thd->is_error())
3946 {
3947 res= error_bool();
3948 break;
3949 }
3950 if (!res)
3951 {
3952 break;
3953 }
3954 }
3955 }
3956 break;
3957 default:
3958 assert(0);
3959 break;
3960 }
3961 }
3962 catch (...)
3963 {
3964 res= error_bool();
3965 handle_gis_exception(func_name());
3966 }
3967
3968 return res;
3969 }
3970
3971
val_int()3972 longlong Item_func_isclosed::val_int()
3973 {
3974 assert(fixed == 1);
3975 String tmp;
3976 String *swkb= args[0]->val_str(&tmp);
3977 Geometry_buffer buffer;
3978 Geometry *geom;
3979 int isclosed= 0; // In case of error
3980
3981 if ((null_value= (!swkb || args[0]->null_value)))
3982 return 0L;
3983
3984 if (!(geom= Geometry::construct(&buffer, swkb)))
3985 {
3986 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3987 return error_int();
3988 }
3989
3990 null_value= geom->is_closed(&isclosed);
3991
3992 return (longlong) isclosed;
3993 }
3994
3995
3996 /**
3997 Checks the validity of a geometry collection, which is valid
3998 if and only if all its components are valid.
3999 */
4000 class Geomcoll_validity_checker: public WKB_scanner_event_handler
4001 {
4002 bool m_isvalid;
4003 Geometry::srid_t m_srid;
4004 std::stack<Geometry::wkbType> types;
4005 public:
Geomcoll_validity_checker(Geometry::srid_t srid)4006 explicit Geomcoll_validity_checker(Geometry::srid_t srid)
4007 :m_isvalid(true), m_srid(srid)
4008 {
4009 }
4010
is_valid() const4011 bool is_valid() const { return m_isvalid; }
4012
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void * wkb,uint32 len,bool has_hdr)4013 virtual void on_wkb_start(Geometry::wkbByteOrder bo,
4014 Geometry::wkbType geotype,
4015 const void *wkb, uint32 len, bool has_hdr)
4016 {
4017 if (!m_isvalid)
4018 return;
4019 Geometry::wkbType top= Geometry::wkb_invalid_type;
4020 if (types.size() > 0)
4021 top= types.top();
4022 else
4023 assert(geotype == Geometry::wkb_geometrycollection);
4024
4025 types.push(geotype);
4026 // A geometry collection's vaidity is determined by that of its components.
4027 if (geotype == Geometry::wkb_geometrycollection)
4028 return;
4029 // If not owned by a GC(i.e. not a direct component of a GC), it doesn't
4030 // influence the GC's validity.
4031 if (top != Geometry::wkb_geometrycollection)
4032 return;
4033
4034 assert(top != Geometry::wkb_invalid_type && has_hdr);
4035 assert(len > WKB_HEADER_SIZE);
4036
4037 Geometry_buffer geobuf;
4038 Geometry *geo= NULL;
4039
4040 // Provide the WKB header starting address, wkb MUST have a WKB header
4041 // right before it.
4042 geo= Geometry::construct(&geobuf,
4043 static_cast<const char *>(wkb) - WKB_HEADER_SIZE,
4044 len + WKB_HEADER_SIZE, false/* has no srid */);
4045 if (geo == NULL)
4046 m_isvalid= false;
4047 else
4048 {
4049 geo->set_srid(m_srid);
4050 m_isvalid= check_geometry_valid(geo);
4051 }
4052 }
4053
4054
on_wkb_end(const void * wkb)4055 virtual void on_wkb_end(const void *wkb)
4056 {
4057 if (types.size() > 0)
4058 types.pop();
4059 }
4060 };
4061
4062
4063 /*
4064 Call Boost Geometry algorithm to check whether a geometry is valid as
4065 defined by OGC.
4066 */
check_geometry_valid(Geometry * geom)4067 static int check_geometry_valid(Geometry *geom)
4068 {
4069 int ret= 0;
4070
4071 // Empty geometry collection is always valid. This is shortcut for
4072 // flat empty GCs.
4073 if (is_empty_geocollection(geom))
4074 return 1;
4075
4076 switch (geom->get_type())
4077 {
4078 case Geometry::wkb_point:
4079 {
4080 BG_models<bgcs::cartesian>::Point
4081 bg(geom->get_data_ptr(), geom->get_data_size(),
4082 geom->get_flags(), geom->get_srid());
4083 ret= bg::is_valid(bg);
4084 break;
4085 }
4086 case Geometry::wkb_linestring:
4087 {
4088 BG_models<bgcs::cartesian>::Linestring
4089 bg(geom->get_data_ptr(), geom->get_data_size(),
4090 geom->get_flags(), geom->get_srid());
4091 ret= bg::is_valid(bg);
4092 break;
4093 }
4094 case Geometry::wkb_polygon:
4095 {
4096 const void *ptr= geom->normalize_ring_order();
4097 if (ptr == NULL)
4098 {
4099 ret= 0;
4100 break;
4101 }
4102
4103 BG_models<bgcs::cartesian>::Polygon
4104 bg(ptr, geom->get_data_size(),
4105 geom->get_flags(), geom->get_srid());
4106 ret= bg::is_valid(bg);
4107 break;
4108 }
4109 case Geometry::wkb_multipoint:
4110 {
4111 BG_models<bgcs::cartesian>::Multipoint
4112 bg(geom->get_data_ptr(), geom->get_data_size(),
4113 geom->get_flags(), geom->get_srid());
4114 ret= bg::is_valid(bg);
4115 break;
4116 }
4117 case Geometry::wkb_multilinestring:
4118 {
4119 BG_models<bgcs::cartesian>::Multilinestring
4120 bg(geom->get_data_ptr(), geom->get_data_size(),
4121 geom->get_flags(), geom->get_srid());
4122 ret= bg::is_valid(bg);
4123 break;
4124 }
4125 case Geometry::wkb_multipolygon:
4126 {
4127 const void *ptr= geom->normalize_ring_order();
4128 if (ptr == NULL)
4129 {
4130 ret= 0;
4131 break;
4132 }
4133
4134 BG_models<bgcs::cartesian>::Multipolygon
4135 bg(ptr, geom->get_data_size(),
4136 geom->get_flags(), geom->get_srid());
4137 ret= bg::is_valid(bg);
4138 break;
4139 }
4140 case Geometry::wkb_geometrycollection:
4141 {
4142 uint32 wkb_len= geom->get_data_size();
4143 Geomcoll_validity_checker validity_checker(geom->get_srid());
4144
4145 /*
4146 This case can only be reached when this function isn't recursively
4147 called (indirectly by itself) but called by other functions, and
4148 it's the WKB data doesn't have a WKB header before it. Otherwise in
4149 Geomcoll_validity_checker it's required that the WKB data has a header.
4150 */
4151 wkb_scanner(geom->get_cptr(), &wkb_len,
4152 Geometry::wkb_geometrycollection, false,
4153 &validity_checker);
4154 ret= validity_checker.is_valid();
4155 break;
4156 }
4157 default:
4158 assert(false);
4159 break;
4160 }
4161
4162 return ret;
4163 }
4164
4165
val_int()4166 longlong Item_func_isvalid::val_int()
4167 {
4168 assert(fixed == 1);
4169 String tmp;
4170 String *swkb= args[0]->val_str(&tmp);
4171 Geometry_buffer buffer;
4172 Geometry *geom;
4173
4174 if ((null_value= (!swkb || args[0]->null_value)))
4175 return 0L;
4176
4177 // It should return false if the argument isn't a valid GEOMETRY string.
4178 if (!(geom= Geometry::construct(&buffer, swkb)))
4179 return 0L;
4180 if (geom->get_srid() != 0)
4181 {
4182 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
4183 return error_int();
4184 }
4185
4186 int ret= 0;
4187 try
4188 {
4189 ret= check_geometry_valid(geom);
4190 }
4191 catch (...)
4192 {
4193 null_value= true;
4194 handle_gis_exception("ST_IsValid");
4195 }
4196
4197 return ret;
4198 }
4199
4200
4201 /*
4202 Numerical functions
4203 */
4204
4205
val_int()4206 longlong Item_func_dimension::val_int()
4207 {
4208 assert(fixed == 1);
4209 uint32 dim= 0; // In case of error
4210 String *swkb= args[0]->val_str(&value);
4211 Geometry_buffer buffer;
4212 Geometry *geom;
4213
4214 if ((null_value= (!swkb || args[0]->null_value)))
4215 return 0;
4216 if (!(geom= Geometry::construct(&buffer, swkb)))
4217 {
4218 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4219 return error_int();
4220 }
4221 null_value= geom->dimension(&dim);
4222 return (longlong) dim;
4223 }
4224
4225
val_int()4226 longlong Item_func_numinteriorring::val_int()
4227 {
4228 assert(fixed == 1);
4229 uint32 num= 0; // In case of error
4230 String *swkb= args[0]->val_str(&value);
4231 Geometry_buffer buffer;
4232 Geometry *geom;
4233
4234 if ((null_value= (!swkb || args[0]->null_value)))
4235 return 0L;
4236 if (!(geom= Geometry::construct(&buffer, swkb)))
4237 {
4238 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4239 return error_int();
4240 }
4241 null_value= geom->num_interior_ring(&num);
4242 return (longlong) num;
4243 }
4244
4245
val_int()4246 longlong Item_func_numgeometries::val_int()
4247 {
4248 assert(fixed == 1);
4249 uint32 num= 0; // In case of errors
4250 String *swkb= args[0]->val_str(&value);
4251 Geometry_buffer buffer;
4252 Geometry *geom;
4253
4254 if ((null_value= (!swkb || args[0]->null_value)))
4255 return 0L;
4256 if (!(geom= Geometry::construct(&buffer, swkb)))
4257 {
4258 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4259 return error_int();
4260 }
4261 null_value= geom->num_geometries(&num);
4262 return (longlong) num;
4263 }
4264
4265
val_int()4266 longlong Item_func_numpoints::val_int()
4267 {
4268 assert(fixed == 1);
4269 uint32 num= 0; // In case of errors
4270 String *swkb= args[0]->val_str(&value);
4271 Geometry_buffer buffer;
4272 Geometry *geom;
4273
4274 if ((null_value= (!swkb || args[0]->null_value)))
4275 return 0L;
4276 if (!(geom= Geometry::construct(&buffer, swkb)))
4277 {
4278 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4279 return error_int();
4280 }
4281 null_value= geom->num_points(&num);
4282 return (longlong) num;
4283 }
4284
4285
val_real()4286 double Item_func_x::val_real()
4287 {
4288 assert(fixed == 1);
4289 double res= 0.0; // In case of errors
4290 String *swkb= args[0]->val_str(&value);
4291 Geometry_buffer buffer;
4292 Geometry *geom;
4293
4294 if ((null_value= (!swkb || args[0]->null_value)))
4295 return res;
4296 if (!(geom= Geometry::construct(&buffer, swkb)))
4297 {
4298 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4299 return error_real();
4300 }
4301 null_value= geom->get_x(&res);
4302 return res;
4303 }
4304
4305
val_real()4306 double Item_func_y::val_real()
4307 {
4308 assert(fixed == 1);
4309 double res= 0; // In case of errors
4310 String *swkb= args[0]->val_str(&value);
4311 Geometry_buffer buffer;
4312 Geometry *geom;
4313
4314 if ((null_value= (!swkb || args[0]->null_value)))
4315 return res;
4316 if (!(geom= Geometry::construct(&buffer, swkb)))
4317 {
4318 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4319 return error_real();
4320 }
4321 null_value= geom->get_y(&res);
4322 return res;
4323 }
4324
4325
4326 template <typename Coordsys>
bg_area(const Geometry * geom)4327 double Item_func_area::bg_area(const Geometry *geom)
4328 {
4329 double res= 0;
4330
4331 try
4332 {
4333 switch (geom->get_type())
4334 {
4335 case Geometry::wkb_point:
4336 case Geometry::wkb_multipoint:
4337 case Geometry::wkb_linestring:
4338 case Geometry::wkb_multilinestring:
4339 res= 0;
4340 break;
4341 case Geometry::wkb_polygon:
4342 {
4343 typename BG_models<Coordsys>::Polygon
4344 plgn(geom->get_data_ptr(), geom->get_data_size(),
4345 geom->get_flags(), geom->get_srid());
4346
4347 res= boost::geometry::area(plgn);
4348 }
4349 break;
4350 case Geometry::wkb_multipolygon:
4351 {
4352 typename BG_models<Coordsys>::Multipolygon
4353 mplgn(geom->get_data_ptr(), geom->get_data_size(),
4354 geom->get_flags(), geom->get_srid());
4355
4356 res= boost::geometry::area(mplgn);
4357 }
4358 break;
4359 case Geometry::wkb_geometrycollection:
4360 {
4361 BG_geometry_collection bggc;
4362
4363 bggc.fill(geom);
4364
4365 for (BG_geometry_collection::Geometry_list::iterator
4366 i= bggc.get_geometries().begin();
4367 i != bggc.get_geometries().end(); ++i)
4368 {
4369 if ((*i)->get_geotype() != Geometry::wkb_geometrycollection &&
4370 (*i)->normalize_ring_order() == NULL)
4371 {
4372 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4373 null_value= true;
4374 return 0;
4375 }
4376
4377 res+= bg_area<Coordsys>(*i);
4378 if (null_value)
4379 return res;
4380 }
4381
4382 }
4383 break;
4384 default:
4385 assert(false);
4386 break;
4387 }
4388 }
4389 catch (...)
4390 {
4391 null_value= true;
4392 handle_gis_exception("st_area");
4393 }
4394
4395 /*
4396 Given a polygon whose rings' points are in counter-clockwise order,
4397 boost geometry computes an area of negative value. Also, the inner ring
4398 has to be clockwise.
4399
4400 We now always make polygon rings CCW --- outer ring CCW and inner rings CW,
4401 thus if we get a negative value, it's because the inner ring is larger than
4402 the outer ring, and we should keep it negative.
4403 */
4404
4405 return res;
4406 }
4407
4408
val_real()4409 double Item_func_area::val_real()
4410 {
4411 assert(fixed == 1);
4412 double res= 0; // In case of errors
4413 String *swkb= args[0]->val_str(&value);
4414 Geometry_buffer buffer;
4415 Geometry *geom;
4416
4417 if ((null_value= (!swkb || args[0]->null_value)))
4418 return res;
4419 if (!(geom= Geometry::construct(&buffer, swkb)))
4420 {
4421 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4422 return error_real();
4423 }
4424 assert(geom->get_coordsys() == Geometry::cartesian);
4425
4426 if (geom->get_geotype() != Geometry::wkb_geometrycollection &&
4427 geom->normalize_ring_order() == NULL)
4428 {
4429 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4430 return error_real();
4431 }
4432
4433 res= bg_area<bgcs::cartesian>(geom);
4434
4435 // Had error in bg_area.
4436 if (null_value)
4437 return error_real();
4438
4439 if (!my_isfinite(res))
4440 {
4441 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4442 return error_real();
4443 }
4444 return res;
4445 }
4446
val_real()4447 double Item_func_glength::val_real()
4448 {
4449 assert(fixed == 1);
4450 double res= 0; // In case of errors
4451 String *swkb= args[0]->val_str(&value);
4452 Geometry_buffer buffer;
4453 Geometry *geom;
4454
4455 if ((null_value= (!swkb || args[0]->null_value)))
4456 return res;
4457 if (!(geom= Geometry::construct(&buffer, swkb)))
4458 {
4459 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4460 return error_real();
4461 }
4462 if ((null_value= geom->geom_length(&res)))
4463 return res;
4464 if (!my_isfinite(res))
4465 {
4466 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4467 return error_real();
4468 }
4469 return res;
4470 }
4471
val_int()4472 longlong Item_func_srid::val_int()
4473 {
4474 assert(fixed == 1);
4475 String *swkb= args[0]->val_str(&value);
4476 Geometry_buffer buffer;
4477 longlong res= 0L;
4478
4479 if ((null_value= (!swkb || args[0]->null_value)))
4480 return res;
4481 if (!Geometry::construct(&buffer, swkb))
4482 {
4483 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4484 return error_int();
4485 }
4486
4487 return (longlong) (uint4korr(swkb->ptr()));
4488 }
4489
4490
4491 /**
4492 Compact a geometry collection, making all its points/multipoints into a
4493 single multipoint object, and all its linestrings/multilinestrings into a
4494 single multilinestring object; leave polygons and multipolygons as they were.
4495
4496 @param g the input geometry collection.
4497 @param gbuf the place to create the new result geometry collection.
4498 @param str the String buffer to hold data of the result geometry collection.
4499 */
4500 static const Geometry *
compact_collection(const Geometry * g,Geometry_buffer * gbuf,String * str)4501 compact_collection(const Geometry *g, Geometry_buffer *gbuf, String *str)
4502 {
4503 if (g->get_geotype() != Geometry::wkb_geometrycollection)
4504 return g;
4505
4506 uint32 wkb_len, wkb_len0;
4507 char *wkb_start= g->get_cptr();
4508
4509 wkb_len= wkb_len0= g->get_data_size();
4510 BG_models<bgcs::cartesian>::Multilinestring mls;
4511 Geometry_grouper<BG_models<bgcs::cartesian>::Linestring>
4512 ls_grouper(&mls);
4513 wkb_scanner(wkb_start, &wkb_len,
4514 Geometry::wkb_geometrycollection, false, &ls_grouper);
4515
4516 BG_models<bgcs::cartesian>::Multipoint mpts;
4517 wkb_len= wkb_len0;
4518 Geometry_grouper<BG_models<bgcs::cartesian>::Point>
4519 pt_grouper(&mpts);
4520 wkb_scanner(wkb_start, &wkb_len,
4521 Geometry::wkb_geometrycollection, false, &pt_grouper);
4522
4523 Gis_geometry_collection *ret= new (gbuf) Gis_geometry_collection();
4524 wkb_len= wkb_len0;
4525 Geometry_grouper<BG_models<bgcs::cartesian>::Polygon>
4526 mplgn_grouper(ret, str);
4527 wkb_scanner(wkb_start, &wkb_len,
4528 Geometry::wkb_geometrycollection, false, &mplgn_grouper);
4529
4530 ret->append_geometry(&mls, str);
4531 ret->append_geometry(&mpts, str);
4532
4533 return ret;
4534 }
4535
4536
4537 template<typename Num_type>
4538 class Numeric_interval
4539 {
4540 Num_type left, right;
4541 bool is_left_open, is_right_open;
4542 bool is_left_unlimitted, is_right_unlimitted;
4543 public:
4544 // Construct a boundless interval.
Numeric_interval()4545 Numeric_interval() :left(0), right(0),
4546 is_left_open(false), is_right_open(false),
4547 is_left_unlimitted(true), is_right_unlimitted(true)
4548 {
4549 }
4550
4551 // Construct a both bounded internal.
Numeric_interval(const Num_type & l,bool left_open,const Num_type & r,bool right_open)4552 Numeric_interval(const Num_type &l, bool left_open,
4553 const Num_type &r, bool right_open) :left(l), right(r),
4554 is_left_open(left_open), is_right_open(right_open),
4555 is_left_unlimitted(false), is_right_unlimitted(false)
4556 {
4557 }
4558
4559 // Construct a half boundless half bounded interval.
4560 // @param is_left true to specify the left boundary and right is boundless;
4561 // false to specify the right boundary and left is boundless.
Numeric_interval(bool is_left,const Num_type & v,bool is_open)4562 Numeric_interval(bool is_left, const Num_type &v, bool is_open)
4563 :left(0), right(0),
4564 is_left_open(false), is_right_open(false),
4565 is_left_unlimitted(true), is_right_unlimitted(true)
4566 {
4567 if (is_left)
4568 {
4569 left= v;
4570 is_left_open= is_open;
4571 is_left_unlimitted= false;
4572 }
4573 else
4574 {
4575 right= v;
4576 is_right_open= is_open;
4577 is_right_unlimitted= false;
4578 }
4579 }
4580
4581 /*
4582 Get left boundary specification.
4583 @param [out] l takes back left boundary value. if boundless it's intact.
4584 @param [out] is_open takes back left boundary openness. if boundless
4585 it's intact.
4586 @return true if it's boundless, false if bounded and the two out parameters
4587 take back the boundary info.
4588 */
get_left(Num_type & l,bool & is_open) const4589 bool get_left(Num_type &l, bool &is_open) const
4590 {
4591 if (is_left_unlimitted)
4592 return true;
4593 l= left;
4594 is_open= is_left_open;
4595 return false;
4596 }
4597
4598
4599 /*
4600 Get right boundary specification.
4601 @param [out] r takes back right boundary value. if boundless it's intact.
4602 @param [out] is_open takes back right boundary openness. if boundless
4603 it's intact.
4604 @return true if it's boundless, false if bounded and the two out parameters
4605 take back the boundary info.
4606 */
get_right(Num_type & r,bool & is_open) const4607 bool get_right(Num_type &r, bool &is_open) const
4608 {
4609 if (is_right_unlimitted)
4610 return true;
4611 r= right;
4612 is_open= is_right_open;
4613 return false;
4614 }
4615 };
4616
4617
4618 class Point_coordinate_checker : public WKB_scanner_event_handler
4619 {
4620 bool has_invalid;
4621
4622 // Valid x and y coordinate range.
4623 Numeric_interval<double> x_val, y_val;
4624 public:
Point_coordinate_checker(Numeric_interval<double> x_range,Numeric_interval<double> y_range)4625 Point_coordinate_checker(Numeric_interval<double> x_range,
4626 Numeric_interval<double> y_range)
4627 :has_invalid(false), x_val(x_range), y_val(y_range)
4628 {}
4629
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void * wkb,uint32 len,bool has_hdr)4630 virtual void on_wkb_start(Geometry::wkbByteOrder bo,
4631 Geometry::wkbType geotype,
4632 const void *wkb, uint32 len, bool has_hdr)
4633 {
4634 if (geotype == Geometry::wkb_point)
4635 {
4636 Gis_point pt(wkb, POINT_DATA_SIZE,
4637 Geometry::Flags_t(Geometry::wkb_point, len), 0);
4638 double x= pt.get<0>(), y= pt.get<1>();
4639
4640 double xmin, xmax, ymin, ymax;
4641 bool xmin_open, ymin_open, xmax_open, ymax_open;
4642
4643 if ((!x_val.get_left(xmin, xmin_open) &&
4644 (x < xmin || (xmin_open && x == xmin))) ||
4645 (!x_val.get_right(xmax, xmax_open) &&
4646 (x > xmax || (xmax_open && x == xmax))) ||
4647 (!y_val.get_left(ymin, ymin_open) &&
4648 (y < ymin || (ymin_open && y == ymin))) ||
4649 (!y_val.get_right(ymax, ymax_open) &&
4650 (y > ymax || (ymax_open && y == ymax))))
4651 {
4652 has_invalid= true;
4653 return;
4654 }
4655 }
4656 }
4657
4658
on_wkb_end(const void * wkb)4659 virtual void on_wkb_end(const void *wkb)
4660 {
4661 }
4662
has_invalid_point() const4663 bool has_invalid_point() const { return has_invalid; }
4664 };
4665
4666
4667 template <typename CoordinateSystem>
4668 struct BG_distance
4669 {};
4670
4671
4672 template <>
4673 struct BG_distance<bg::cs::cartesian>
4674 {
applyBG_distance4675 static double apply(Item_func_distance *item, Geometry *g1, Geometry *g2)
4676 {
4677 // Do the actual computation here for the cartesian CS.
4678 return item->bg_distance<bgcs::cartesian>(g1, g2);
4679 }
4680 };
4681
4682
4683 template <>
4684 struct BG_distance<bg::cs::spherical_equatorial<bg::degree> >
4685 {
applyBG_distance4686 static double apply(Item_func_distance *item, Geometry *g1, Geometry *g2)
4687 {
4688 // Do the actual computation here for the spherical equatorial CS
4689 // with degree units.
4690 return item->bg_distance_spherical(g1, g2);
4691 }
4692 };
4693
4694
val_real()4695 double Item_func_distance::val_real()
4696 {
4697 typedef bgcs::spherical_equatorial<bg::degree> bgcssed;
4698 double distance= 0;
4699
4700 DBUG_ENTER("Item_func_distance::val_real");
4701 assert(fixed == 1);
4702
4703 String *res1= args[0]->val_str(&tmp_value1);
4704 String *res2= args[1]->val_str(&tmp_value2);
4705 Geometry_buffer buffer1, buffer2;
4706 Geometry *g1, *g2;
4707
4708 if ((null_value= (!res1 || args[0]->null_value ||
4709 !res2 || args[1]->null_value)))
4710 DBUG_RETURN(0.0);
4711
4712 if (!(g1= Geometry::construct(&buffer1, res1)) ||
4713 !(g2= Geometry::construct(&buffer2, res2)))
4714 {
4715 // If construction fails, we assume invalid input data.
4716 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4717 DBUG_RETURN(error_real());
4718 }
4719
4720 // The two geometry operand must be in the same coordinate system.
4721 if (g1->get_srid() != g2->get_srid())
4722 {
4723 my_error(ER_GIS_DIFFERENT_SRIDS, MYF(0), func_name(),
4724 g1->get_srid(), g2->get_srid());
4725 DBUG_RETURN(error_real());
4726 }
4727
4728 if ((g1->get_geotype() != Geometry::wkb_geometrycollection &&
4729 g1->normalize_ring_order() == NULL) ||
4730 (g2->get_geotype() != Geometry::wkb_geometrycollection &&
4731 g2->normalize_ring_order() == NULL))
4732 {
4733 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4734 DBUG_RETURN(error_real());
4735 }
4736
4737 if (is_spherical_equatorial)
4738 {
4739 Geometry::wkbType gt1= g1->get_geotype();
4740 Geometry::wkbType gt2= g2->get_geotype();
4741 if (!((gt1 == Geometry::wkb_point || gt1 == Geometry::wkb_multipoint) &&
4742 (gt2 == Geometry::wkb_point || gt2 == Geometry::wkb_multipoint)))
4743 {
4744 my_error(ER_GIS_UNSUPPORTED_ARGUMENT, MYF(0), func_name());
4745 DBUG_RETURN(error_real());
4746 }
4747
4748 if (arg_count == 3)
4749 {
4750 earth_radius= args[2]->val_real();
4751 if (args[2]->null_value)
4752 DBUG_RETURN(error_real());
4753 if (earth_radius <= 0)
4754 {
4755 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
4756 DBUG_RETURN(error_real());
4757 }
4758 }
4759
4760 /*
4761 Make sure all points' coordinates are valid:
4762 x in (-180, 180], y in [-90, 90].
4763 */
4764 Numeric_interval<double> x_range(-180, true, 180, false); // (-180, 180]
4765 Numeric_interval<double> y_range(-90, false, 90, false); // [-90, 90]
4766 Point_coordinate_checker checker(x_range, y_range);
4767
4768 uint32 wkblen= res1->length() - 4;
4769 wkb_scanner(res1->ptr() + 4, &wkblen, Geometry::wkb_invalid_type,
4770 true, &checker);
4771 if (checker.has_invalid_point())
4772 {
4773 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
4774 DBUG_RETURN(error_real());
4775 }
4776
4777 wkblen= res2->length() - 4;
4778 wkb_scanner(res2->ptr() + 4, &wkblen, Geometry::wkb_invalid_type,
4779 true, &checker);
4780 if (checker.has_invalid_point())
4781 {
4782 my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
4783 DBUG_RETURN(error_real());
4784 }
4785 }
4786
4787 if (g1->get_type() != Geometry::wkb_geometrycollection &&
4788 g2->get_type() != Geometry::wkb_geometrycollection)
4789 {
4790 if (is_spherical_equatorial)
4791 distance= BG_distance<bgcssed>::apply(this, g1, g2);
4792 else
4793 distance= BG_distance<bgcs::cartesian>::apply(this, g1, g2);
4794 }
4795 else
4796 distance= geometry_collection_distance(g1, g2);
4797
4798 if (null_value)
4799 DBUG_RETURN(error_real());
4800
4801 if (!my_isfinite(distance) || distance < 0)
4802 {
4803 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4804 DBUG_RETURN(error_real());
4805 }
4806 DBUG_RETURN(distance);
4807 }
4808
4809
4810 /*
4811 Calculate the distance of two geometry collections. BG has optimized
4812 algorithm to calculate distance among multipoints, multilinestrings
4813 and polygons, so we compact the collection to make a single multipoint,
4814 a single multilinestring, and the rest are all polygons and multipolygons,
4815 and do a nested loop to calculate the minimum distances among such
4816 compacted components as the final result.
4817 */
4818 double Item_func_distance::
geometry_collection_distance(const Geometry * g1,const Geometry * g2)4819 geometry_collection_distance(const Geometry *g1, const Geometry *g2)
4820 {
4821 BG_geometry_collection bggc1, bggc2;
4822 bool initialized= false, all_normalized= false;
4823 double min_distance= DBL_MAX, dist= DBL_MAX;
4824 String gcstr1, gcstr2;
4825 Geometry_buffer buf1, buf2;
4826 const Geometry *g11, *g22;
4827
4828 g11= compact_collection(g1, &buf1, &gcstr1);
4829 g22= compact_collection(g2, &buf2, &gcstr2);
4830
4831 bggc1.fill(g11);
4832 bggc2.fill(g22);
4833 for (BG_geometry_collection::Geometry_list::iterator
4834 i= bggc1.get_geometries().begin();
4835 i != bggc1.get_geometries().end(); ++i)
4836 {
4837 /* Normalize polygon rings, do only once for each component. */
4838 if ((*i)->get_geotype() != Geometry::wkb_geometrycollection &&
4839 (*i)->normalize_ring_order() == NULL)
4840 {
4841 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4842 return error_real();
4843 }
4844
4845 for (BG_geometry_collection::Geometry_list::iterator
4846 j= bggc2.get_geometries().begin();
4847 j != bggc2.get_geometries().end(); ++j)
4848 {
4849 /* Normalize polygon rings, do only once for each component. */
4850 if (!all_normalized &&
4851 (*j)->get_geotype() != Geometry::wkb_geometrycollection &&
4852 (*j)->normalize_ring_order() == NULL)
4853 {
4854 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
4855 return error_real();
4856 }
4857
4858 if (is_spherical_equatorial)
4859 {
4860 // For now this is never reached because we only support
4861 // distance([multi]point, [multi]point) for spherical.
4862 assert(false);
4863 }
4864 else
4865 dist= BG_distance<bgcs::cartesian>::apply(this, *i, *j);
4866 if (null_value)
4867 return error_real();
4868 if (dist < 0 || boost::math::isnan(dist))
4869 return dist;
4870
4871 if (!initialized)
4872 {
4873 min_distance= dist;
4874 initialized= true;
4875 }
4876 else if (min_distance > dist)
4877 min_distance= dist;
4878
4879 }
4880
4881 all_normalized= true;
4882 if (!initialized)
4883 break; // bggc2 is empty.
4884 }
4885
4886 /*
4887 If at least one of the collections is empty, we have NULL result.
4888 */
4889 if (!initialized)
4890 return error_real();
4891
4892 return min_distance;
4893 }
4894
4895
4896 double Item_func_distance::
distance_point_geometry_spherical(const Geometry * g1,const Geometry * g2)4897 distance_point_geometry_spherical(const Geometry *g1, const Geometry *g2)
4898 {
4899 typedef bgcs::spherical_equatorial<bg::degree> bgcssed;
4900 double res= 0;
4901 bg::strategy::distance::haversine<double, double>
4902 dist_strategy(earth_radius);
4903
4904 BG_models<bgcssed>::Point
4905 bg1(g1->get_data_ptr(), g1->get_data_size(),
4906 g1->get_flags(), g1->get_srid());
4907
4908 switch (g2->get_type())
4909 {
4910 case Geometry::wkb_point:
4911 {
4912 BG_models<bgcssed>::Point
4913 bg2(g2->get_data_ptr(), g2->get_data_size(),
4914 g2->get_flags(), g2->get_srid());
4915 res= bg::distance(bg1, bg2, dist_strategy);
4916 }
4917 break;
4918 case Geometry::wkb_multipoint:
4919 {
4920 BG_models<bgcssed>::Multipoint
4921 bg2(g2->get_data_ptr(), g2->get_data_size(),
4922 g2->get_flags(), g2->get_srid());
4923
4924 res= bg::distance(bg1, bg2, dist_strategy);
4925 }
4926 break;
4927 default:
4928 assert(false);
4929 break;
4930 }
4931 return res;
4932 }
4933
4934
4935 double Item_func_distance::
distance_multipoint_geometry_spherical(const Geometry * g1,const Geometry * g2)4936 distance_multipoint_geometry_spherical(const Geometry *g1, const Geometry *g2)
4937 {
4938 typedef bgcs::spherical_equatorial<bg::degree> bgcssed;
4939 double res= 0;
4940 bg::strategy::distance::haversine<double, double>
4941 dist_strategy(earth_radius);
4942
4943 BG_models<bgcssed>::Multipoint
4944 bg1(g1->get_data_ptr(), g1->get_data_size(),
4945 g1->get_flags(), g1->get_srid());
4946
4947 switch (g2->get_type())
4948 {
4949 case Geometry::wkb_point:
4950 {
4951 BG_models<bgcssed>::Point
4952 bg2(g2->get_data_ptr(), g2->get_data_size(),
4953 g2->get_flags(), g2->get_srid());
4954 res= bg::distance(bg1, bg2, dist_strategy);
4955 }
4956 break;
4957 case Geometry::wkb_multipoint:
4958 {
4959 BG_models<bgcssed>::Multipoint
4960 bg2(g2->get_data_ptr(), g2->get_data_size(),
4961 g2->get_flags(), g2->get_srid());
4962 res= bg::distance(bg1, bg2, dist_strategy);
4963 }
4964 break;
4965 default:
4966 assert(false);
4967 break;
4968 }
4969
4970 return res;
4971 }
4972
4973
4974 double Item_func_distance::
bg_distance_spherical(const Geometry * g1,const Geometry * g2)4975 bg_distance_spherical(const Geometry *g1, const Geometry *g2)
4976 {
4977 double res= 0;
4978
4979 try
4980 {
4981 switch (g1->get_type())
4982 {
4983 case Geometry::wkb_point:
4984 res= distance_point_geometry_spherical(g1, g2);
4985 break;
4986 case Geometry::wkb_multipoint:
4987 res= distance_multipoint_geometry_spherical(g1, g2);
4988 break;
4989 default:
4990 assert(false);
4991 break;
4992 }
4993 }
4994 catch (...)
4995 {
4996 null_value= true;
4997 handle_gis_exception("st_distance_sphere");
4998 }
4999
5000 return res;
5001 }
5002
5003
5004 template <typename Coordsys, typename BG_geometry>
5005 double Item_func_distance::
distance_dispatch_second_geometry(const BG_geometry & bg1,const Geometry * g2)5006 distance_dispatch_second_geometry(const BG_geometry& bg1, const Geometry* g2)
5007 {
5008 double res= 0;
5009 switch (g2->get_type())
5010 {
5011 case Geometry::wkb_point:
5012 {
5013 typename BG_models<Coordsys>::Point
5014 bg2(g2->get_data_ptr(), g2->get_data_size(),
5015 g2->get_flags(), g2->get_srid());
5016 res= bg::distance(bg1, bg2);
5017 }
5018 break;
5019 case Geometry::wkb_multipoint:
5020 {
5021 typename BG_models<Coordsys>::Multipoint
5022 bg2(g2->get_data_ptr(), g2->get_data_size(),
5023 g2->get_flags(), g2->get_srid());
5024 res= bg::distance(bg1, bg2);
5025 }
5026 break;
5027 case Geometry::wkb_linestring:
5028 {
5029 typename BG_models<Coordsys>::Linestring
5030 bg2(g2->get_data_ptr(), g2->get_data_size(),
5031 g2->get_flags(), g2->get_srid());
5032 res= bg::distance(bg1, bg2);
5033 }
5034 break;
5035 case Geometry::wkb_multilinestring:
5036 {
5037 typename BG_models<Coordsys>::Multilinestring
5038 bg2(g2->get_data_ptr(), g2->get_data_size(),
5039 g2->get_flags(), g2->get_srid());
5040 res= bg::distance(bg1, bg2);
5041 }
5042 break;
5043 case Geometry::wkb_polygon:
5044 {
5045 typename BG_models<Coordsys>::Polygon
5046 bg2(g2->get_data_ptr(), g2->get_data_size(),
5047 g2->get_flags(), g2->get_srid());
5048 res= bg::distance(bg1, bg2);
5049 }
5050 break;
5051 case Geometry::wkb_multipolygon:
5052 {
5053 typename BG_models<Coordsys>::Multipolygon
5054 bg2(g2->get_data_ptr(), g2->get_data_size(),
5055 g2->get_flags(), g2->get_srid());
5056 res= bg::distance(bg1, bg2);
5057 }
5058 break;
5059 default:
5060 assert(false);
5061 break;
5062 }
5063 return res;
5064 }
5065
5066
5067 /*
5068 Calculate distance of g1 and g2 using Boost.Geometry. We split the
5069 implementation into 6 smaller functions according to the type of g1, to
5070 make all functions smaller in size. Because distance is symmetric, we swap
5071 parameters if the swapped type combination is already implemented.
5072 */
5073 template <typename Coordsys>
bg_distance(const Geometry * g1,const Geometry * g2)5074 double Item_func_distance::bg_distance(const Geometry *g1, const Geometry *g2)
5075 {
5076 double res= 0;
5077 bool had_except= false;
5078
5079 try
5080 {
5081 switch (g1->get_type())
5082 {
5083 case Geometry::wkb_point:
5084 {
5085 typename BG_models<Coordsys>::Point
5086 bg1(g1->get_data_ptr(), g1->get_data_size(),
5087 g1->get_flags(), g1->get_srid());
5088 res= distance_dispatch_second_geometry<Coordsys>(bg1, g2);
5089 }
5090 break;
5091 case Geometry::wkb_multipoint:
5092 {
5093 typename BG_models<Coordsys>::Multipoint
5094 bg1(g1->get_data_ptr(), g1->get_data_size(),
5095 g1->get_flags(), g1->get_srid());
5096 res= distance_dispatch_second_geometry<Coordsys>(bg1, g2);
5097 }
5098 break;
5099 case Geometry::wkb_linestring:
5100 {
5101 typename BG_models<Coordsys>::Linestring
5102 bg1(g1->get_data_ptr(), g1->get_data_size(),
5103 g1->get_flags(), g1->get_srid());
5104 res= distance_dispatch_second_geometry<Coordsys>(bg1, g2);
5105 }
5106 break;
5107 case Geometry::wkb_multilinestring:
5108 {
5109 typename BG_models<Coordsys>::Multilinestring
5110 bg1(g1->get_data_ptr(), g1->get_data_size(),
5111 g1->get_flags(), g1->get_srid());
5112 res= distance_dispatch_second_geometry<Coordsys>(bg1, g2);
5113 }
5114 break;
5115 case Geometry::wkb_polygon:
5116 {
5117 typename BG_models<Coordsys>::Polygon
5118 bg1(g1->get_data_ptr(), g1->get_data_size(),
5119 g1->get_flags(), g1->get_srid());
5120 res= distance_dispatch_second_geometry<Coordsys>(bg1, g2);
5121 }
5122 break;
5123 case Geometry::wkb_multipolygon:
5124 {
5125 typename BG_models<Coordsys>::Multipolygon
5126 bg1(g1->get_data_ptr(), g1->get_data_size(),
5127 g1->get_flags(), g1->get_srid());
5128 res= distance_dispatch_second_geometry<Coordsys>(bg1, g2);
5129 }
5130 break;
5131 default:
5132 assert(false);
5133 break;
5134 }
5135 }
5136 catch (...)
5137 {
5138 had_except= true;
5139 handle_gis_exception("st_distance");
5140 }
5141
5142 if (had_except)
5143 return error_real();
5144
5145 return res;
5146 }
5147
5148
5149