1 /* Copyright (c) 2014, 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 implementations of GIS set operation functions.
29 */
30 #include "my_config.h"
31 #include "item_geofunc_internal.h"
32 
33 #include <set>
34 
35 #define BGOPCALL(GeoOutType, geom_out, bgop,                            \
36                  GeoType1, g1, GeoType2, g2, wkbres, nullval)           \
37 do                                                                      \
38 {                                                                       \
39   const void *pg1= g1->normalize_ring_order();                          \
40   const void *pg2= g2->normalize_ring_order();                          \
41   geom_out= NULL;                                                       \
42   if (pg1 != NULL && pg2 != NULL)                                       \
43   {                                                                     \
44     GeoType1 geo1(pg1, g1->get_data_size(), g1->get_flags(),            \
45                   g1->get_srid());                                      \
46     GeoType2 geo2(pg2, g2->get_data_size(), g2->get_flags(),            \
47                   g2->get_srid());                                      \
48     auto_ptr<GeoOutType>geout(new GeoOutType());                        \
49     geout->set_srid(g1->get_srid());                                    \
50     boost::geometry::bgop(geo1, geo2, *geout);                          \
51     (nullval)= false;                                                   \
52     if (geout->size() == 0 ||                                           \
53         (nullval= post_fix_result(&(m_ifso->bg_resbuf_mgr),             \
54                                   *geout, wkbres)))                     \
55     {                                                                   \
56       if (nullval)                                                      \
57         return NULL;                                                    \
58     }                                                                   \
59     else                                                                \
60       geom_out= geout.release();                                        \
61   }                                                                     \
62   else                                                                  \
63   {                                                                     \
64     (nullval)= true;                                                    \
65     my_error(ER_GIS_INVALID_DATA, MYF(0), "st_" #bgop);                 \
66     return NULL;                                                        \
67   }                                                                     \
68 } while (0)
69 
70 
~Item_func_spatial_operation()71 Item_func_spatial_operation::~Item_func_spatial_operation()
72 {
73 }
74 
75 
76 /*
77   Write an empty geometry collection's wkb encoding into str, and create a
78   geometry object for this empty geometry colletion.
79  */
empty_result(String * str,uint32 srid)80 Geometry *Item_func_spatial_operation::empty_result(String *str, uint32 srid)
81 {
82   if ((null_value= str->reserve(GEOM_HEADER_SIZE + 4 + 16, 256)))
83     return 0;
84 
85   write_geometry_header(str, srid, Geometry::wkb_geometrycollection, 0);
86   Gis_geometry_collection *gcol= new Gis_geometry_collection();
87   gcol->set_data_ptr(str->ptr() + GEOM_HEADER_SIZE, 4);
88   gcol->has_geom_header_space(true);
89   return gcol;
90 }
91 
92 
93 /**
94   Wraps and dispatches type specific BG function calls according to operation
95   type and the 1st or both operand type(s), depending on code complexity.
96 
97   We want to isolate boost header file inclusion only inside this file, so we
98   can't put this class declaration in any header file. And we want to make the
99   methods static since no state is needed here.
100   @tparam Geom_types A wrapper for all geometry types.
101 */
102 template<typename Geom_types>
103 class BG_setop_wrapper
104 {
105   // Some computation in this class may rely on functions in
106   // Item_func_spatial_operation.
107   Item_func_spatial_operation *m_ifso;
108   my_bool null_value; // Whether computation has error.
109 
110   // Some computation in this class may rely on functions in
111   // Item_func_spatial_operation, after each call of its functions, copy its
112   // null_value, we don't want to miss errors.
copy_ifso_state()113   void copy_ifso_state()
114   {
115     null_value= m_ifso->null_value;
116   }
117 
118 public:
119   typedef typename Geom_types::Point Point;
120   typedef typename Geom_types::Linestring Linestring;
121   typedef typename Geom_types::Polygon Polygon;
122   typedef typename Geom_types::Polygon::ring_type Polygon_ring;
123   typedef typename Geom_types::Multipoint Multipoint;
124   typedef typename Geom_types::Multilinestring Multilinestring;
125   typedef typename Geom_types::Multipolygon Multipolygon;
126   typedef typename Geom_types::Coordinate_type Coord_type;
127   typedef typename Geom_types::Coordinate_system Coordsys;
128   typedef Item_func_spatial_rel Ifsr;
129   typedef Item_func_spatial_operation Ifso;
130   typedef std::set<Point, bgpt_lt> Point_set;
131   typedef std::vector<Point> Point_vector;
132 
BG_setop_wrapper(Item_func_spatial_operation * ifso)133   BG_setop_wrapper(Item_func_spatial_operation *ifso)
134   {
135     m_ifso= ifso;
136     null_value= 0;
137   }
138 
139 
get_null_value() const140   my_bool get_null_value() const
141   {
142     return null_value;
143   }
144 
145 
146   /**
147     Do point insersection point operation.
148     @param g1 First geometry operand, must be a point.
149     @param g2 Second geometry operand, must be a point.
150     @param[out] result Holds WKB data of the result.
151     @return the result Geometry whose WKB data is in result.
152     */
point_intersection_point(Geometry * g1,Geometry * g2,String * result)153   Geometry *point_intersection_point(Geometry *g1, Geometry *g2,
154                                      String *result)
155   {
156     Geometry *retgeo= NULL;
157 
158     Point pt1(g1->get_data_ptr(),
159               g1->get_data_size(), g1->get_flags(), g1->get_srid());
160     Point pt2(g2->get_data_ptr(),
161               g2->get_data_size(), g2->get_flags(), g2->get_srid());
162 
163     if (bgpt_eq()(pt1, pt2))
164     {
165       retgeo= g1;
166       null_value= retgeo->as_geometry(result, true);
167     }
168     else
169     {
170       retgeo= m_ifso->empty_result(result, g1->get_srid());
171       copy_ifso_state();
172     }
173     return retgeo;
174   }
175 
176 
177   /*
178     Do point intersection Multipoint operation.
179     The parameters and return value has identical/similar meaning as to above
180     function, which can be inferred from the function name, we won't repeat
181     here or for the rest of the functions in this class.
182   */
point_intersection_multipoint(Geometry * g1,Geometry * g2,String * result)183   Geometry *point_intersection_multipoint(Geometry *g1, Geometry *g2,
184                                           String *result)
185   {
186     Geometry *retgeo= NULL;
187 
188     Point pt(g1->get_data_ptr(),
189              g1->get_data_size(), g1->get_flags(), g1->get_srid());
190     Multipoint mpts(g2->get_data_ptr(),
191                     g2->get_data_size(), g2->get_flags(), g2->get_srid());
192     Point_set ptset(mpts.begin(), mpts.end());
193 
194     if (ptset.find(pt) != ptset.end())
195     {
196       retgeo= g1;
197       null_value= retgeo->as_geometry(result, true);
198     }
199     else
200     {
201       retgeo= m_ifso->empty_result(result, g1->get_srid());
202       copy_ifso_state();
203     }
204     return retgeo;
205   }
206 
207 
point_intersection_geometry(Geometry * g1,Geometry * g2,String * result)208   Geometry *point_intersection_geometry(Geometry *g1, Geometry *g2,
209                                         String *result)
210   {
211 #if !defined(NDEBUG)
212     Geometry::wkbType gt2= g2->get_type();
213 #endif
214     Geometry *retgeo= NULL;
215 
216     bool is_out= !Ifsr::bg_geo_relation_check<Coordsys>
217       (g1, g2, Ifsr::SP_DISJOINT_FUNC, &null_value);
218 
219     assert(gt2 == Geometry::wkb_linestring ||
220            gt2 == Geometry::wkb_polygon ||
221            gt2 == Geometry::wkb_multilinestring ||
222            gt2 == Geometry::wkb_multipolygon);
223     if (!null_value)
224     {
225       if (is_out)
226       {
227         null_value= g1->as_geometry(result, true);
228         retgeo= g1;
229       }
230       else
231       {
232         retgeo= m_ifso->empty_result(result, g1->get_srid());
233         copy_ifso_state();
234       }
235     }
236     return retgeo;
237   }
238 
239 
multipoint_intersection_multipoint(Geometry * g1,Geometry * g2,String * result)240   Geometry *multipoint_intersection_multipoint(Geometry *g1, Geometry *g2,
241                                                String *result)
242   {
243     Geometry *retgeo= NULL;
244     Point_set ptset1, ptset2;
245     Multipoint *mpts= new Multipoint();
246     auto_ptr<Multipoint> guard(mpts);
247 
248     mpts->set_srid(g1->get_srid());
249 
250     Multipoint mpts1(g1->get_data_ptr(),
251                      g1->get_data_size(), g1->get_flags(), g1->get_srid());
252     Multipoint mpts2(g2->get_data_ptr(),
253                      g2->get_data_size(), g2->get_flags(), g2->get_srid());
254 
255     ptset1.insert(mpts1.begin(), mpts1.end());
256     ptset2.insert(mpts2.begin(), mpts2.end());
257 
258     Point_vector respts;
259     TYPENAME Point_vector::iterator endpos;
260     size_t ptset1sz= ptset1.size(), ptset2sz= ptset2.size();
261     respts.resize(ptset1sz > ptset2sz ? ptset1sz : ptset2sz);
262 
263     endpos= std::set_intersection(ptset1.begin(), ptset1.end(),
264                                   ptset2.begin(), ptset2.end(),
265                                   respts.begin(), bgpt_lt());
266     std::copy(respts.begin(), endpos, std::back_inserter(*mpts));
267     if (mpts->size() > 0)
268     {
269       null_value= m_ifso->assign_result(mpts, result);
270       retgeo= mpts;
271       guard.release();
272     }
273     else
274     {
275       retgeo= m_ifso->empty_result(result, g1->get_srid());
276       copy_ifso_state();
277     }
278 
279     return retgeo;
280   }
281 
282 
multipoint_intersection_geometry(Geometry * g1,Geometry * g2,String * result)283   Geometry *multipoint_intersection_geometry(Geometry *g1, Geometry *g2,
284                                              String *result)
285   {
286     Geometry *retgeo= NULL;
287 #if !defined(NDEBUG)
288     Geometry::wkbType gt2= g2->get_type();
289 #endif
290     Point_set ptset;
291     Multipoint mpts(g1->get_data_ptr(),
292                     g1->get_data_size(), g1->get_flags(), g1->get_srid());
293     Multipoint *mpts2= new Multipoint();
294     auto_ptr<Multipoint> guard(mpts2);
295 
296     mpts2->set_srid(g1->get_srid());
297 
298     assert(gt2 == Geometry::wkb_linestring ||
299            gt2 == Geometry::wkb_polygon ||
300            gt2 == Geometry::wkb_multilinestring ||
301            gt2 == Geometry::wkb_multipolygon);
302     ptset.insert(mpts.begin(), mpts.end());
303 
304     for (TYPENAME Point_set::iterator i= ptset.begin(); i != ptset.end(); ++i)
305     {
306       Point &pt= const_cast<Point&>(*i);
307       if (!Ifsr::bg_geo_relation_check<Coordsys>
308           (&pt, g2, Ifsr::SP_DISJOINT_FUNC, &null_value) && !null_value)
309       {
310         mpts2->push_back(pt);
311       }
312 
313       if (null_value)
314         return 0;
315     }
316 
317     if (mpts2->size() > 0)
318     {
319       null_value= m_ifso->assign_result(mpts2, result);
320       retgeo= mpts2;
321       guard.release();
322     }
323     else
324     {
325       retgeo= m_ifso->empty_result(result, g1->get_srid());
326       copy_ifso_state();
327     }
328 
329     return retgeo;
330   }
331 
332 
linestring_intersection_linestring(Geometry * g1,Geometry * g2,String * result)333   Geometry *linestring_intersection_linestring(Geometry *g1, Geometry *g2,
334                                                String *result)
335   {
336     Multilinestring *res= NULL;
337     Geometry *retgeo= NULL;
338 
339     // BG will return all intersection lines and points in a
340     // multilinestring. Intersection points are represented as lines
341     // with the same start and end points.
342     BGOPCALL(Multilinestring, res, intersection,
343              Linestring, g1, Linestring, g2, NULL, null_value);
344 
345     if (res == NULL)
346       return m_ifso->empty_result(result, g1->get_srid());
347 
348     retgeo= m_ifso->simplify_multilinestring(res, result);
349     delete res;
350     return retgeo;
351   }
352 
353 
linestring_intersection_polygon(Geometry * g1,Geometry * g2,String * result)354   Geometry *linestring_intersection_polygon(Geometry *g1, Geometry *g2,
355                                             String *result)
356   {
357     Geometry::wkbType gt2= g2->get_type();
358     Geometry *retgeo= NULL, *tmp1= NULL, *tmp2= NULL;
359     // It is likely for there to be discrete intersection Points.
360     if (gt2 == Geometry::wkb_multipolygon)
361     {
362       BGOPCALL(Multilinestring, tmp1, intersection,
363                Linestring, g1, Multipolygon, g2, NULL, null_value);
364       BGOPCALL(Multipoint, tmp2, intersection,
365                Linestring, g1, Multipolygon, g2, NULL, null_value);
366     }
367     else
368     {
369       BGOPCALL(Multilinestring, tmp1, intersection,
370                Linestring, g1, Polygon, g2, NULL, null_value);
371       BGOPCALL(Multipoint, tmp2, intersection,
372                Linestring, g1, Polygon, g2, NULL, null_value);
373     }
374 
375     // Need merge, exclude Points that are on the result Linestring.
376     retgeo= m_ifso->combine_sub_results<Coordsys>
377       (tmp1, tmp2, result);
378     copy_ifso_state();
379 
380     return retgeo;
381   }
382 
383 
linestring_intersection_multilinestring(Geometry * g1,Geometry * g2,String * result)384   Geometry *linestring_intersection_multilinestring(Geometry *g1, Geometry *g2,
385                                                     String *result)
386   {
387     Multilinestring *res= NULL;
388     Geometry *retgeo= NULL;
389 
390     // BG will return all intersection lines and points in a
391     // multilinestring. Intersection points are represented as lines
392     // with the same start and end points.
393     BGOPCALL(Multilinestring, res, intersection,
394              Linestring, g1, Multilinestring, g2, NULL, null_value);
395 
396     if (res == NULL || res->size() == 0)
397       return m_ifso->empty_result(result, g1->get_srid());
398 
399     retgeo= m_ifso->simplify_multilinestring(res, result);
400     delete res;
401     return retgeo;
402   }
403 
404 
polygon_intersection_multilinestring(Geometry * g1,Geometry * g2,String * result)405   Geometry *polygon_intersection_multilinestring(Geometry *g1, Geometry *g2,
406                                                  String *result)
407   {
408     Geometry *retgeo= NULL, *tmp1= NULL;
409     Multipoint *tmp2= NULL;
410     auto_ptr<Geometry> guard1;
411 
412 
413     BGOPCALL(Multilinestring, tmp1, intersection,
414              Polygon, g1, Multilinestring, g2, NULL, null_value);
415     guard1.reset(tmp1);
416 
417     Multilinestring mlstr(g2->get_data_ptr(), g2->get_data_size(),
418                           g2->get_flags(), g2->get_srid());
419     Multipoint mpts;
420     Point_set ptset;
421 
422     const void *data_ptr= g1->normalize_ring_order();
423     if (data_ptr == NULL)
424     {
425       null_value= true;
426       my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
427       return NULL;
428     }
429 
430     Polygon plgn(data_ptr, g1->get_data_size(),
431                  g1->get_flags(), g1->get_srid());
432 
433     for (TYPENAME Multilinestring::iterator i= mlstr.begin();
434          i != mlstr.end(); ++i)
435     {
436       boost::geometry::intersection(plgn, *i, mpts);
437       if (mpts.size() > 0)
438       {
439         ptset.insert(mpts.begin(), mpts.end());
440         mpts.clear();
441       }
442     }
443 
444     auto_ptr<Multipoint> guard2;
445     if (ptset.size() > 0)
446     {
447       tmp2= new Multipoint;
448       tmp2->set_srid(g1->get_srid());
449       guard2.reset(tmp2);
450       std::copy(ptset.begin(), ptset.end(), std::back_inserter(*tmp2));
451     }
452 
453     retgeo= m_ifso->combine_sub_results<Coordsys>
454       (guard1.release(), guard2.release(), result);
455     copy_ifso_state();
456 
457     return retgeo;
458   }
459 
460 
461   /**
462     Compute the multilinestring result if any for intersection of two polygons.
463     First get the multilinestrings mls1 and mls2 from polygons, then compute
464     mls3 = mls1 intersection mls2, and finally return mls3 - mls4 as result,
465     where mls4 is got from rings of the multipolygon intersection result of
466     plgn1 and plgn2.
467 
468     @param plgn1 the 1st polygon operand
469     @param plgn2 the 2nd polygon operand
470     @param mplgn the areal intersection result of plgn1 and plgn2
471     @param [out] mls the result multilinestring
472    */
473   template<typename Polygon_type1, typename Polygon_type2>
plgn_intersection_plgn_mls(const Polygon_type1 & plgn1,const Polygon_type2 & plgn2,const Multipolygon & mplgn,Multilinestring & mls)474   void plgn_intersection_plgn_mls(const Polygon_type1 &plgn1,
475                                   const Polygon_type2 &plgn2,
476                                   const Multipolygon &mplgn,
477                                   Multilinestring &mls)
478   {
479     if (mplgn.size() > 0)
480     {
481       /*
482         Remove the linestring parts that are part of the result polygon rings.
483         The discrete intersection points that are also removed here.
484       */
485       Multilinestring mls3;
486       bg::intersection(plgn1, plgn2, mls3);
487       bg::detail::boundary_view<Multipolygon const> view(mplgn);
488       bg::difference(mls3, view, mls);
489     }
490     else
491       bg::intersection(plgn1, plgn2, mls);
492   }
493 
494 
polygon_intersection_polygon(Geometry * g1,Geometry * g2,String * result)495   Geometry *polygon_intersection_polygon(Geometry *g1, Geometry *g2,
496                                          String *result)
497   {
498     Geometry::wkbType gt2= g2->get_type();
499     Geometry *retgeo= NULL;
500 
501     const void *pg1= g1->normalize_ring_order();
502     const void *pg2= g2->normalize_ring_order();
503     if (pg1 == NULL || pg2 == NULL)
504     {
505       null_value= true;
506       my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
507       return NULL;
508     }
509 
510     Multilinestring mls;
511     Polygon plgn1(pg1, g1->get_data_size(), g1->get_flags(),
512                   g1->get_srid());
513 
514     auto_ptr<Multipolygon> mplgn_result(new Multipolygon());
515     mplgn_result->set_srid(g1->get_srid());
516 
517     if (gt2 == Geometry::wkb_polygon)
518     {
519       Polygon plgn2(pg2, g2->get_data_size(), g2->get_flags(),
520                     g2->get_srid());
521       bg::intersection(plgn1, plgn2, *mplgn_result);
522       plgn_intersection_plgn_mls(plgn1, plgn2, *mplgn_result, mls);
523     }
524     else
525     {
526       Multipolygon mplgn2(pg2, g2->get_data_size(), g2->get_flags(),
527                           g2->get_srid());
528       bg::intersection(plgn1, mplgn2, *mplgn_result);
529       plgn_intersection_plgn_mls(plgn1, mplgn2, *mplgn_result, mls);
530     }
531 
532     retgeo= combine_mls_mplgn_results(&mls, mplgn_result, result);
533     copy_ifso_state();
534 
535     return retgeo;
536   }
537 
combine_mls_mplgn_results(Multilinestring * mls,auto_ptr<Multipolygon> mplgn_result,String * result)538   Geometry *combine_mls_mplgn_results(Multilinestring *mls,
539                                       auto_ptr<Multipolygon> mplgn_result,
540                                       String *result)
541   {
542     Geometry *geom= NULL, *retgeo= NULL;
543     assert(mls != NULL);
544 
545     if (mls->size() > 0)
546       geom= m_ifso->simplify_multilinestring(mls, result);
547 
548     if (mplgn_result->size() > 0)
549     {
550       if (mls->size() == 0)
551       {
552         // The multipolygon is the only result.
553         assert(result->length() == 0);
554         null_value= post_fix_result(&(m_ifso->bg_resbuf_mgr),
555                                     *mplgn_result, result);
556         if (null_value)
557           return NULL;
558         else
559           retgeo= mplgn_result.release();
560       }
561       else
562       {
563         String mplgn_resbuf;
564         null_value= post_fix_result(&(m_ifso->bg_resbuf_mgr),
565                                     *mplgn_result, &mplgn_resbuf);
566         if (null_value)
567           return NULL;
568         if (geom->get_type() == Geometry::wkb_geometrycollection)
569         {
570           down_cast<Gis_geometry_collection *>(geom)->
571             append_geometry(&(*mplgn_result), result);
572           retgeo= geom;
573         }
574         else
575         {
576           /*
577             The geom created from mls isn't a GC, so we have to create a GC to
578             hole both geom and mplgn_result.
579            */
580           String tmp_mls_resbuf;
581           tmp_mls_resbuf.takeover(*result);
582 
583           Gis_geometry_collection *tmp_gc=
584             new Gis_geometry_collection(&(*mplgn_result), result);
585           tmp_gc->append_geometry(geom, result);
586           retgeo= tmp_gc;
587           delete geom;
588         }
589       }
590     }
591     else
592       retgeo= geom;
593 
594     if (retgeo == NULL)
595       retgeo= m_ifso->empty_result(result, mplgn_result->get_srid());
596 
597     return retgeo;
598   }
599 
600 
multilinestring_intersection_multilinestring(Geometry * g1,Geometry * g2,String * result)601   Geometry *multilinestring_intersection_multilinestring(Geometry *g1,
602                                                          Geometry *g2,
603                                                          String *result)
604   {
605     Multilinestring *res= NULL;
606     Geometry *retgeo= NULL;
607 
608     // BG will return all intersection lines and points in a
609     // multilinestring. Intersection points are represented as lines
610     // with the same start and end points.
611     BGOPCALL(Multilinestring, res, intersection,
612              Multilinestring, g1, Multilinestring, g2, NULL, null_value);
613 
614     if (res == NULL)
615       return m_ifso->empty_result(result, g1->get_srid());
616 
617     retgeo= m_ifso->simplify_multilinestring(res, result);
618     delete res;
619     return retgeo;
620   }
621 
622 
multilinestring_intersection_multipolygon(Geometry * g1,Geometry * g2,String * result)623   Geometry *multilinestring_intersection_multipolygon(Geometry *g1,
624                                                       Geometry *g2,
625                                                       String *result)
626   {
627     Geometry *retgeo= NULL, *tmp1= NULL;
628     Multipoint *tmp2= NULL;
629 
630     auto_ptr<Geometry> guard1;
631     BGOPCALL(Multilinestring, tmp1, intersection,
632              Multilinestring, g1, Multipolygon, g2,
633              NULL, null_value);
634     guard1.reset(tmp1);
635 
636     Multilinestring mlstr(g1->get_data_ptr(), g1->get_data_size(),
637                           g1->get_flags(), g1->get_srid());
638     Multipoint mpts;
639 
640     const void *data_ptr= g2->normalize_ring_order();
641     if (data_ptr == NULL)
642     {
643       null_value= true;
644       my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
645       return NULL;
646     }
647 
648     Multipolygon mplgn(data_ptr, g2->get_data_size(),
649                        g2->get_flags(), g2->get_srid());
650     Point_set ptset;
651 
652     for (TYPENAME Multilinestring::iterator i= mlstr.begin();
653          i != mlstr.end(); ++i)
654     {
655       boost::geometry::intersection(*i, mplgn, mpts);
656       if (mpts.size() > 0)
657       {
658         ptset.insert(mpts.begin(), mpts.end());
659         mpts.clear();
660       }
661     }
662 
663     auto_ptr<Multipoint> guard2;
664     if (ptset.empty() == false)
665     {
666       tmp2= new Multipoint;
667       tmp2->set_srid(g1->get_srid());
668       guard2.reset(tmp2);
669       std::copy(ptset.begin(), ptset.end(), std::back_inserter(*tmp2));
670     }
671 
672     retgeo= m_ifso->combine_sub_results<Coordsys>
673       (guard1.release(), guard2.release(), result);
674     copy_ifso_state();
675 
676     return retgeo;
677   }
678 
679 
multipolygon_intersection_multipolygon(Geometry * g1,Geometry * g2,String * result)680   Geometry *multipolygon_intersection_multipolygon(Geometry *g1, Geometry *g2,
681                                                    String *result)
682   {
683     Geometry *retgeo= NULL;
684 
685     const void *pg1= g1->normalize_ring_order();
686     const void *pg2= g2->normalize_ring_order();
687     if (pg1 == NULL || pg2 == NULL)
688     {
689       null_value= true;
690       my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
691       return NULL;
692     }
693 
694     Multilinestring mls;
695     Multipolygon mplgn1(pg1, g1->get_data_size(), g1->get_flags(),
696                         g1->get_srid());
697 
698     auto_ptr<Multipolygon> mplgn_result(new Multipolygon());
699     mplgn_result->set_srid(g1->get_srid());
700 
701     Multipolygon mplgn2(pg2, g2->get_data_size(), g2->get_flags(),
702                         g2->get_srid());
703     bg::intersection(mplgn1, mplgn2, *mplgn_result);
704     plgn_intersection_plgn_mls(mplgn1, mplgn2, *mplgn_result, mls);
705     retgeo= combine_mls_mplgn_results(&mls, mplgn_result, result);
706 
707     copy_ifso_state();
708 
709     return retgeo;
710   }
711 
712 
point_union_point(Geometry * g1,Geometry * g2,String * result)713   Geometry *point_union_point(Geometry *g1, Geometry *g2, String *result)
714   {
715     Geometry *retgeo= NULL;
716     Geometry::wkbType gt2= g2->get_type();
717     Point_set ptset;// Use set to make Points unique.
718 
719     Point pt1(g1->get_data_ptr(),
720               g1->get_data_size(), g1->get_flags(), g1->get_srid());
721     Multipoint *mpts= new Multipoint();
722     auto_ptr<Multipoint> guard(mpts);
723 
724     mpts->set_srid(g1->get_srid());
725     ptset.insert(pt1);
726     if (gt2 == Geometry::wkb_point)
727     {
728       Point pt2(g2->get_data_ptr(),
729                 g2->get_data_size(), g2->get_flags(), g2->get_srid());
730       ptset.insert(pt2);
731     }
732     else
733     {
734       Multipoint mpts2(g2->get_data_ptr(),
735                        g2->get_data_size(), g2->get_flags(), g2->get_srid());
736       ptset.insert(mpts2.begin(), mpts2.end());
737     }
738 
739     std::copy(ptset.begin(), ptset.end(), std::back_inserter(*mpts));
740     if (mpts->size() > 0)
741     {
742       retgeo= mpts;
743       null_value= m_ifso->assign_result(mpts, result);
744       guard.release();
745     }
746     else
747     {
748       if (!null_value)
749       {
750         retgeo= m_ifso->empty_result(result, g1->get_srid());
751         copy_ifso_state();
752       }
753     }
754     return retgeo;
755   }
756 
757 
point_union_geometry(Geometry * g1,Geometry * g2,String * result)758   Geometry *point_union_geometry(Geometry *g1, Geometry *g2, String *result)
759   {
760     Geometry *retgeo= NULL;
761 #if !defined(NDEBUG)
762     Geometry::wkbType gt2= g2->get_type();
763 #endif
764     assert(gt2 == Geometry::wkb_linestring ||
765            gt2 == Geometry::wkb_polygon ||
766            gt2 == Geometry::wkb_multilinestring ||
767            gt2 == Geometry::wkb_multipolygon);
768     if (Ifsr::bg_geo_relation_check<Coordsys>
769         (g1, g2, Ifsr::SP_DISJOINT_FUNC, &null_value) && !null_value)
770     {
771       Gis_geometry_collection *geocol= new Gis_geometry_collection(g2, result);
772       null_value= (geocol == NULL || geocol->append_geometry(g1, result));
773       retgeo= geocol;
774     }
775     else if (null_value)
776     {
777       retgeo= NULL;
778       return 0;
779     }
780     else
781     {
782       retgeo= g2;
783       null_value= retgeo->as_geometry(result, true);
784     }
785 
786     return retgeo;
787   }
788 
789 
linestring_union_linestring(Geometry * g1,Geometry * g2,String * result)790   Geometry *linestring_union_linestring(Geometry *g1, Geometry *g2,
791                                         String *result)
792   {
793     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
794                    g1->get_flags(), g1->get_srid());
795     Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
796                    g2->get_flags(), g2->get_srid());
797     auto_ptr<Multilinestring> res(new Multilinestring());
798     res->set_srid(g1->get_srid());
799 
800     boost::geometry::union_(ls1, ls2, *res);
801     assert(res.get() != 0);
802     assert(res->size() != 0);
803     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
804     {
805       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
806       null_value= TRUE;
807       return NULL;
808     }
809 
810     return res.release();
811   }
812 
813 
linestring_union_polygon(Geometry * g1,Geometry * g2,String * result)814   Geometry *linestring_union_polygon(Geometry *g1, Geometry *g2,
815                                      String *result)
816   {
817     Geometry *retgeo= NULL;
818     const void *g2_wkb= g2->normalize_ring_order();
819     if (g2_wkb == NULL)
820     {
821       // Invalid polygon
822       my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
823       null_value= TRUE;
824       return NULL;
825     }
826 
827     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
828                    g1->get_flags(), g1->get_srid());
829     Polygon py2(g2_wkb, g2->get_data_size(),
830                 g2->get_flags(), g2->get_srid());
831     auto_ptr<Multilinestring> linestrings(new Multilinestring());
832     linestrings->set_srid(g1->get_srid());
833 
834     // Union(LineString, Polygon) isn't supported by BG, but it's
835     // equivalent to GeometryCollection(Polygon, Difference(LineString,
836     // Polygon)).
837     boost::geometry::difference(ls1, py2, *linestrings);
838     assert(linestrings.get() != 0);
839     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
840         linestrings->size() > 0)
841     {
842       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
843       null_value= TRUE;
844       return NULL;
845     }
846 
847     // Return the simplest result possible.
848     if (linestrings->size() == 0)
849     {
850       // Polygon result.
851       g2->as_geometry(result, true);
852       retgeo= g2;
853     }
854     else
855     {
856       // GeometryCollection result containing one Polygon and one or
857       // more LineStrings.
858       Gis_geometry_collection *collection= new Gis_geometry_collection();
859       py2.to_wkb_unparsed();
860       collection->append_geometry(&py2, result);
861       if (linestrings->size() > 1)
862       {
863         collection->append_geometry(&(*linestrings), result);
864       }
865       else
866       {
867         collection->append_geometry(&(*linestrings)[0], result);
868       }
869       collection->set_ownmem(false);
870       retgeo= collection;
871     }
872 
873     return retgeo;
874   }
875 
876 
linestring_union_multilinestring(Geometry * g1,Geometry * g2,String * result)877   Geometry *linestring_union_multilinestring(Geometry *g1, Geometry *g2,
878                                              String *result)
879   {
880     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
881                    g1->get_flags(), g1->get_srid());
882     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
883                          g2->get_flags(), g2->get_srid());
884     auto_ptr<Multilinestring> res(new Multilinestring);
885     res->set_srid(g1->get_srid());
886 
887     boost::geometry::union_(ls1, mls2, *res);
888     assert(res.get() != 0);
889     assert(res->size() != 0);
890     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
891     {
892       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
893       null_value= TRUE;
894       return NULL;
895     }
896 
897     return res.release();
898   }
899 
900 
linestring_union_multipolygon(Geometry * g1,Geometry * g2,String * result)901   Geometry *linestring_union_multipolygon(Geometry *g1, Geometry *g2,
902                                           String *result)
903   {
904     Geometry *retgeo= NULL;
905     const void *g2_wkb= g2->normalize_ring_order();
906     if (g2_wkb == NULL)
907     {
908       // Invalid polygon
909       my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
910       null_value= TRUE;
911       return NULL;
912     }
913 
914     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
915                    g1->get_flags(), g1->get_srid());
916     Multipolygon mpy2(g2_wkb, g2->get_data_size(),
917                       g2->get_flags(), g2->get_srid());
918     auto_ptr<Multilinestring> linestrings(new Multilinestring());
919     linestrings->set_srid(g1->get_srid());
920 
921     // Union(LineString, MultiPolygon) isn't supported by BG, but it's
922     // equivalent to GeometryCollection(MultiPolygon,
923     // Difference(LineString, MultiPolygon)).
924     boost::geometry::difference(ls1, mpy2, *linestrings);
925     assert(linestrings.get() != 0);
926     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
927         linestrings->size() > 0)
928     {
929       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
930       null_value= TRUE;
931       return NULL;
932     }
933 
934     // Return the simplest result possible.
935     if (linestrings->size() == 0)
936     {
937       // MultiPolygon result.
938       g2->as_geometry(result, true);
939       retgeo= g2;
940     }
941     else
942     {
943       // GeometryCollection result containing one or more Polygons and
944       // one or more LineStrings.
945       Gis_geometry_collection *collection= new Gis_geometry_collection();
946 
947       if (mpy2.size() > 1)
948       {
949         collection->append_geometry(&mpy2, result);
950       }
951       else
952       {
953         mpy2[0].to_wkb_unparsed();
954         collection->append_geometry(&mpy2[0], result);
955       }
956 
957       if (linestrings->size() > 1)
958       {
959         collection->append_geometry(&(*linestrings), result);
960       }
961       else
962       {
963         collection->append_geometry(&(*linestrings)[0], result);
964       }
965 
966       collection->set_ownmem(false);
967       retgeo= collection;
968     }
969 
970     return retgeo;
971   }
972 
973 
polygon_union_multilinestring(Geometry * g1,Geometry * g2,String * result)974   Geometry *polygon_union_multilinestring(Geometry *g1, Geometry *g2,
975                                           String *result)
976   {
977     Geometry *retgeo= NULL;
978     const void *g1_wkb= g1->normalize_ring_order();
979     if (g1_wkb == NULL)
980     {
981       // Invalid polygon
982       my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
983       null_value= TRUE;
984       return NULL;
985     }
986 
987     Polygon py1(g1_wkb, g1->get_data_size(),
988                 g1->get_flags(), g1->get_srid());
989     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
990                          g2->get_flags(), g2->get_srid());
991     auto_ptr<Multilinestring> linestrings(new Multilinestring());
992     linestrings->set_srid(g1->get_srid());
993 
994     // Union(Polygon, MultiLineString) isn't supported by BG, but it's
995     // equivalent to GeometryCollection(Polygon,
996     // Difference(MultiLineString, Polygon)).
997     boost::geometry::difference(mls2, py1, *linestrings);
998     assert(linestrings.get() != 0);
999     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
1000         linestrings->size() > 0)
1001     {
1002       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1003       null_value= TRUE;
1004       return NULL;
1005     }
1006 
1007     // Return the simplest result possible.
1008     if (linestrings->size() == 0)
1009     {
1010       // Polygon result.
1011       g2->as_geometry(result, true);
1012       retgeo= g2;
1013     }
1014     else
1015     {
1016       // GeometryCollection result containing one Polygon and one or
1017       // more LineStrings.
1018       Gis_geometry_collection *collection= new Gis_geometry_collection();
1019       py1.to_wkb_unparsed();
1020       collection->append_geometry(&py1, result);
1021       if (linestrings->size() > 1)
1022       {
1023         collection->append_geometry(&(*linestrings), result);
1024       }
1025       else
1026       {
1027         collection->append_geometry(&(*linestrings)[0], result);
1028       }
1029       collection->set_ownmem(false);
1030       retgeo= collection;
1031     }
1032 
1033     return retgeo;
1034   }
1035 
1036 
multipoint_union_multipoint(Geometry * g1,Geometry * g2,String * result)1037   Geometry *multipoint_union_multipoint(Geometry *g1, Geometry *g2,
1038                                         String *result)
1039   {
1040     Geometry *retgeo= NULL;
1041     Point_set ptset;
1042     Multipoint *mpts= new Multipoint();
1043     auto_ptr<Multipoint> guard(mpts);
1044 
1045     mpts->set_srid(g1->get_srid());
1046     Multipoint mpts1(g1->get_data_ptr(),
1047                      g1->get_data_size(), g1->get_flags(), g1->get_srid());
1048     Multipoint mpts2(g2->get_data_ptr(),
1049                      g2->get_data_size(), g2->get_flags(), g2->get_srid());
1050 
1051     ptset.insert(mpts1.begin(), mpts1.end());
1052     ptset.insert(mpts2.begin(), mpts2.end());
1053     std::copy(ptset.begin(), ptset.end(), std::back_inserter(*mpts));
1054 
1055     if (mpts->size() > 0)
1056     {
1057       retgeo= mpts;
1058       null_value= m_ifso->assign_result(mpts, result);
1059       guard.release();
1060     }
1061     else
1062     {
1063       if (!null_value)
1064       {
1065         retgeo= m_ifso->empty_result(result, g1->get_srid());
1066         copy_ifso_state();
1067       }
1068     }
1069     return retgeo;
1070   }
1071 
1072 
multipoint_union_geometry(Geometry * g1,Geometry * g2,String * result)1073   Geometry *multipoint_union_geometry(Geometry *g1, Geometry *g2,
1074                                       String *result)
1075   {
1076     Geometry *retgeo= NULL;
1077 #if !defined(NDEBUG)
1078     Geometry::wkbType gt2= g2->get_type();
1079 #endif
1080     Point_set ptset;
1081     Multipoint mpts(g1->get_data_ptr(),
1082                     g1->get_data_size(), g1->get_flags(), g1->get_srid());
1083 
1084     assert(gt2 == Geometry::wkb_linestring ||
1085            gt2 == Geometry::wkb_polygon ||
1086            gt2 == Geometry::wkb_multilinestring ||
1087            gt2 == Geometry::wkb_multipolygon);
1088     ptset.insert(mpts.begin(), mpts.end());
1089 
1090     Gis_geometry_collection *geocol= new Gis_geometry_collection(g2, result);
1091     auto_ptr<Gis_geometry_collection> guard(geocol);
1092     bool added= false;
1093 
1094     for (TYPENAME Point_set::iterator i= ptset.begin(); i != ptset.end(); ++i)
1095     {
1096       Point &pt= const_cast<Point&>(*i);
1097       if (Ifsr::bg_geo_relation_check<Coordsys>
1098           (&pt, g2, Ifsr::SP_DISJOINT_FUNC, &null_value))
1099       {
1100         if (null_value || (null_value= geocol->append_geometry(&pt, result)))
1101           break;
1102         added= true;
1103       }
1104     }
1105 
1106     if (null_value)
1107       return 0;
1108 
1109     if (added)
1110     {
1111       // Result is already filled above.
1112       retgeo= geocol;
1113       guard.release();
1114     }
1115     else
1116     {
1117       retgeo= g2;
1118       null_value= g2->as_geometry(result, true);
1119     }
1120 
1121     return retgeo;
1122   }
1123 
1124 
multilinestring_union_multilinestring(Geometry * g1,Geometry * g2,String * result)1125   Geometry *multilinestring_union_multilinestring(Geometry *g1, Geometry *g2,
1126                                                   String *result)
1127   {
1128     Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1129                          g1->get_flags(), g1->get_srid());
1130     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1131                          g2->get_flags(), g2->get_srid());
1132     auto_ptr<Multilinestring> res(new Multilinestring());
1133     res->set_srid(g1->get_srid());
1134 
1135     boost::geometry::union_(mls1, mls2, *res);
1136     assert(res.get() != 0);
1137     assert(res->size() != 0);
1138     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1139     {
1140       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1141       null_value= TRUE;
1142       return NULL;
1143     }
1144 
1145     return res.release();
1146   }
1147 
1148 
multilinestring_union_multipolygon(Geometry * g1,Geometry * g2,String * result)1149   Geometry *multilinestring_union_multipolygon(Geometry *g1, Geometry *g2,
1150                                                String *result)
1151   {
1152     Geometry *retgeo= NULL;
1153     const void *g2_wkb= g2->normalize_ring_order();
1154     if (g2_wkb == NULL)
1155     {
1156       // Invalid polygon
1157       my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
1158       null_value= TRUE;
1159       return NULL;
1160     }
1161 
1162     Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1163                          g1->get_flags(), g1->get_srid());
1164     Multipolygon mpy2(g2_wkb, g2->get_data_size(),
1165                       g2->get_flags(), g2->get_srid());
1166     auto_ptr<Multilinestring> linestrings(new Multilinestring());
1167     linestrings->set_srid(g1->get_srid());
1168 
1169     // Union(MultiLineString, MultiPolygon) isn't supported by BG, but
1170     // it's equivalent to GeometryCollection(MultiPolygon,
1171     // Difference(MultiLineString, MultiPolygon)).
1172     boost::geometry::difference(mls1, mpy2, *linestrings);
1173     assert(linestrings.get() != 0);
1174     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
1175         linestrings->size() > 0)
1176     {
1177       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1178       null_value= TRUE;
1179       return NULL;
1180     }
1181 
1182     // Return the simplest result possible.
1183     if (linestrings->size() == 0)
1184     {
1185       // MultiPolygon result.
1186       g2->as_geometry(result, true);
1187       retgeo= g2;
1188     }
1189     else
1190     {
1191       // GeometryCollection result containing one or more Polygons and
1192       // one or more LineStrings.
1193       Gis_geometry_collection *collection= new Gis_geometry_collection();
1194 
1195       if (mpy2.size() > 1)
1196       {
1197         collection->append_geometry(&mpy2, result);
1198       }
1199       else
1200       {
1201         mpy2[0].to_wkb_unparsed();
1202         collection->append_geometry(&mpy2[0], result);
1203       }
1204 
1205       if (linestrings->size() > 1)
1206       {
1207         collection->append_geometry(&(*linestrings), result);
1208       }
1209       else
1210       {
1211         collection->append_geometry(&(*linestrings)[0], result);
1212       }
1213 
1214       collection->set_ownmem(false);
1215       retgeo= collection;
1216     }
1217 
1218     return retgeo;
1219   }
1220 
1221 
polygon_union_polygon(Geometry * g1,Geometry * g2,String * result)1222   Geometry *polygon_union_polygon(Geometry *g1, Geometry *g2, String *result)
1223   {
1224     Geometry *retgeo= NULL;
1225     const void *g1_wkb= g1->normalize_ring_order();
1226     const void *g2_wkb= g2->normalize_ring_order();
1227     if (g1_wkb == NULL || g2_wkb == NULL)
1228     {
1229       // Invalid polygon
1230       my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
1231       null_value= TRUE;
1232       return NULL;
1233     }
1234 
1235     Polygon py1(g1->get_data_ptr(), g1->get_data_size(), g1->get_flags(),
1236                 g1->get_srid());
1237     Polygon py2(g2_wkb, g2->get_data_size(), g2->get_flags(),
1238                 g2->get_srid());
1239     auto_ptr<Multipolygon> res(new Multipolygon());
1240     res->set_srid(g1->get_srid());
1241 
1242     boost::geometry::union_(py1, py2, *res);
1243     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1244         res->size() > 0)
1245     {
1246       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1247       null_value= TRUE;
1248       return NULL;
1249     }
1250 
1251     if (res->size() == 0)
1252     {
1253       // Invalid polygon
1254       my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
1255       null_value= TRUE;
1256       return NULL;
1257     }
1258 
1259     retgeo= res.release();
1260     return retgeo;
1261   }
1262 
1263 
polygon_union_multipolygon(Geometry * g1,Geometry * g2,String * result)1264   Geometry *polygon_union_multipolygon(Geometry *g1, Geometry *g2,
1265                                        String *result)
1266   {
1267     Geometry *retgeo= NULL;
1268 
1269     BGOPCALL(Multipolygon, retgeo, union_,
1270              Polygon, g1, Multipolygon, g2, result, null_value);
1271     return retgeo;
1272   }
1273 
1274 
multipolygon_union_multipolygon(Geometry * g1,Geometry * g2,String * result)1275   Geometry *multipolygon_union_multipolygon(Geometry *g1, Geometry *g2,
1276                                             String *result)
1277   {
1278     Geometry *retgeo= NULL;
1279 
1280     BGOPCALL(Multipolygon, retgeo, union_,
1281              Multipolygon, g1, Multipolygon, g2, result, null_value);
1282 
1283     return retgeo;
1284   }
1285 
1286 
point_difference_geometry(Geometry * g1,Geometry * g2,String * result)1287   Geometry *point_difference_geometry(Geometry *g1, Geometry *g2,
1288                                       String *result)
1289   {
1290     Geometry *retgeo= NULL;
1291     bool is_out= Ifsr::bg_geo_relation_check<Coordsys>
1292       (g1, g2, Ifsr::SP_DISJOINT_FUNC, &null_value);
1293 
1294     if (!null_value)
1295     {
1296       if (is_out)
1297       {
1298         retgeo= g1;
1299         null_value= retgeo->as_geometry(result, true);
1300       }
1301       else
1302       {
1303         retgeo= m_ifso->empty_result(result, g1->get_srid());
1304         copy_ifso_state();
1305       }
1306     }
1307     return retgeo;
1308   }
1309 
1310 
multipoint_difference_geometry(Geometry * g1,Geometry * g2,String * result)1311   Geometry *multipoint_difference_geometry(Geometry *g1, Geometry *g2,
1312                                            String *result)
1313   {
1314     Geometry *retgeo= NULL;
1315     Multipoint *mpts= new Multipoint();
1316     auto_ptr<Multipoint> guard(mpts);
1317 
1318     mpts->set_srid(g1->get_srid());
1319     Multipoint mpts1(g1->get_data_ptr(),
1320                      g1->get_data_size(), g1->get_flags(), g1->get_srid());
1321     Point_set ptset;
1322 
1323     for (TYPENAME Multipoint::iterator i= mpts1.begin();
1324          i != mpts1.end(); ++i)
1325     {
1326       if (Ifsr::bg_geo_relation_check<Coordsys>
1327           (&(*i), g2, Ifsr::SP_DISJOINT_FUNC, &null_value))
1328       {
1329         if (null_value)
1330           return 0;
1331         ptset.insert(*i);
1332       }
1333     }
1334 
1335     if (ptset.empty() == false)
1336     {
1337       std::copy(ptset.begin(), ptset.end(), std::back_inserter(*mpts));
1338       null_value= m_ifso->assign_result(mpts, result);
1339       retgeo= mpts;
1340       guard.release();
1341     }
1342     else
1343     {
1344       if (!null_value)
1345       {
1346         retgeo= m_ifso->empty_result(result, g1->get_srid());
1347         copy_ifso_state();
1348       }
1349     }
1350     return retgeo;
1351   }
1352 
1353 
linestring_difference_linestring(Geometry * g1,Geometry * g2,String * result)1354   Geometry *linestring_difference_linestring(Geometry *g1, Geometry *g2,
1355                                              String *result)
1356   {
1357     Geometry *retgeo= NULL;
1358     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1359                    g1->get_flags(), g1->get_srid());
1360     Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
1361                    g2->get_flags(), g2->get_srid());
1362     auto_ptr<Multilinestring> res(new Multilinestring());
1363     res->set_srid(g1->get_srid());
1364 
1365     boost::geometry::difference(ls1, ls2, *res);
1366 
1367     // The call to ls->set_ptr() below assumes that result->length()
1368     // is the length of the LineString, which is only true if result
1369     // is empty to begin with.
1370     assert(result->length() == 0);
1371 
1372     if (res->size() == 0)
1373     {
1374       post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1375       retgeo= m_ifso->empty_result(result, g1->get_srid());
1376     }
1377     else if (res->size() == 1)
1378     {
1379       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1380       {
1381         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1382         null_value= TRUE;
1383         return NULL;
1384       }
1385       Linestring *ls= new Linestring();
1386       res->begin()->as_geometry(result, false);
1387       ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1388                   result->length() - GEOM_HEADER_SIZE);
1389       ls->set_ownmem(false);
1390       retgeo= ls;
1391     }
1392     else
1393     {
1394       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1395       {
1396         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1397         null_value= TRUE;
1398         return NULL;
1399       }
1400       retgeo= res.release();
1401     }
1402 
1403     return retgeo;
1404   }
1405 
1406 
linestring_difference_polygon(Geometry * g1,Geometry * g2,String * result)1407   Geometry *linestring_difference_polygon(Geometry *g1, Geometry *g2,
1408                                           String *result)
1409   {
1410     Geometry *retgeo= NULL;
1411 
1412     BGOPCALL(Multilinestring, retgeo, difference,
1413              Linestring, g1, Polygon, g2, result, null_value);
1414 
1415     if (!retgeo && !null_value)
1416     {
1417       retgeo= m_ifso->empty_result(result, g1->get_srid());
1418       copy_ifso_state();
1419     }
1420 
1421     return retgeo;
1422   }
1423 
1424 
linestring_difference_multilinestring(Geometry * g1,Geometry * g2,String * result)1425   Geometry *linestring_difference_multilinestring(Geometry *g1, Geometry *g2,
1426                                                   String *result)
1427   {
1428     Geometry *retgeo= NULL;
1429     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1430                    g1->get_flags(), g1->get_srid());
1431     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1432                          g2->get_flags(), g2->get_srid());
1433     auto_ptr<Multilinestring> res(new Multilinestring());
1434     res->set_srid(g1->get_srid());
1435 
1436     boost::geometry::difference(ls1, mls2, *res);
1437 
1438     // The call to ls->set_ptr() below assumes that result->length()
1439     // is the length of the LineString, which is only true if result
1440     // is empty to begin with.
1441     assert(result->length() == 0);
1442 
1443     if (res->size() == 0)
1444     {
1445       post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1446       retgeo= m_ifso->empty_result(result, g1->get_srid());
1447     }
1448     else if (res->size() == 1)
1449     {
1450       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1451       {
1452         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1453         null_value= TRUE;
1454         return NULL;
1455       }
1456       Linestring *ls= new Linestring();
1457       res->begin()->as_geometry(result, false);
1458       ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1459                   result->length() - GEOM_HEADER_SIZE);
1460       ls->set_ownmem(false);
1461       retgeo= ls;
1462     }
1463     else
1464     {
1465       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1466       {
1467         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1468         null_value= TRUE;
1469         return NULL;
1470       }
1471       retgeo= res.release();
1472     }
1473 
1474     return retgeo;
1475   }
1476 
1477 
linestring_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1478   Geometry *linestring_difference_multipolygon(Geometry *g1, Geometry *g2,
1479                                                String *result)
1480   {
1481     Geometry *retgeo= NULL;
1482 
1483     BGOPCALL(Multilinestring, retgeo, difference,
1484              Linestring, g1, Multipolygon, g2, result, null_value);
1485 
1486     if (!retgeo && !null_value)
1487     {
1488       retgeo= m_ifso->empty_result(result, g1->get_srid());
1489       copy_ifso_state();
1490     }
1491     return retgeo;
1492   }
1493 
1494 
polygon_difference_polygon(Geometry * g1,Geometry * g2,String * result)1495   Geometry *polygon_difference_polygon(Geometry *g1, Geometry *g2,
1496                                        String *result)
1497   {
1498     Geometry *retgeo= NULL;
1499 
1500     BGOPCALL(Multipolygon, retgeo, difference,
1501              Polygon, g1, Polygon, g2, result, null_value);
1502 
1503     if (!retgeo && !null_value)
1504     {
1505       retgeo= m_ifso->empty_result(result, g1->get_srid());
1506       copy_ifso_state();
1507     }
1508     return retgeo;
1509   }
1510 
1511 
polygon_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1512   Geometry *polygon_difference_multipolygon(Geometry *g1, Geometry *g2,
1513                                             String *result)
1514   {
1515     Geometry *retgeo= NULL;
1516 
1517     BGOPCALL(Multipolygon, retgeo, difference,
1518              Polygon, g1, Multipolygon, g2, result, null_value);
1519 
1520     if (!retgeo && !null_value)
1521     {
1522       retgeo= m_ifso->empty_result(result, g1->get_srid());
1523       copy_ifso_state();
1524     }
1525     return retgeo;
1526   }
1527 
1528 
multilinestring_difference_linestring(Geometry * g1,Geometry * g2,String * result)1529   Geometry *multilinestring_difference_linestring(Geometry *g1, Geometry *g2,
1530                                                   String *result)
1531   {
1532     Geometry *retgeo= NULL;
1533     Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1534                          g1->get_flags(), g1->get_srid());
1535     Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
1536                    g2->get_flags(), g2->get_srid());
1537     auto_ptr<Multilinestring> res(new Multilinestring());
1538     res->set_srid(g1->get_srid());
1539 
1540     boost::geometry::difference(mls1, ls2, *res);
1541 
1542     // The call to ls->set_ptr() below assumes that result->length()
1543     // is the length of the LineString, which is only true if result
1544     // is empty to begin with.
1545     assert(result->length() == 0);
1546 
1547     if (res->size() == 0)
1548     {
1549       post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1550       retgeo= m_ifso->empty_result(result, g1->get_srid());
1551     }
1552     else if (res->size() == 1)
1553     {
1554       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1555       {
1556         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1557         null_value= TRUE;
1558         return NULL;
1559       }
1560       Linestring *ls= new Linestring();
1561       res->begin()->as_geometry(result, false);
1562       ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1563                   result->length() - GEOM_HEADER_SIZE);
1564       ls->set_ownmem(false);
1565       retgeo= ls;
1566     }
1567     else
1568     {
1569       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1570       {
1571         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1572         null_value= TRUE;
1573         return NULL;
1574       }
1575       retgeo= res.release();
1576     }
1577 
1578     return retgeo;
1579   }
1580 
1581 
multilinestring_difference_polygon(Geometry * g1,Geometry * g2,String * result)1582   Geometry *multilinestring_difference_polygon(Geometry *g1, Geometry *g2,
1583                                                String *result)
1584   {
1585     Geometry *retgeo= NULL;
1586 
1587     BGOPCALL(Multilinestring, retgeo, difference,
1588              Multilinestring, g1, Polygon, g2, result, null_value);
1589 
1590     if (!retgeo && !null_value)
1591     {
1592       retgeo= m_ifso->empty_result(result, g1->get_srid());
1593       copy_ifso_state();
1594     }
1595     return retgeo;
1596   }
1597 
1598 
multilinestring_difference_multilinestring(Geometry * g1,Geometry * g2,String * result)1599   Geometry *multilinestring_difference_multilinestring(Geometry *g1,
1600                                                        Geometry *g2,
1601                                                        String *result)
1602   {
1603     Geometry *retgeo= NULL;
1604     Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1605                          g1->get_flags(), g1->get_srid());
1606     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1607                          g2->get_flags(), g2->get_srid());
1608     auto_ptr<Multilinestring> res(new Multilinestring());
1609     res->set_srid(g1->get_srid());
1610 
1611     boost::geometry::difference(mls1, mls2, *res);
1612 
1613     // The call to ls->set_ptr() below assumes that result->length()
1614     // is the length of the LineString, which is only true if result
1615     // is empty to begin with.
1616     assert(result->length() == 0);
1617 
1618     if (res->size() == 0)
1619     {
1620       post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1621       retgeo= m_ifso->empty_result(result, g1->get_srid());
1622     }
1623     else if (res->size() == 1)
1624     {
1625       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1626       {
1627         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1628         null_value= TRUE;
1629         return NULL;
1630       }
1631       Linestring *ls= new Linestring();
1632       res->begin()->as_geometry(result, false);
1633       ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1634                   result->length() - GEOM_HEADER_SIZE);
1635       ls->set_ownmem(false);
1636       retgeo= ls;
1637     }
1638     else
1639     {
1640       if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1641       {
1642         my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1643         null_value= TRUE;
1644         return NULL;
1645       }
1646       retgeo= res.release();
1647     }
1648 
1649     return retgeo;
1650   }
1651 
1652 
multilinestring_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1653   Geometry *multilinestring_difference_multipolygon(Geometry *g1, Geometry *g2,
1654                                                     String *result)
1655   {
1656     Geometry *retgeo= NULL;
1657 
1658     BGOPCALL(Multilinestring, retgeo, difference,
1659              Multilinestring, g1, Multipolygon, g2, result, null_value);
1660 
1661     if (!retgeo && !null_value)
1662     {
1663       retgeo= m_ifso->empty_result(result, g1->get_srid());
1664       copy_ifso_state();
1665     }
1666     return retgeo;
1667   }
1668 
1669 
multipolygon_difference_polygon(Geometry * g1,Geometry * g2,String * result)1670   Geometry *multipolygon_difference_polygon(Geometry *g1, Geometry *g2,
1671                                             String *result)
1672   {
1673     Geometry *retgeo= NULL;
1674 
1675     BGOPCALL(Multipolygon, retgeo, difference,
1676              Multipolygon, g1, Polygon, g2, result, null_value);
1677 
1678     if (!retgeo && !null_value)
1679     {
1680       retgeo= m_ifso->empty_result(result, g1->get_srid());
1681       copy_ifso_state();
1682     }
1683     return retgeo;
1684   }
1685 
1686 
multipolygon_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1687   Geometry *multipolygon_difference_multipolygon(Geometry *g1, Geometry *g2,
1688                                                  String *result)
1689   {
1690     Geometry *retgeo= NULL;
1691 
1692     BGOPCALL(Multipolygon, retgeo, difference,
1693              Multipolygon, g1, Multipolygon, g2, result, null_value);
1694 
1695     if (!retgeo && !null_value)
1696     {
1697       retgeo= m_ifso->empty_result(result, g1->get_srid());
1698       copy_ifso_state();
1699     }
1700     return retgeo;
1701   }
1702 
1703 
linestring_symdifference_linestring(Geometry * g1,Geometry * g2,String * result)1704   Geometry *linestring_symdifference_linestring(Geometry *g1, Geometry *g2,
1705                                                 String *result)
1706   {
1707     Geometry *retgeo= NULL;
1708     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1709                    g1->get_flags(), g1->get_srid());
1710     Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
1711                    g2->get_flags(), g2->get_srid());
1712     auto_ptr<Multilinestring> res(new Multilinestring());
1713     res->set_srid(g1->get_srid());
1714 
1715     boost::geometry::sym_difference(ls1, ls2, *res);
1716     assert(res.get() != 0);
1717     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1718         res->size() > 0)
1719     {
1720       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1721       null_value= TRUE;
1722       return NULL;
1723     }
1724 
1725     if (res->size() == 0)
1726     {
1727       retgeo= m_ifso->empty_result(result, g1->get_srid());
1728     }
1729     else
1730     {
1731       retgeo= res.release();
1732     }
1733 
1734     return retgeo;
1735   }
1736 
1737 
linestring_symdifference_multilinestring(Geometry * g1,Geometry * g2,String * result)1738   Geometry *linestring_symdifference_multilinestring(Geometry *g1, Geometry *g2,
1739                                                      String *result)
1740   {
1741     Geometry *retgeo= NULL;
1742     Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1743                    g1->get_flags(), g1->get_srid());
1744     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1745                          g2->get_flags(), g2->get_srid());
1746     auto_ptr<Multilinestring> res(new Multilinestring());
1747     res->set_srid(g1->get_srid());
1748 
1749     boost::geometry::sym_difference(ls1, mls2, *res);
1750     assert(res.get() != 0);
1751     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1752         res->size() > 0)
1753     {
1754       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1755       null_value= TRUE;
1756       return NULL;
1757     }
1758 
1759     if (res->size() == 0)
1760     {
1761       retgeo= m_ifso->empty_result(result, g1->get_srid());
1762     }
1763     else
1764     {
1765       retgeo= res.release();
1766     }
1767 
1768     return retgeo;
1769   }
1770 
1771 
polygon_symdifference_polygon(Geometry * g1,Geometry * g2,String * result)1772   Geometry *polygon_symdifference_polygon(Geometry *g1, Geometry *g2,
1773                                           String *result)
1774   {
1775     Geometry *retgeo= NULL;
1776 
1777     BGOPCALL(Multipolygon, retgeo, sym_difference,
1778              Polygon, g1, Polygon, g2, result, null_value);
1779 
1780     if (!retgeo && !null_value)
1781     {
1782       retgeo= m_ifso->empty_result(result, g1->get_srid());
1783       copy_ifso_state();
1784     }
1785     return retgeo;
1786   }
1787 
1788 
polygon_symdifference_multipolygon(Geometry * g1,Geometry * g2,String * result)1789   Geometry *polygon_symdifference_multipolygon(Geometry *g1, Geometry *g2,
1790                                                String *result)
1791   {
1792     Geometry *retgeo= NULL;
1793 
1794     BGOPCALL(Multipolygon, retgeo, sym_difference,
1795              Polygon, g1, Multipolygon, g2, result, null_value);
1796 
1797     if (!retgeo && !null_value)
1798     {
1799       retgeo= m_ifso->empty_result(result, g1->get_srid());
1800       copy_ifso_state();
1801     }
1802     return retgeo;
1803   }
1804 
1805 
multilinestring_symdifference_multilinestring(Geometry * g1,Geometry * g2,String * result)1806   Geometry *multilinestring_symdifference_multilinestring(Geometry *g1,
1807                                                           Geometry *g2,
1808                                                           String *result)
1809   {
1810     Geometry *retgeo= NULL;
1811     Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1812                          g1->get_flags(), g1->get_srid());
1813     Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1814                          g2->get_flags(), g2->get_srid());
1815     auto_ptr<Multilinestring> res(new Multilinestring());
1816     res->set_srid(g1->get_srid());
1817 
1818     boost::geometry::sym_difference(mls1, mls2, *res);
1819     assert(res.get() != 0);
1820     if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1821         res->size() > 0)
1822     {
1823       my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1824       null_value= TRUE;
1825       return NULL;
1826     }
1827 
1828     if (res->size() == 0)
1829     {
1830       retgeo= m_ifso->empty_result(result, g1->get_srid());
1831     }
1832     else
1833     {
1834       retgeo= res.release();
1835     }
1836 
1837     return retgeo;
1838   }
1839 
1840 
multipolygon_symdifference_polygon(Geometry * g1,Geometry * g2,String * result)1841   Geometry *multipolygon_symdifference_polygon(Geometry *g1, Geometry *g2,
1842                                                String *result)
1843   {
1844     Geometry *retgeo= NULL;
1845 
1846     BGOPCALL(Multipolygon, retgeo, sym_difference,
1847              Multipolygon, g1, Polygon, g2, result, null_value);
1848 
1849     if (!retgeo && !null_value)
1850     {
1851       retgeo= m_ifso->empty_result(result, g1->get_srid());
1852       copy_ifso_state();
1853     }
1854     return retgeo;
1855   }
1856 
1857 
multipolygon_symdifference_multipolygon(Geometry * g1,Geometry * g2,String * result)1858   Geometry *multipolygon_symdifference_multipolygon(Geometry *g1, Geometry *g2,
1859                                                     String *result)
1860   {
1861     Geometry *retgeo= NULL;
1862 
1863     BGOPCALL(Multipolygon, retgeo, sym_difference,
1864              Multipolygon, g1, Multipolygon, g2, result, null_value);
1865 
1866     if (!retgeo && !null_value)
1867     {
1868       retgeo= m_ifso->empty_result(result, g1->get_srid());
1869       copy_ifso_state();
1870     }
1871     return retgeo;
1872   }
1873 };
1874 
1875 
1876 /**
1877   Do intersection operation for two geometries, dispatch to specific BG
1878   function wrapper calls according to set operation type, and the 1st or
1879   both operand types.
1880 
1881   @tparam Geom_types A wrapper for all geometry types.
1882   @param g1 First Geometry operand, not a geometry collection.
1883   @param g2 Second Geometry operand, not a geometry collection.
1884   @param[out] result Holds WKB data of the result.
1885   @return The result geometry whose WKB data is held in result.
1886  */
1887 template <typename Geom_types>
1888 Geometry *Item_func_spatial_operation::
intersection_operation(Geometry * g1,Geometry * g2,String * result)1889 intersection_operation(Geometry *g1, Geometry *g2,
1890                        String *result)
1891 {
1892   typedef typename Geom_types::Coordinate_type Coord_type;
1893   typedef typename Geom_types::Coordinate_system Coordsys;
1894 
1895   BG_setop_wrapper<Geom_types> wrap(this);
1896   Geometry *retgeo= NULL;
1897   Geometry::wkbType gt1= g1->get_type();
1898   Geometry::wkbType gt2= g2->get_type();
1899 
1900   switch (gt1)
1901   {
1902   case Geometry::wkb_point:
1903     switch (gt2)
1904     {
1905     case Geometry::wkb_point:
1906       retgeo= wrap.point_intersection_point(g1, g2, result);
1907       break;
1908     case Geometry::wkb_multipoint:
1909       retgeo= wrap.point_intersection_multipoint(g1, g2, result);
1910       break;
1911     case Geometry::wkb_linestring:
1912     case Geometry::wkb_polygon:
1913     case Geometry::wkb_multilinestring:
1914     case Geometry::wkb_multipolygon:
1915       retgeo= wrap.point_intersection_geometry(g1, g2, result);
1916       break;
1917     default:
1918       break;
1919     }
1920 
1921     break;
1922   case Geometry::wkb_multipoint:
1923     switch (gt2)
1924     {
1925     case Geometry::wkb_point:
1926       retgeo= wrap.point_intersection_multipoint(g2, g1, result);
1927       break;
1928 
1929     case Geometry::wkb_multipoint:
1930       retgeo= wrap.multipoint_intersection_multipoint(g1, g2, result);
1931       break;
1932     case Geometry::wkb_linestring:
1933     case Geometry::wkb_polygon:
1934     case Geometry::wkb_multilinestring:
1935     case Geometry::wkb_multipolygon:
1936       retgeo= wrap.multipoint_intersection_geometry(g1, g2, result);
1937       break;
1938     default:
1939       break;
1940     }
1941     break;
1942   case Geometry::wkb_linestring:
1943     switch (gt2)
1944     {
1945     case Geometry::wkb_point:
1946     case Geometry::wkb_multipoint:
1947       retgeo= intersection_operation<Geom_types>(g2, g1, result);
1948       break;
1949     case Geometry::wkb_linestring:
1950       retgeo= wrap.linestring_intersection_linestring(g1, g2, result);
1951       break;
1952     case Geometry::wkb_multilinestring:
1953       retgeo= wrap.linestring_intersection_multilinestring(g1, g2, result);
1954       break;
1955     case Geometry::wkb_polygon:
1956     case Geometry::wkb_multipolygon:
1957       retgeo= wrap.linestring_intersection_polygon(g1, g2, result);
1958       break;
1959     default:
1960       break;
1961     }
1962     break;
1963   case Geometry::wkb_polygon:
1964     switch (gt2)
1965     {
1966     case Geometry::wkb_point:
1967     case Geometry::wkb_multipoint:
1968     case Geometry::wkb_linestring:
1969       retgeo= intersection_operation<Geom_types>(g2, g1, result);
1970       break;
1971     case Geometry::wkb_multilinestring:
1972       retgeo= wrap.polygon_intersection_multilinestring(g1, g2, result);
1973       break;
1974     case Geometry::wkb_polygon:
1975     case Geometry::wkb_multipolygon:
1976       // Note: for now BG's set operations don't allow returning a
1977       // Multilinestring, thus this result isn't complete.
1978       retgeo= wrap.polygon_intersection_polygon(g1, g2, result);
1979       break;
1980     default:
1981       break;
1982     }
1983     break;
1984   case Geometry::wkb_multilinestring:
1985     switch (gt2)
1986     {
1987     case Geometry::wkb_point:
1988     case Geometry::wkb_multipoint:
1989     case Geometry::wkb_linestring:
1990     case Geometry::wkb_polygon:
1991       retgeo= intersection_operation<Geom_types>(g2, g1, result);
1992       break;
1993     case Geometry::wkb_multilinestring:
1994       retgeo= wrap.multilinestring_intersection_multilinestring(g1, g2, result);
1995       break;
1996 
1997     case Geometry::wkb_multipolygon:
1998       retgeo= wrap.multilinestring_intersection_multipolygon(g1, g2, result);
1999       break;
2000     default:
2001       break;
2002     }
2003     break;
2004   case Geometry::wkb_multipolygon:
2005     switch (gt2)
2006     {
2007     case Geometry::wkb_point:
2008     case Geometry::wkb_multipoint:
2009     case Geometry::wkb_linestring:
2010     case Geometry::wkb_multilinestring:
2011     case Geometry::wkb_polygon:
2012       retgeo= intersection_operation<Geom_types>(g2, g1, result);
2013       break;
2014     case Geometry::wkb_multipolygon:
2015       retgeo= wrap.multipolygon_intersection_multipolygon(g1, g2, result);
2016       break;
2017     default:
2018       break;
2019     }
2020     break;
2021   default:
2022     break;
2023   }
2024 
2025   if (!null_value)
2026     null_value= wrap.get_null_value();
2027   return retgeo;
2028 }
2029 
2030 
2031 /**
2032   Do union operation for two geometries, dispatch to specific BG
2033   function wrapper calls according to set operation type, and the 1st or
2034   both operand types.
2035 
2036   @tparam Geom_types A wrapper for all geometry types.
2037   @param g1 First Geometry operand, not a geometry collection.
2038   @param g2 Second Geometry operand, not a geometry collection.
2039   @param[out] result Holds WKB data of the result.
2040   @return The result geometry whose WKB data is held in result.
2041  */
2042 template <typename Geom_types>
2043 Geometry *Item_func_spatial_operation::
union_operation(Geometry * g1,Geometry * g2,String * result)2044 union_operation(Geometry *g1, Geometry *g2, String *result)
2045 {
2046   typedef typename Geom_types::Coordinate_type Coord_type;
2047   typedef typename Geom_types::Coordinate_system Coordsys;
2048 
2049   BG_setop_wrapper<Geom_types> wrap(this);
2050   Geometry *retgeo= NULL;
2051   Geometry::wkbType gt1= g1->get_type();
2052   Geometry::wkbType gt2= g2->get_type();
2053 
2054   // Note that union can't produce empty point set unless given two empty
2055   // point set arguments.
2056   switch (gt1)
2057   {
2058   case Geometry::wkb_point:
2059     switch (gt2)
2060     {
2061     case Geometry::wkb_point:
2062     case Geometry::wkb_multipoint:
2063       retgeo= wrap.point_union_point(g1, g2, result);
2064       break;
2065     case Geometry::wkb_linestring:
2066     case Geometry::wkb_multilinestring:
2067     case Geometry::wkb_polygon:
2068     case Geometry::wkb_multipolygon:
2069       retgeo= wrap.point_union_geometry(g1, g2, result);
2070       break;
2071     default:
2072       break;
2073     }
2074     break;
2075   case Geometry::wkb_multipoint:
2076     switch (gt2)
2077     {
2078     case Geometry::wkb_point:
2079       retgeo= wrap.point_union_point(g2, g1, result);
2080       break;
2081     case Geometry::wkb_multipoint:
2082       retgeo= wrap.multipoint_union_multipoint(g1, g2, result);
2083       break;
2084     case Geometry::wkb_linestring:
2085     case Geometry::wkb_multilinestring:
2086     case Geometry::wkb_polygon:
2087     case Geometry::wkb_multipolygon:
2088       retgeo= wrap.multipoint_union_geometry(g1, g2, result);
2089       break;
2090     default:
2091       break;
2092     }
2093     break;
2094   case Geometry::wkb_linestring:
2095     switch (gt2)
2096     {
2097     case Geometry::wkb_point:
2098     case Geometry::wkb_multipoint:
2099       retgeo= union_operation<Geom_types>(g2, g1, result);
2100       break;
2101     case Geometry::wkb_linestring:
2102       retgeo= wrap.linestring_union_linestring(g1, g2, result);
2103       break;
2104     case Geometry::wkb_multilinestring:
2105       retgeo= wrap.linestring_union_multilinestring(g1, g2, result);
2106       break;
2107     case Geometry::wkb_polygon:
2108       retgeo= wrap.linestring_union_polygon(g1, g2, result);
2109       break;
2110     case Geometry::wkb_multipolygon:
2111       retgeo= wrap.linestring_union_multipolygon(g1, g2, result);
2112       break;
2113     default:
2114       break;
2115     }
2116     break;
2117   case Geometry::wkb_polygon:
2118     switch (gt2)
2119     {
2120     case Geometry::wkb_point:
2121     case Geometry::wkb_multipoint:
2122     case Geometry::wkb_linestring:
2123       retgeo= union_operation<Geom_types>(g2, g1, result);
2124       break;
2125     case Geometry::wkb_multilinestring:
2126       retgeo= wrap.polygon_union_multilinestring(g1, g2, result);
2127       break;
2128     case Geometry::wkb_polygon:
2129       retgeo= wrap.polygon_union_polygon(g1, g2, result);
2130       break;
2131     case Geometry::wkb_multipolygon:
2132       retgeo= wrap.polygon_union_multipolygon(g1, g2, result);
2133       break;
2134     default:
2135       break;
2136     }
2137     break;
2138   case Geometry::wkb_multilinestring:
2139     switch (gt2)
2140     {
2141     case Geometry::wkb_point:
2142     case Geometry::wkb_multipoint:
2143     case Geometry::wkb_linestring:
2144     case Geometry::wkb_polygon:
2145       retgeo= union_operation<Geom_types>(g2, g1, result);
2146       break;
2147       break;
2148     case Geometry::wkb_multilinestring:
2149       retgeo= wrap.multilinestring_union_multilinestring(g1, g2, result);
2150       break;
2151     case Geometry::wkb_multipolygon:
2152       retgeo= wrap.multilinestring_union_multipolygon(g1, g2, result);
2153       break;
2154     default:
2155       break;
2156     }
2157     break;
2158   case Geometry::wkb_multipolygon:
2159     switch (gt2)
2160     {
2161     case Geometry::wkb_point:
2162     case Geometry::wkb_multipoint:
2163     case Geometry::wkb_linestring:
2164     case Geometry::wkb_polygon:
2165     case Geometry::wkb_multilinestring:
2166       retgeo= union_operation<Geom_types>(g2, g1, result);
2167       break;
2168     case Geometry::wkb_multipolygon:
2169       retgeo= wrap.multipolygon_union_multipolygon(g1, g2, result);
2170       break;
2171     default:
2172       break;
2173     }
2174     break;
2175   default:
2176     break;
2177   }
2178 
2179   if (!null_value)
2180     null_value= wrap.get_null_value() && maybe_null;
2181   if (!null_value && retgeo == NULL)
2182   {
2183     my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2184     error_str();
2185     return NULL;
2186   }
2187   return retgeo;
2188 }
2189 
2190 
2191 /**
2192   Do difference operation for two geometries, dispatch to specific BG
2193   function wrapper calls according to set operation type, and the 1st or
2194   both operand types.
2195 
2196   @tparam Geom_types A wrapper for all geometry types.
2197   @param g1 First Geometry operand, not a geometry collection.
2198   @param g2 Second Geometry operand, not a geometry collection.
2199   @param[out] result Holds WKB data of the result.
2200   @return The result geometry whose WKB data is held in result.
2201  */
2202 template <typename Geom_types>
2203 Geometry *Item_func_spatial_operation::
difference_operation(Geometry * g1,Geometry * g2,String * result)2204 difference_operation(Geometry *g1, Geometry *g2, String *result)
2205 {
2206   BG_setop_wrapper<Geom_types> wrap(this);
2207   Geometry *retgeo= NULL;
2208   Geometry::wkbType gt1= g1->get_type();
2209   Geometry::wkbType gt2= g2->get_type();
2210 
2211   /*
2212     Given two geometries g1 and g2, where g1.dimension < g2.dimension, then
2213     g2 - g1 is equal to g2, this is always true. This is how postgis works.
2214     Below implementation uses this fact.
2215    */
2216   switch (gt1)
2217   {
2218   case Geometry::wkb_point:
2219     switch (gt2)
2220     {
2221     case Geometry::wkb_point:
2222     case Geometry::wkb_multipoint:
2223     case Geometry::wkb_linestring:
2224     case Geometry::wkb_polygon:
2225     case Geometry::wkb_multilinestring:
2226     case Geometry::wkb_multipolygon:
2227       retgeo= wrap.point_difference_geometry(g1, g2, result);
2228       break;
2229     default:
2230       break;
2231     }
2232     break;
2233   case Geometry::wkb_multipoint:
2234     switch (gt2)
2235     {
2236     case Geometry::wkb_point:
2237     case Geometry::wkb_multipoint:
2238     case Geometry::wkb_linestring:
2239     case Geometry::wkb_polygon:
2240     case Geometry::wkb_multilinestring:
2241     case Geometry::wkb_multipolygon:
2242       retgeo= wrap.multipoint_difference_geometry(g1, g2, result);
2243       break;
2244     default:
2245       break;
2246     }
2247     break;
2248   case Geometry::wkb_linestring:
2249     switch (gt2)
2250     {
2251     case Geometry::wkb_point:
2252     case Geometry::wkb_multipoint:
2253       retgeo= g1;
2254       null_value= g1->as_geometry(result, true);
2255       break;
2256     case Geometry::wkb_linestring:
2257       retgeo= wrap.linestring_difference_linestring(g1, g2, result);
2258       break;
2259     case Geometry::wkb_polygon:
2260       retgeo= wrap.linestring_difference_polygon(g1, g2, result);
2261       break;
2262     case Geometry::wkb_multilinestring:
2263       retgeo= wrap.linestring_difference_multilinestring(g1, g2, result);
2264       break;
2265     case Geometry::wkb_multipolygon:
2266       retgeo= wrap.linestring_difference_multipolygon(g1, g2, result);
2267       break;
2268     default:
2269       break;
2270     }
2271     break;
2272   case Geometry::wkb_polygon:
2273     switch (gt2)
2274     {
2275     case Geometry::wkb_point:
2276     case Geometry::wkb_multipoint:
2277     case Geometry::wkb_linestring:
2278     case Geometry::wkb_multilinestring:
2279       retgeo= g1;
2280       null_value= g1->as_geometry(result, true);
2281       break;
2282     case Geometry::wkb_polygon:
2283       retgeo= wrap.polygon_difference_polygon(g1, g2, result);
2284       break;
2285     case Geometry::wkb_multipolygon:
2286       retgeo= wrap.polygon_difference_multipolygon(g1, g2, result);
2287       break;
2288     default:
2289       break;
2290     }
2291     break;
2292   case Geometry::wkb_multilinestring:
2293     switch (gt2)
2294     {
2295     case Geometry::wkb_point:
2296     case Geometry::wkb_multipoint:
2297       retgeo= g1;
2298       null_value= g1->as_geometry(result, true);
2299       break;
2300     case Geometry::wkb_linestring:
2301       retgeo= wrap.multilinestring_difference_linestring(g1, g2, result);
2302       break;
2303     case Geometry::wkb_polygon:
2304       retgeo= wrap.multilinestring_difference_polygon(g1, g2, result);
2305       break;
2306     case Geometry::wkb_multilinestring:
2307       retgeo= wrap.multilinestring_difference_multilinestring(g1, g2, result);
2308       break;
2309     case Geometry::wkb_multipolygon:
2310       retgeo= wrap.multilinestring_difference_multipolygon(g1, g2,
2311                                                            result);
2312       break;
2313     default:
2314       break;
2315     }
2316     break;
2317   case Geometry::wkb_multipolygon:
2318     switch (gt2)
2319     {
2320     case Geometry::wkb_point:
2321     case Geometry::wkb_multipoint:
2322     case Geometry::wkb_linestring:
2323     case Geometry::wkb_multilinestring:
2324       retgeo= g1;
2325       null_value= g1->as_geometry(result, true);
2326       break;
2327     case Geometry::wkb_polygon:
2328       retgeo= wrap.multipolygon_difference_polygon(g1, g2, result);
2329       break;
2330     case Geometry::wkb_multipolygon:
2331       retgeo= wrap.multipolygon_difference_multipolygon(g1, g2, result);
2332       break;
2333     default:
2334       break;
2335     }
2336     break;
2337   default:
2338     break;
2339   }
2340 
2341   if (!null_value)
2342     null_value= wrap.get_null_value();
2343   return retgeo;
2344 }
2345 
2346 
2347 /**
2348   Do symdifference operation for two geometries, dispatch to specific BG
2349   function wrapper calls according to set operation type, and the 1st or
2350   both operand types.
2351 
2352   @tparam Geom_types A wrapper for all geometry types.
2353   @param g1 First Geometry operand, not a geometry collection.
2354   @param g2 Second Geometry operand, not a geometry collection.
2355   @param[out] result Holds WKB data of the result.
2356   @return The result geometry whose WKB data is held in result.
2357  */
2358 template <typename Geom_types>
2359 Geometry *Item_func_spatial_operation::
symdifference_operation(Geometry * g1,Geometry * g2,String * result)2360 symdifference_operation(Geometry *g1, Geometry *g2, String *result)
2361 {
2362   typedef typename Geom_types::Coordinate_type Coord_type;
2363   typedef typename Geom_types::Coordinate_system Coordsys;
2364 
2365   BG_setop_wrapper<Geom_types> wrap(this);
2366   Geometry *retgeo= NULL;
2367   Geometry::wkbType gt1= g1->get_type();
2368   Geometry::wkbType gt2= g2->get_type();
2369 
2370   /*
2371     SymDifference(L, A) isn't supported by BG, but it's equivalent
2372     to
2373 
2374         GeometryCollection(Difference(A, L), Difference(L, A))
2375 
2376     Since geometries must be closed, Difference(A, L) is equivalent
2377     to A, so we can simplify to
2378 
2379         GeometryCollection(A, Difference(L, A))
2380 
2381     This is equivalent to Union(L, A), so we reuse that implementation.
2382    */
2383   bool do_geocol_setop= false;
2384 
2385   switch (gt1)
2386   {
2387   case Geometry::wkb_linestring:
2388     switch (gt2)
2389     {
2390     case Geometry::wkb_linestring:
2391       retgeo= wrap.linestring_symdifference_linestring(g1, g2, result);
2392       break;
2393     case Geometry::wkb_polygon:
2394       retgeo= wrap.linestring_union_polygon(g1, g2, result);
2395       break;
2396     case Geometry::wkb_multilinestring:
2397       retgeo= wrap.linestring_symdifference_multilinestring(g1, g2, result);
2398       break;
2399     case Geometry::wkb_multipolygon:
2400       retgeo= wrap.linestring_union_multipolygon(g1, g2, result);
2401       break;
2402     default:
2403       do_geocol_setop= true;
2404       break;
2405     }
2406     break;
2407   case Geometry::wkb_polygon:
2408 
2409     switch (gt2)
2410     {
2411     case Geometry::wkb_linestring:
2412       retgeo= wrap.linestring_union_polygon(g2, g1, result);
2413       break;
2414     case Geometry::wkb_polygon:
2415       retgeo= wrap.polygon_symdifference_polygon(g1, g2, result);
2416       break;
2417     case Geometry::wkb_multilinestring:
2418       retgeo= wrap.polygon_union_multilinestring(g1, g2, result);
2419       break;
2420     case Geometry::wkb_multipolygon:
2421       retgeo= wrap.polygon_symdifference_multipolygon(g1, g2, result);
2422       break;
2423     default:
2424       do_geocol_setop= true;
2425       break;
2426     }
2427     break;
2428   case Geometry::wkb_multilinestring:
2429     switch (gt2)
2430     {
2431     case Geometry::wkb_linestring:
2432       retgeo= wrap.linestring_symdifference_multilinestring(g2, g1, result);
2433       break;
2434     case Geometry::wkb_polygon:
2435       retgeo= wrap.polygon_union_multilinestring(g2, g1, result);
2436       break;
2437     case Geometry::wkb_multilinestring:
2438       retgeo= wrap.multilinestring_symdifference_multilinestring(g1, g2,
2439                                                                  result);
2440       break;
2441     case Geometry::wkb_multipolygon:
2442       retgeo= wrap.multilinestring_union_multipolygon(g1, g2, result);
2443       break;
2444     default:
2445       do_geocol_setop= true;
2446       break;
2447     }
2448     break;
2449   case Geometry::wkb_multipolygon:
2450     switch (gt2)
2451     {
2452     case Geometry::wkb_linestring:
2453       retgeo= wrap.linestring_union_multipolygon(g2, g1, result);
2454       break;
2455     case Geometry::wkb_polygon:
2456       retgeo= wrap.multipolygon_symdifference_polygon(g1, g2, result);
2457       break;
2458     case Geometry::wkb_multilinestring:
2459       retgeo= wrap.multilinestring_union_multipolygon(g2, g1, result);
2460       break;
2461     case Geometry::wkb_multipolygon:
2462       retgeo= wrap.multipolygon_symdifference_multipolygon(g1, g2, result);
2463       break;
2464     default:
2465       do_geocol_setop= true;
2466       break;
2467     }
2468     break;
2469   default:
2470     do_geocol_setop= true;
2471     break;
2472   }
2473 
2474   if (do_geocol_setop)
2475     retgeo= geometry_collection_set_operation<
2476       Coordsys>(g1, g2, result);
2477   else if (!null_value)
2478     null_value= wrap.get_null_value();
2479   return retgeo;
2480 }
2481 
2482 
2483 /**
2484   Call boost geometry set operations to compute set operation result, and
2485   returns the result as a Geometry object.
2486 
2487   @tparam Coordsys Coordinate system type, specified using those defined in
2488           boost::geometry::cs.
2489   @param g1 First Geometry operand, not a geometry collection.
2490   @param g2 Second Geometry operand, not a geometry collection.
2491   @param[out] result buffer containing the GEOMETRY byte string of
2492   the returned geometry.
2493 
2494   @return If the set operation results in an empty point set, return a
2495   geometry collection containing 0 objects. If null_value is set to
2496   true, always returns 0. The returned geometry object can be used in the same
2497   val_str call.
2498  */
2499 template<typename Coordsys>
2500 Geometry *Item_func_spatial_operation::
bg_geo_set_op(Geometry * g1,Geometry * g2,String * result)2501 bg_geo_set_op(Geometry *g1, Geometry *g2, String *result)
2502 {
2503   typedef BG_models<Coordsys> Geom_types;
2504 
2505   Geometry *retgeo= NULL;
2506 
2507   if (g1->get_coordsys() != g2->get_coordsys())
2508     return 0;
2509 
2510 
2511   switch (spatial_op)
2512   {
2513   case op_intersection:
2514     retgeo= intersection_operation<Geom_types>(g1, g2, result);
2515     break;
2516   case op_union:
2517     retgeo= union_operation<Geom_types>(g1, g2, result);
2518     break;
2519   case op_difference:
2520     retgeo= difference_operation<Geom_types>(g1, g2, result);
2521     break;
2522   case op_symdifference:
2523     retgeo= symdifference_operation<Geom_types>(g1, g2, result);
2524     break;
2525   default:
2526     // Other operations are not set operations.
2527     assert(false);
2528     break;
2529   }
2530 
2531   /*
2532     null_value is set in above xxx_operatoin calls if error occured.
2533   */
2534   if (null_value)
2535   {
2536     error_str();
2537     assert(retgeo == NULL);
2538   }
2539 
2540   // If we got effective result, the wkb encoding is written to 'result', and
2541   // the retgeo is effective geometry object whose data points into
2542   // 'result''s data.
2543   return retgeo;
2544 
2545 }
2546 
2547 
2548 /**
2549   Combine sub-results of set operation into a geometry collection.
2550   This function eliminates points in geo2 that are within
2551   geo1(polygons or linestrings). We have to do so
2552   because BG set operations return results in 3 forms --- multipolygon,
2553   multilinestring and multipoint, however given a type of set operation and
2554   the operands, the returned 3 types of results may intersect, and we want to
2555   eliminate the points already in the polygons/linestrings. And in future we
2556   also need to remove the linestrings that are already in the polygons, this
2557   isn't done now because there are no such set operation results to combine.
2558 
2559   @tparam Coordsys Coordinate system type, specified using those defined in
2560           boost::geometry::cs.
2561   @param geo1 First operand, a Multipolygon or Multilinestring object
2562               computed by BG set operation.
2563   @param geo2 Second operand, a Multipoint object
2564               computed by BG set operation.
2565   @param result Holds result geometry's WKB data in GEOMETRY format.
2566   @return A geometry combined from geo1 and geo2. Either or both of
2567   geo1 and geo2 can be NULL, so we may end up with a multipoint,
2568   a multipolygon/multilinestring, a geometry collection, or an
2569   empty geometry collection.
2570  */
2571 template<typename Coordsys>
2572 Geometry *Item_func_spatial_operation::
combine_sub_results(Geometry * geo1,Geometry * geo2,String * result)2573 combine_sub_results(Geometry *geo1, Geometry *geo2, String *result)
2574 {
2575   typedef BG_models<Coordsys> Geom_types;
2576   typedef typename Geom_types::Multipoint Multipoint;
2577   Geometry *retgeo= NULL;
2578   bool isin= false, added= false;
2579 
2580   if (null_value)
2581   {
2582     delete geo1;
2583     delete geo2;
2584     return NULL;
2585   }
2586 
2587   auto_ptr<Geometry> guard1(geo1), guard2(geo2);
2588 
2589   Gis_geometry_collection *geocol= NULL;
2590   if (geo1 == NULL && geo2 == NULL)
2591     retgeo= empty_result(result, Geometry::default_srid);
2592   else if (geo1 != NULL && geo2 == NULL)
2593   {
2594     retgeo= geo1;
2595     null_value= assign_result(geo1, result);
2596     guard1.release();
2597   }
2598   else if (geo1 == NULL && geo2 != NULL)
2599   {
2600     retgeo= geo2;
2601     null_value= assign_result(geo2, result);
2602     guard2.release();
2603   }
2604 
2605   if (geo1 == NULL || geo2 == NULL)
2606   {
2607     if (null_value)
2608       retgeo= NULL;
2609     return retgeo;
2610   }
2611 
2612   assert((geo1->get_type() == Geometry::wkb_multilinestring ||
2613           geo1->get_type() == Geometry::wkb_multipolygon) &&
2614          geo2->get_type() == Geometry::wkb_multipoint);
2615   Multipoint mpts(geo2->get_data_ptr(), geo2->get_data_size(),
2616                   geo2->get_flags(), geo2->get_srid());
2617   geocol= new Gis_geometry_collection(geo1, result);
2618   geocol->set_components_no_overlapped(geo1->is_components_no_overlapped());
2619   auto_ptr<Gis_geometry_collection> guard3(geocol);
2620   my_bool had_error= false;
2621 
2622   for (TYPENAME Multipoint::iterator i= mpts.begin();
2623        i != mpts.end(); ++i)
2624   {
2625     isin= !Item_func_spatial_rel::bg_geo_relation_check<
2626       Coordsys>(&(*i), geo1, SP_DISJOINT_FUNC, &had_error);
2627 
2628     if (had_error)
2629     {
2630       error_str();
2631       return NULL;
2632     }
2633 
2634     if (!isin)
2635     {
2636       geocol->append_geometry(&(*i), result);
2637       added= true;
2638     }
2639   }
2640 
2641   if (added)
2642   {
2643     retgeo= geocol;
2644     guard3.release();
2645   }
2646   else
2647   {
2648     retgeo= geo1;
2649     guard1.release();
2650     null_value= assign_result(geo1, result);
2651   }
2652 
2653   if (null_value)
2654     error_str();
2655 
2656   return retgeo;
2657 }
2658 
2659 
2660 /**
2661   Simplify a multilinestring result from the BG intersection function.
2662 
2663   The multilinestring may contain linestrings with two points where
2664   both points are the same. Those are intersection points that should
2665   be returned to the user as points. This function loops through the
2666   multilinestring, separating true linestrings and linestring-encoded
2667   points, and returns the simplest geometric result possible: point,
2668   linestring, multilinestring, multipoint, or geometry collection.
2669 
2670   @param mls The multilinestring to simplify
2671   @param[out] result The GEOMETRY string (SRID+WKB) of the returned object
2672   @return The simplest geometry type representing the input.
2673 */
2674 Geometry *Item_func_spatial_operation::
simplify_multilinestring(Gis_multi_line_string * mls,String * result)2675 simplify_multilinestring(Gis_multi_line_string *mls, String *result)
2676 {
2677   // Null values are handled by caller.
2678   assert(mls != NULL);
2679   Geometry *retgeo;
2680 
2681   // Loop through the multilinestring and separate true linestrings
2682   // from points.
2683   auto_ptr<Gis_multi_line_string> linestrings(new Gis_multi_line_string());
2684   auto_ptr<Gis_multi_point> points(new Gis_multi_point());
2685   linestrings->set_srid(mls->get_srid());
2686   points->set_srid(mls->get_srid());
2687   // BG may return duplicate points, so use a point set to get unique
2688   // points before storing them into 'points'.
2689   typedef std::set<Gis_point, bgpt_lt> Point_set;
2690   Point_set point_set(points->begin(), points->end());
2691   Gis_point prev_point;
2692   bool has_prev_point= false;
2693 
2694   for (Gis_multi_line_string::iterator i= mls->begin();
2695        i != mls->end();
2696        ++i)
2697   {
2698     if (i->size() != 2)
2699     {
2700       assert(i->size() > 2);
2701       linestrings->push_back(*i);
2702       continue;
2703     }
2704 
2705     const Gis_point &start= (*i)[0];
2706     const Gis_point &end= (*i)[1];
2707     if (start == end)
2708     {
2709       if (!has_prev_point || prev_point != start)
2710       {
2711         has_prev_point= true;
2712         prev_point= start;
2713         point_set.insert(start);
2714       }
2715     }
2716     else
2717       linestrings->push_back(*i);
2718   }
2719 
2720   for (Point_set::iterator i= point_set.begin();
2721        i != point_set.end(); ++i)
2722   {
2723     /*
2724       When computing [m]ls intersection [m]ls, BG may return points that are
2725       on the linestrings, so here we have to exclude such points.
2726     */
2727     if (bg::disjoint(*i, *linestrings))
2728       points->push_back(*i);
2729   }
2730 
2731   String dummy;
2732   post_fix_result(&bg_resbuf_mgr, *linestrings, &dummy);
2733   post_fix_result(&bg_resbuf_mgr, *points, &dummy);
2734 
2735   // Return the simplest type possible
2736   if (points->size() == 0 && linestrings->size() == 1)
2737   {
2738     // Linestring result
2739     Gis_line_string *linestringresult= new Gis_line_string();
2740     size_t oldlength= result->length();
2741     linestrings->begin()->as_geometry(result, false);
2742     size_t newlength= result->length();
2743     linestringresult->set_ptr(const_cast<char*>(result->ptr()) + oldlength +
2744                               GEOM_HEADER_SIZE,
2745                               (newlength - oldlength) - GEOM_HEADER_SIZE);
2746     linestringresult->set_ownmem(false);
2747     retgeo= linestringresult;
2748   }
2749   else if (points->size() == 0 && linestrings->size() > 1)
2750   {
2751     // Multilinestring result
2752     linestrings->as_geometry(result, false);
2753     linestrings->set_ownmem(false);
2754     retgeo= linestrings.release();
2755   }
2756   else if (points->size() == 1 && linestrings->size() == 0)
2757   {
2758     // Point result
2759     Gis_point *pointresult= new Gis_point();
2760     size_t oldlength= result->length();
2761     points->begin()->as_geometry(result, false);
2762     size_t newlength= result->length();
2763     pointresult->set_ptr(const_cast<char*>(result->ptr()) + oldlength +
2764                          GEOM_HEADER_SIZE,
2765                          (newlength - oldlength) - GEOM_HEADER_SIZE);
2766     pointresult->set_ownmem(false);
2767     retgeo= pointresult;
2768   }
2769   else if (points->size() > 1 && linestrings->size() == 0)
2770   {
2771     // Multipoint result
2772     points->as_geometry(result, false);
2773     points->set_ownmem(false);
2774     retgeo= points.release();
2775   }
2776   else
2777   {
2778     // GeometryCollection result
2779     Gis_geometry_collection *collection= new Gis_geometry_collection();
2780 
2781     if (points->size() > 1)
2782     {
2783       collection->append_geometry(&(*points), result);
2784     }
2785     else
2786     {
2787       collection->append_geometry(&(*points)[0], result);
2788     }
2789 
2790     if (linestrings->size() > 1)
2791     {
2792       collection->append_geometry(&(*linestrings), result);
2793     }
2794     else
2795     {
2796       collection->append_geometry(&(*linestrings)[0], result);
2797     }
2798 
2799     collection->set_ownmem(false);
2800     retgeo= collection;
2801   }
2802 
2803   return retgeo;
2804 }
2805 
2806 
2807 /**
2808   Extract a basic geometry component from a multi geometry or a geometry
2809   collection, if it's the only one in it.
2810  */
2811 class Singleton_extractor : public WKB_scanner_event_handler
2812 {
2813   /*
2814     If we see the nested geometries as a forest, seeing the outmost one as the
2815     ground where the trees grow, and seeing each of its components
2816     as a tree, then the search for a singleton in a geometry collection(GC) or
2817     multi-geometry(i.e. multipoint, multilinestring, multipolygon) is identical
2818     to searching on the ground to see if there is only one tree on the ground,
2819     if so we also need to record its starting address within the root node's
2820     memory buffer.
2821 
2822     Some details complicate the problem:
2823     1. GCs can be nested into another GC, a nested GC should be see also as
2824        the 'ground' rather than a tree.
2825     2. A single multi-geometry contained in a GC may be a singleton or not.
2826        a. When it has only one component in itself, that component is
2827           the singleton;
2828        b. Otherwise itself is the singleton.
2829     3. Basic geometries are always atomic(undevidible).
2830     4. A multi-geometry can't be nested into another multigeometry, it can
2831        only be a component of a GC.
2832 
2833     Below comment for the data members are based on this context information.
2834   */
2835   // The number of trees on the ground.
2836   int ntrees;
2837   // The number of trees inside all multi-geometries.
2838   int nsubtrees;
2839   // Current tree travasal stack depth, i.e. tree height.
2840   int depth;
2841   // The depth of the multi-geometry, if any.
2842   int mg_depth;
2843   // The effective stack depth, i.e. excludes the nested GCs.
2844   int levels;
2845   // The stack depth of heighest GC in current ground.
2846   int gc_depth;
2847   // Starting and ending address of tree on ground.
2848   const char *start, *end;
2849   // Starting address of and type of the basic geometry which is on top of the
2850   // multi-geometry.
2851   const char *bg_start;
2852   Geometry::wkbType bg_type;
2853 
2854   // The type of the geometry on the ground.
2855   Geometry::wkbType gtype;
2856 public:
Singleton_extractor()2857   Singleton_extractor()
2858   {
2859     ntrees= nsubtrees= depth= mg_depth= levels= gc_depth= 0;
2860     bg_start= start= end= NULL;
2861     bg_type= gtype= Geometry::wkb_invalid_type;
2862   }
2863 
is_basic_type(const Geometry::wkbType t)2864   static bool is_basic_type(const Geometry::wkbType t)
2865   {
2866     return t == Geometry::wkb_point || t == Geometry::wkb_linestring ||
2867       t == Geometry::wkb_polygon;
2868   }
2869 
has_single_component() const2870   bool has_single_component() const
2871   {
2872     return ntrees == 1;
2873   }
2874 
2875   // Functions to get singleton information.
2876 
2877   /*
2878     Returns start of singleton. If only one sub-tree, the basic geometry
2879     is returned instead of the multi-geometry, otherwise the multi-geometry
2880     is returned.
2881    */
get_start() const2882   const char *get_start() const
2883   {
2884     return nsubtrees == 1 ? bg_start : start;
2885   }
2886 
2887   /*
2888     Returns the end of the singleton geometry. For a singleton,
2889     its end is always also the end of the root geometry, so this function
2890     is correct only when the root geometry really contains a singleton.
2891    */
get_end() const2892   const char *get_end() const
2893   {
2894     return end;
2895   }
2896 
get_type() const2897   Geometry::wkbType get_type() const
2898   {
2899     return nsubtrees == 1 ? bg_type : gtype;
2900   }
2901 
2902 
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void * wkb,uint32 len,bool has_hdr)2903   virtual void on_wkb_start(Geometry::wkbByteOrder bo,
2904                             Geometry::wkbType geotype,
2905                             const void *wkb, uint32 len, bool has_hdr)
2906   {
2907     if (geotype != Geometry::wkb_geometrycollection)
2908     {
2909       if (gc_depth == 0)
2910       {
2911         gc_depth= depth;
2912         start= static_cast<const char *>(wkb);
2913         end= start + len;
2914         gtype= geotype;
2915       }
2916 
2917       if (!is_basic_type(geotype))
2918         mg_depth= depth;
2919 
2920       if (mg_depth + 1 == depth)
2921       {
2922         bg_type= geotype;
2923         bg_start= static_cast<const char *>(wkb);
2924       }
2925 
2926       levels++;
2927     }
2928     else
2929       gc_depth= 0;
2930 
2931     depth++;
2932   }
2933 
2934 
on_wkb_end(const void * wkb)2935   virtual void on_wkb_end(const void *wkb)
2936   {
2937     depth--;
2938     assert(depth >= 0);
2939 
2940     if (levels > 0)
2941     {
2942       levels--;
2943       if (levels == 0)
2944       {
2945         assert(depth == gc_depth);
2946         ntrees++;
2947         end= static_cast<const char *>(wkb);
2948         mg_depth= 0;
2949         gc_depth= 0;
2950       }
2951     }
2952 
2953     // The subtree is either a multi-geometry or a basic geometry.
2954     if (mg_depth != 0 && levels == 1)
2955       nsubtrees++;
2956   }
2957 };
2958 
2959 
base_type(Geometry::wkbType gt)2960 inline Geometry::wkbType base_type(Geometry::wkbType gt)
2961 {
2962   Geometry::wkbType ret;
2963 
2964   switch (gt)
2965   {
2966   case Geometry::wkb_multipoint:
2967     ret= Geometry::wkb_point;
2968     break;
2969   case Geometry::wkb_multilinestring:
2970     ret= Geometry::wkb_linestring;
2971     break;
2972   case Geometry::wkb_multipolygon:
2973     ret= Geometry::wkb_polygon;
2974     break;
2975   default:
2976     ret= gt;
2977   }
2978   return ret;
2979 }
2980 
2981 
2982 /**
2983   Simplify multi-geometry data. If str contains a multi-geometry or geometry
2984   collection with one component, the component is made as content of str.
2985   If str contains a nested geometry collection, the effective concrete geometry
2986   object is returned.
2987   @param str A string buffer containing a GEOMETRY byte string.
2988   @param [out] result_buffer if not NULL and if a simplification is to be done,
2989                we will copy the GEOMETRY byte string from str into result_buffer
2990                and simplify the one stored in result_buffer, and the one in str
2991                is intact. If there is any data in result_buffer before calling
2992                this function, it is overwritten; If no simplification done, the
2993                result_buffer is intact if it is provided.
2994   @return whether the geometry is simplified or not.
2995  */
simplify_multi_geometry(String * str,String * result_buffer)2996 bool simplify_multi_geometry(String *str, String *result_buffer)
2997 {
2998   if (str->length() < GEOM_HEADER_SIZE)
2999     return false;
3000 
3001   char *p= const_cast<char *>(str->ptr());
3002   Geometry::wkbType gtype= get_wkb_geotype(p + 5);
3003   bool ret= false;
3004 
3005   if (gtype == Geometry::wkb_multipoint ||
3006       gtype == Geometry::wkb_multilinestring ||
3007       gtype == Geometry::wkb_multipolygon)
3008   {
3009     if (uint4korr(p + GEOM_HEADER_SIZE) == 1)
3010     {
3011       if (result_buffer)
3012       {
3013         result_buffer->length(0);
3014         result_buffer->append(*str);
3015         p= const_cast<char *>(result_buffer->ptr());
3016         str= result_buffer;
3017       }
3018       assert((str->length() - GEOM_HEADER_SIZE - 4 - WKB_HEADER_SIZE) > 0);
3019       int4store(p + 5, static_cast<uint32>(base_type(gtype)));
3020       memmove(p + GEOM_HEADER_SIZE, p + GEOM_HEADER_SIZE + 4 + WKB_HEADER_SIZE,
3021               str->length() - GEOM_HEADER_SIZE - 4 - WKB_HEADER_SIZE);
3022       str->length(str->length() - 4 - WKB_HEADER_SIZE);
3023       ret= true;
3024     }
3025   }
3026   else if (gtype == Geometry::wkb_geometrycollection)
3027   {
3028     Singleton_extractor ex;
3029     uint32 wkb_len= str->length() - GEOM_HEADER_SIZE;
3030     wkb_scanner(p + GEOM_HEADER_SIZE, &wkb_len,
3031                 Geometry::wkb_geometrycollection, false, &ex);
3032     if (ex.has_single_component())
3033     {
3034       if (result_buffer)
3035       {
3036         result_buffer->length(0);
3037         result_buffer->append(*str);
3038         p= const_cast<char *>(result_buffer->ptr());
3039         str= result_buffer;
3040       }
3041       p= write_wkb_header(p + 4, ex.get_type());
3042       ptrdiff_t len= ex.get_end() - ex.get_start();
3043       assert(len > 0);
3044       memmove(p, ex.get_start(), len);
3045       str->length(GEOM_HEADER_SIZE + len);
3046       ret= true;
3047     }
3048   }
3049 
3050   return ret;
3051 }
3052 
3053 
3054 /*
3055   Do set operations on geometries.
3056   Writes geometry set operation result into str_value_arg in wkb format.
3057  */
val_str(String * str_value_arg)3058 String *Item_func_spatial_operation::val_str(String *str_value_arg)
3059 {
3060   DBUG_ENTER("Item_func_spatial_operation::val_str");
3061   assert(fixed == 1);
3062 
3063   tmp_value1.length(0);
3064   tmp_value2.length(0);
3065   String *res1= args[0]->val_str(&tmp_value1);
3066   String *res2= args[1]->val_str(&tmp_value2);
3067   Geometry_buffer buffer1, buffer2;
3068   Geometry *g1= NULL, *g2= NULL, *gres= NULL;
3069   bool had_except1= false, had_except2= false;
3070   bool result_is_args= false;
3071 
3072   // Release last call's result buffer.
3073   bg_resbuf_mgr.free_result_buffer();
3074 
3075   // Clean up the result first, since caller may give us one with non-NULL
3076   // buffer, we don't need it here.
3077   str_value_arg->set(NullS, 0, &my_charset_bin);
3078 
3079   if ((null_value= (!res1 || args[0]->null_value ||
3080                     !res2 || args[1]->null_value)))
3081     goto exit;
3082   if (!(g1= Geometry::construct(&buffer1, res1)) ||
3083       !(g2= Geometry::construct(&buffer2, res2)))
3084   {
3085     my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3086     DBUG_RETURN(error_str());
3087   }
3088 
3089   // The two geometry operand must be in the same coordinate system.
3090   if (g1->get_srid() != g2->get_srid())
3091   {
3092     my_error(ER_GIS_DIFFERENT_SRIDS, MYF(0), func_name(),
3093              g1->get_srid(), g2->get_srid());
3094     DBUG_RETURN(error_str());
3095   }
3096 
3097   str_value_arg->set_charset(&my_charset_bin);
3098   str_value_arg->length(0);
3099 
3100 
3101   /*
3102     Catch all exceptions to make sure no exception can be thrown out of
3103     current function. Put all and any code that calls Boost.Geometry functions,
3104     STL functions into this try block. Code out of the try block should never
3105     throw any exception.
3106   */
3107   try
3108   {
3109     if (g1->get_type() != Geometry::wkb_geometrycollection &&
3110         g2->get_type() != Geometry::wkb_geometrycollection)
3111       gres= bg_geo_set_op<bgcs::cartesian>(g1, g2, str_value_arg);
3112     else
3113       gres= geometry_collection_set_operation<bgcs::cartesian>
3114         (g1, g2, str_value_arg);
3115 
3116   }
3117   catch (...)
3118   {
3119     had_except1= true;
3120     handle_gis_exception(func_name());
3121   }
3122 
3123   try
3124   {
3125     /*
3126       The buffers in res1 and res2 either belong to argument Item_xxx objects
3127       or simply belong to tmp_value1 or tmp_value2, they will be deleted
3128       properly by their owners, not by our bg_resbuf_mgr, so here we must
3129       forget them in order not to free the buffers before the Item_xxx
3130       owner nodes are destroyed.
3131     */
3132     bg_resbuf_mgr.forget_buffer(const_cast<char *>(res1->ptr()));
3133     bg_resbuf_mgr.forget_buffer(const_cast<char *>(res2->ptr()));
3134     bg_resbuf_mgr.forget_buffer(const_cast<char *>(tmp_value1.ptr()));
3135     bg_resbuf_mgr.forget_buffer(const_cast<char *>(tmp_value2.ptr()));
3136 
3137     /*
3138       Release intermediate geometry data buffers accumulated during execution
3139       of this set operation.
3140     */
3141     if (!str_value_arg->is_alloced() && gres != g1 && gres != g2)
3142       bg_resbuf_mgr.set_result_buffer(const_cast<char *>(str_value_arg->ptr()));
3143     bg_resbuf_mgr.free_intermediate_result_buffers();
3144   }
3145   catch (...)
3146   {
3147     had_except2= true;
3148     handle_gis_exception(func_name());
3149   }
3150 
3151   if (had_except1 || had_except2 || null_value)
3152   {
3153     if (gres != NULL && gres != g1 && gres != g2)
3154     {
3155       delete gres;
3156       gres= NULL;
3157     }
3158     DBUG_RETURN(error_str());
3159   }
3160 
3161   assert(gres != NULL && !null_value && str_value_arg->length() > 0);
3162 
3163   /*
3164     There are 4 ways to create the result geometry object and allocate
3165     memory for the result String object:
3166     1. Created in BGOPCALL and allocated by BG code using gis_wkb_alloc
3167        functions; The geometry result object's memory is took over by
3168        str_value_arg, thus not allocated by str_value_arg.
3169     2. Created as a new geometry object and allocated by
3170        str_value_arg's String member functions.
3171     3. One of g1 or g2 used as result and g1/g2's String object is used as
3172        final result without duplicating their byte strings. Also, g1 and/or
3173        g2 may be used as intermediate result and their byte strings are
3174        assigned to intermediate String objects without giving the ownerships
3175        to them, so they are always owned by tmp_value1 and/or tmp_value2.
3176     4. A geometry duplicated from a component of BG_geometry_collection.
3177        when both GCs have 1 member, we do set operation for the two members
3178        directly, and if such a component is the result we have to duplicate
3179        it and its WKB String buffer.
3180 
3181     Among above 4 ways, #1, #2 and #4 write the byte string only once without
3182     any data copying, #3 doesn't write any byte strings.
3183 
3184     And here we always have a GEOMETRY byte string in str_value_arg, although
3185     in some cases gres->has_geom_header_space() is false.
3186    */
3187   if (!str_value_arg->is_alloced() && gres != g1 && gres != g2)
3188   {
3189     assert(gres->has_geom_header_space() || gres->is_bg_adapter());
3190   }
3191   else
3192   {
3193     assert(gres->has_geom_header_space() || (gres == g1 || gres == g2));
3194     if (gres == g1)
3195     {
3196       str_value_arg= res1;
3197       result_is_args= true;
3198     }
3199     else if (gres == g2)
3200     {
3201       str_value_arg= res2;
3202       result_is_args= true;
3203     }
3204   }
3205 
3206   /*
3207     We can not modify the geometry argument because it is assumed to be stable.
3208     So if returning one of the arguments as result directly, make sure the
3209     simplification is done in a separate buffer.
3210   */
3211   if (simplify_multi_geometry(str_value_arg,
3212                               (result_is_args ? &m_result_buffer : NULL)) &&
3213       result_is_args)
3214     str_value_arg= &m_result_buffer;
3215 
3216 exit:
3217   if (gres != g1 && gres != g2 && gres != NULL)
3218     delete gres;
3219   DBUG_RETURN(null_value ? NULL : str_value_arg);
3220 }
3221 
3222 
is_areal(const Geometry * g)3223 inline bool is_areal(const Geometry *g)
3224 {
3225   return g != NULL && (g->get_type() == Geometry::wkb_polygon ||
3226                        g->get_type() == Geometry::wkb_multipolygon);
3227 }
3228 
3229 
3230 /**
3231   Do set operation on geometry collections.
3232   BG doesn't directly support geometry collections in any function, so we
3233   have to do so by computing the set operation result of all two operands'
3234   components, which must be the 6 basic types of geometries, and then we
3235   combine the sub-results.
3236 
3237   This function dispatches to specific set operation types.
3238 
3239   @tparam Coordsys Coordinate system type, specified using those defined in
3240           boost::geometry::cs.
3241   @param g1 First geometry operand, a geometry collection.
3242   @param g2 Second geometry operand, a geometry collection.
3243   @param[out] result Holds WKB data of the result, which must be a
3244                 geometry collection.
3245   @return The set operation result, whose WKB data is stored in 'result'.
3246  */
3247 template<typename Coordsys>
3248 Geometry *Item_func_spatial_operation::
geometry_collection_set_operation(Geometry * g1,Geometry * g2,String * result)3249 geometry_collection_set_operation(Geometry *g1, Geometry *g2,
3250                                   String *result)
3251 {
3252   Geometry *gres= NULL;
3253   BG_geometry_collection bggc1, bggc2;
3254 
3255   bggc1.set_srid(g1->get_srid());
3256   bggc2.set_srid(g2->get_srid());
3257   bool empty1= is_empty_geocollection(g1);
3258   bool empty2= is_empty_geocollection(g2);
3259 
3260   /* Short cut for either one operand being empty. */
3261   if (empty1 || empty2)
3262   {
3263     if (spatial_op == op_intersection ||
3264         (empty1 && empty2 && (spatial_op == op_symdifference ||
3265                               spatial_op == op_union)) ||
3266         (empty1 && spatial_op == op_difference))
3267     {
3268       return empty_result(result, g1->get_srid());
3269     }
3270 
3271     // If spatial_op is op_union or op_symdifference and one of g1/g2 is empty,
3272     // we will continue in order to merge components in the argument.
3273 
3274     if (empty2 && spatial_op == op_difference)
3275     {
3276       null_value= g1->as_geometry(result, true/* shallow copy */);
3277       return g1;
3278     }
3279   }
3280 
3281   bggc1.fill(g1);
3282   bggc2.fill(g2);
3283   if (spatial_op != op_union)
3284   {
3285     bggc1.merge_components<Coordsys>(&null_value);
3286     if (null_value)
3287       return gres;
3288     bggc2.merge_components<Coordsys>(&null_value);
3289     if (null_value)
3290       return gres;
3291   }
3292 
3293   BG_geometry_collection::Geometry_list &gv1= bggc1.get_geometries();
3294   BG_geometry_collection::Geometry_list &gv2= bggc2.get_geometries();
3295 
3296   /*
3297     If there is only one component in one argument and the other is empty,
3298     no merge is possible.
3299   */
3300   if (spatial_op == op_union ||
3301       spatial_op == op_symdifference)
3302   {
3303     if (gv1.size() == 0 && gv2.size() == 1)
3304     {
3305       null_value= g2->as_geometry(result, true/* shallow copy */);
3306       return g2;
3307     }
3308 
3309     if (gv1.size() == 1 && gv2.size() == 0)
3310     {
3311       null_value= g1->as_geometry(result, true/* shallow copy */);
3312       return g1;
3313     }
3314   }
3315 
3316   /*
3317     If both collections have only one basic component, do basic set operation.
3318     The exception is symdifference with at least one operand being not a
3319     polygon or multipolygon, in which case this exact function is called to
3320     perform symdifference for the two basic components.
3321    */
3322   if (gv1.size() == 1 && gv2.size() == 1 &&
3323       (spatial_op != op_symdifference ||
3324        (is_areal(*(gv1.begin())) && is_areal(*(gv2.begin())))))
3325   {
3326     gres= bg_geo_set_op<Coordsys>(*(gv1.begin()), *(gv2.begin()),
3327                                               result);
3328     if (null_value)
3329       return NULL;
3330     if (gres == NULL && !null_value)
3331     {
3332       gres= empty_result(result, g1->get_srid());
3333       return gres;
3334     }
3335 
3336     /*
3337       If this set operation gives us a gres that's a component/member of either
3338       bggc1 or bggc2, we have to duplicate the object and its buffer because
3339       they will be destroyed when bggc1/bggc2 goes out of scope.
3340      */
3341     bool do_dup= false;
3342     for (BG_geometry_collection::Geometry_list::iterator i= gv1.begin();
3343          i != gv1.end(); ++i)
3344       if (*i == gres)
3345         do_dup= true;
3346     if (!do_dup)
3347       for (BG_geometry_collection::Geometry_list::iterator i= gv2.begin();
3348            i != gv2.end(); ++i)
3349         if (*i == gres)
3350           do_dup= true;
3351 
3352     if (do_dup)
3353     {
3354       String tmpres;
3355       Geometry *gres2= NULL;
3356       tmpres.append(result->ptr(), result->length());
3357       const void *data_start= static_cast<const char *>(tmpres.ptr()) +
3358         GEOM_HEADER_SIZE;
3359 
3360       switch (gres->get_geotype())
3361       {
3362       case Geometry::wkb_point:
3363         gres2= new Gis_point;
3364         break;
3365       case Geometry::wkb_linestring:
3366         gres2= new Gis_line_string;
3367         break;
3368       case Geometry::wkb_polygon:
3369         gres2= new Gis_polygon;
3370         break;
3371       case Geometry::wkb_multipoint:
3372         gres2= new Gis_multi_point;
3373         break;
3374       case Geometry::wkb_multilinestring:
3375         gres2= new Gis_multi_line_string;
3376         break;
3377       case Geometry::wkb_multipolygon:
3378         gres2= new Gis_multi_polygon;
3379         break;
3380       default:
3381         assert(false);
3382       }
3383 
3384       gres2->set_data_ptr(data_start, tmpres.length() - GEOM_HEADER_SIZE);
3385       gres2->has_geom_header_space(true);
3386       gres2->set_bg_adapter(false);
3387       result->takeover(tmpres);
3388       gres= gres2;
3389     }
3390 
3391     return gres;
3392   }
3393 
3394 
3395   switch (this->spatial_op)
3396   {
3397   case op_intersection:
3398     gres= geocol_intersection<Coordsys>(bggc1, bggc2, result);
3399     break;
3400   case op_union:
3401     gres= geocol_union<Coordsys>(bggc1, bggc2, result);
3402     break;
3403   case op_difference:
3404     gres= geocol_difference<Coordsys>(bggc1, bggc2, result);
3405     break;
3406   case op_symdifference:
3407     gres= geocol_symdifference<Coordsys>(bggc1, bggc2, result);
3408     break;
3409   default:
3410     /* Only above four supported. */
3411     assert(false);
3412     break;
3413   }
3414 
3415   if (gres == NULL && !null_value)
3416     gres= empty_result(result, g1->get_srid());
3417   return gres;
3418 }
3419 
3420 
3421 /**
3422   Do intersection operation on geometry collections. We do intersection for
3423   all pairs of components in g1 and g2, put the results in a geometry
3424   collection. If all subresults can be computed successfully, the geometry
3425   collection is our result.
3426 
3427   @tparam Coordsys Coordinate system type, specified using those defined in
3428           boost::geometry::cs.
3429   @param bggc1 First geometry operand, a geometry collection.
3430   @param bggc2 Second geometry operand, a geometry collection.
3431   @param[out] result Holds WKB data of the result, which must be a
3432                 geometry collection.
3433   @return The intersection result, whose WKB data is stored in 'result'.
3434  */
3435 template<typename Coordsys>
3436 Geometry *Item_func_spatial_operation::
geocol_intersection(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3437 geocol_intersection(const BG_geometry_collection &bggc1,
3438                     const BG_geometry_collection &bggc2,
3439                     String *result)
3440 {
3441   Geometry *gres= NULL;
3442   String wkbres;
3443   Geometry *g0= NULL;
3444   BG_geometry_collection bggc;
3445   const BG_geometry_collection::Geometry_list &gv1= bggc1.get_geometries();
3446   const BG_geometry_collection::Geometry_list &gv2= bggc2.get_geometries();
3447   bggc.set_srid(bggc1.get_srid());
3448 
3449   if (gv1.size() == 0 || gv2.size() == 0)
3450   {
3451     return empty_result(result, bggc1.get_srid());
3452   }
3453 
3454   const typename BG_geometry_collection::Geometry_list *gv= NULL, *gvr= NULL;
3455 
3456   if (gv1.size() > gv2.size())
3457   {
3458     gv= &gv2;
3459     gvr= &gv1;
3460   }
3461   else
3462   {
3463     gv= &gv1;
3464     gvr= &gv2;
3465   }
3466 
3467   Rtree_index rtree;
3468   make_rtree(*gvr, &rtree);
3469 
3470   for (BG_geometry_collection::
3471        Geometry_list::const_iterator i= gv->begin();
3472        i != gv->end(); ++i)
3473   {
3474     BG_box box;
3475     make_bg_box(*i, &box);
3476 
3477     Rtree_index::const_query_iterator j= rtree.qbegin(bgi::intersects(box));
3478     if (j == rtree.qend())
3479       continue;
3480 
3481     for (; j != rtree.qend(); ++j)
3482     {
3483       Geometry *geom= (*gvr)[j->second];
3484       // Free before using it, wkbres may have WKB data from last execution.
3485       wkbres.mem_free();
3486       wkbres.length(0);
3487       g0= bg_geo_set_op<Coordsys>(*i, geom, &wkbres);
3488 
3489       if (null_value)
3490       {
3491         if (g0 != NULL && g0 != *i && g0 != geom)
3492           delete g0;
3493         return 0;
3494       }
3495 
3496       if (g0 && !is_empty_geocollection(wkbres))
3497         bggc.fill(g0);
3498       if (g0 != NULL && g0 != *i && g0 != geom)
3499       {
3500         delete g0;
3501         g0= NULL;
3502       }
3503     }
3504   }
3505 
3506   /*
3507     Note: result unify and merge
3508 
3509     The result may have geometry elements that overlap, caused by overlap
3510     geos in either or both gc1 and/or gc2. Also, there may be geometries
3511     that can be merged into a larger one of the same type in the result.
3512     We will need to figure out how to make such enhancements.
3513    */
3514   bggc.merge_components<Coordsys>(&null_value);
3515   if (null_value)
3516     return NULL;
3517   gres= bggc.as_geometry_collection(result);
3518 
3519   return gres;
3520 }
3521 
3522 
3523 /**
3524   Do union operation on geometry collections. We do union for
3525   all pairs of components in g1 and g2, whenever a union can be done, we do
3526   so and put the results in a geometry collection GC and remove the two
3527   components from g1 and g2 respectively. Finally no components in g1 and g2
3528   overlap and GC is our result.
3529 
3530   @tparam Coordsys Coordinate system type, specified using those defined in
3531           boost::geometry::cs.
3532   @param bggc1 First geometry operand, a geometry collection.
3533   @param bggc2 Second geometry operand, a geometry collection.
3534   @param[out] result Holds WKB data of the result, which must be a
3535                 geometry collection.
3536   @return The union result, whose WKB data is stored in 'result'.
3537  */
3538 template<typename Coordsys>
3539 Geometry *Item_func_spatial_operation::
geocol_union(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3540 geocol_union(const BG_geometry_collection &bggc1,
3541              const BG_geometry_collection &bggc2,
3542              String *result)
3543 {
3544   Geometry *gres= NULL;
3545   BG_geometry_collection bggc;
3546   BG_geometry_collection::Geometry_list &gv= bggc.get_geometries();
3547   gv.insert(gv.end(), bggc1.get_geometries().begin(),
3548             bggc1.get_geometries().end());
3549   gv.insert(gv.end(), bggc2.get_geometries().begin(),
3550             bggc2.get_geometries().end());
3551   bggc.set_srid(bggc1.get_srid());
3552 
3553   // It's likely that there are overlapping components in bggc because it
3554   // has components from both bggc1 and bggc2.
3555   bggc.merge_components<Coordsys>(&null_value);
3556   if (!null_value)
3557     gres= bggc.as_geometry_collection(result);
3558 
3559   return gres;
3560 }
3561 
3562 
3563 /**
3564   Do difference operation on geometry collections. For each component CX in g1,
3565   we do CX:= CX difference CY for all components CY in g2. When at last CX isn't
3566   empty, it's put into result geometry collection GC.
3567   If all subresults can be computed successfully, the geometry
3568   collection GC is our result.
3569 
3570   @tparam Coordsys Coordinate system type, specified using those defined in
3571           boost::geometry::cs.
3572   @param bggc1 First geometry operand, a geometry collection.
3573   @param bggc2 Second geometry operand, a geometry collection.
3574   @param[out] result Holds WKB data of the result, which must be a
3575                 geometry collection.
3576   @return The difference result, whose WKB data is stored in 'result'.
3577  */
3578 template<typename Coordsys>
3579 Geometry *Item_func_spatial_operation::
geocol_difference(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3580 geocol_difference(const BG_geometry_collection &bggc1,
3581                   const BG_geometry_collection &bggc2,
3582                   String *result)
3583 {
3584   Geometry *gres= NULL;
3585   String *wkbres= NULL;
3586   BG_geometry_collection bggc;
3587   const BG_geometry_collection::Geometry_list *gv1= &(bggc1.get_geometries());
3588   const BG_geometry_collection::Geometry_list *gv2= &(bggc2.get_geometries());
3589 
3590   bggc.set_srid(bggc1.get_srid());
3591 
3592   // Difference isn't symetric so we have to always build rtree tndex on gv2.
3593   Rtree_index rtree;
3594   make_rtree(*gv2, &rtree);
3595 
3596   for (BG_geometry_collection::
3597        Geometry_list::const_iterator i= gv1->begin();
3598        i != gv1->end(); ++i)
3599   {
3600     bool g11_isempty= false;
3601     auto_ptr<Geometry> guard11;
3602     Geometry *g11= NULL;
3603     g11= *i;
3604     Inplace_vector<String> wkbstrs(PSI_INSTRUMENT_ME);
3605 
3606     BG_box box;
3607     make_bg_box(*i, &box);
3608 
3609     /*
3610       Above theory makes sure all results are in rtree_result, the logic
3611       here is sufficient when rtree_result is empty.
3612     */
3613     for (Rtree_index::const_query_iterator
3614          j= rtree.qbegin(bgi::intersects(box));
3615          j != rtree.qend(); ++j)
3616     {
3617       Geometry *geom= (*gv2)[j->second];
3618 
3619       wkbres= wkbstrs.append_object();
3620       if (wkbres == NULL)
3621       {
3622         null_value= TRUE;
3623         return NULL;
3624       }
3625       Geometry *g0= bg_geo_set_op<Coordsys>(g11, geom, wkbres);
3626       auto_ptr<Geometry> guard0(g0);
3627 
3628       if (null_value)
3629       {
3630         if (!(g0 != NULL && g0 != *i && g0 != geom))
3631           guard0.release();
3632         if (!(g11 != NULL && g11 != g0 && g11 != *i && g11 != geom))
3633           guard11.release();
3634         return NULL;
3635       }
3636 
3637       if (g0 != NULL && !is_empty_geocollection(*wkbres))
3638       {
3639         if (g11 != NULL && g11 != *i && g11 != geom && g11 != g0)
3640           delete guard11.release();
3641         else
3642           guard11.release();
3643         guard0.release();
3644         g11= g0;
3645         if (g0 != NULL && g0 != *i && g0 != geom)
3646           guard11.reset(g11);
3647       }
3648       else
3649       {
3650         g11_isempty= true;
3651         if (!(g0 != NULL && g0 != *i && g0 != geom && g0 != g11))
3652           guard0.release();
3653         break;
3654       }
3655     }
3656 
3657     if (!g11_isempty)
3658       bggc.fill(g11);
3659     if (!(g11 != NULL && g11 != *i))
3660       guard11.release();
3661     else
3662       guard11.reset(NULL);
3663   }
3664 
3665   bggc.merge_components<Coordsys>(&null_value);
3666   if (null_value)
3667     return NULL;
3668   gres= bggc.as_geometry_collection(result);
3669 
3670   return gres;
3671 }
3672 
3673 
3674 /**
3675   Symmetric difference of geometry collections.
3676 
3677   Symdifference(g1, g2) is implemented as
3678   union(difference(g1, g2), difference(g2, g1)).
3679 
3680   @tparam Coordsys Coordinate system type, specified using those defined in
3681           boost::geometry::cs.
3682   @param bggc1 First geometry operand, a geometry collection.
3683   @param bggc2 Second geometry operand, a geometry collection.
3684   @param[out] result Holds WKB data of the result, which must be a
3685                 geometry collection.
3686   @return The symdifference result, whose WKB data is stored in 'result'.
3687  */
3688 template<typename Coordsys>
3689 Geometry *Item_func_spatial_operation::
geocol_symdifference(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3690 geocol_symdifference(const BG_geometry_collection &bggc1,
3691                      const BG_geometry_collection &bggc2,
3692                      String *result)
3693 {
3694   Geometry *res= NULL;
3695   auto_ptr<Geometry> diff12(NULL);
3696   auto_ptr<Geometry> diff21(NULL);
3697   String diff12_wkb;
3698   String diff21_wkb;
3699 
3700   Var_resetter<op_type>
3701     var_reset(&spatial_op, op_symdifference);
3702 
3703   spatial_op= op_difference;
3704   diff12.reset(geocol_difference<Coordsys>(bggc1, bggc2, &diff12_wkb));
3705   if (null_value)
3706     return NULL;
3707   assert(diff12.get() != NULL);
3708 
3709   diff21.reset(geocol_difference<Coordsys>(bggc2, bggc1, &diff21_wkb));
3710   if (null_value)
3711     return NULL;
3712   assert(diff21.get() != NULL);
3713 
3714   spatial_op= op_union;
3715   res= geometry_collection_set_operation<Coordsys>(diff12.get(), diff21.get(),
3716                                                    result);
3717   if (diff12.get() == res)
3718   {
3719     result->takeover(diff12_wkb);
3720     diff12.release();
3721   }
3722   else if (diff21.get() == res)
3723   {
3724     result->takeover(diff21_wkb);
3725     diff21.release();
3726   }
3727 
3728   if (null_value)
3729   {
3730     if (res != NULL)
3731       delete res;
3732     return NULL;
3733   }
3734 
3735   return res;
3736 }
3737 
3738 
assign_result(Geometry * geo,String * result)3739 bool Item_func_spatial_operation::assign_result(Geometry *geo, String *result)
3740 {
3741   assert(geo->has_geom_header_space());
3742   char *p= geo->get_cptr() - GEOM_HEADER_SIZE;
3743   write_geometry_header(p, geo->get_srid(), geo->get_geotype());
3744   result->set(p, GEOM_HEADER_SIZE + geo->get_nbytes(), &my_charset_bin);
3745   bg_resbuf_mgr.add_buffer(p);
3746   geo->set_ownmem(false);
3747 
3748   return false;
3749 }
3750 
3751 
func_name() const3752 const char *Item_func_spatial_operation::func_name() const
3753 {
3754   switch (spatial_op) {
3755     case op_intersection:
3756       return "st_intersection";
3757     case op_difference:
3758       return "st_difference";
3759     case op_union:
3760       return "st_union";
3761     case op_symdifference:
3762       return "st_symdifference";
3763     default:
3764       assert(0);  // Should never happen
3765       return "sp_unknown";
3766   }
3767 }
3768