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