1 /* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /**
24   @file
25   @brief
26   This file contains the implementation for the Item that implements
27   ST_Buffer().
28 */
29 
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <algorithm>
35 #include <boost/concept/usage.hpp>
36 #include <boost/geometry/algorithms/buffer.hpp>
37 #include <boost/geometry/strategies/agnostic/buffer_distance_symmetric.hpp>
38 #include <boost/geometry/strategies/buffer.hpp>
39 #include <boost/geometry/strategies/cartesian/buffer_end_flat.hpp>
40 #include <boost/geometry/strategies/cartesian/buffer_end_round.hpp>
41 #include <boost/geometry/strategies/cartesian/buffer_join_miter.hpp>
42 #include <boost/geometry/strategies/cartesian/buffer_join_round.hpp>
43 #include <boost/geometry/strategies/cartesian/buffer_point_circle.hpp>
44 #include <boost/geometry/strategies/cartesian/buffer_point_square.hpp>
45 #include <boost/geometry/strategies/cartesian/buffer_side_straight.hpp>
46 #include <boost/geometry/strategies/strategies.hpp>
47 #include <boost/iterator/iterator_facade.hpp>
48 #include <memory>  // std::unique_ptr
49 #include <vector>
50 
51 #include "m_ctype.h"
52 #include "m_string.h"
53 #include "my_byteorder.h"
54 #include "my_dbug.h"
55 #include "my_inttypes.h"
56 #include "my_sys.h"
57 #include "mysqld_error.h"
58 #include "sql/current_thd.h"
59 #include "sql/dd/cache/dictionary_client.h"
60 #include "sql/dd/types/spatial_reference_system.h"
61 #include "sql/derror.h"  // ER_THD
62 #include "sql/item.h"
63 #include "sql/item_geofunc.h"
64 #include "sql/item_geofunc_internal.h"
65 #include "sql/item_strfunc.h"
66 #include "sql/parse_tree_node_base.h"
67 #include "sql/spatial.h"
68 #include "sql/sql_class.h"  // THD
69 #include "sql/sql_error.h"
70 #include "sql/sql_exception_handler.h"
71 #include "sql/srs_fetcher.h"
72 #include "sql/system_variables.h"
73 #include "sql_string.h"
74 #include "template_utils.h"
75 
76 class PT_item_list;
77 
78 namespace boost {
79 namespace geometry {
80 namespace cs {
81 struct cartesian;
82 }  // namespace cs
83 }  // namespace geometry
84 }  // namespace boost
85 
86 static const char *const buffer_strategy_names[] = {
87     "invalid_strategy", "end_round",    "end_flat",    "join_round",
88     "join_miter",       "point_circle", "point_square"};
89 
90 template <typename Char_type>
char_icmp(const Char_type a,const Char_type b)91 inline int char_icmp(const Char_type a, const Char_type b) {
92   const int a1 = std::tolower(a);
93   const int b1 = std::tolower(b);
94   return a1 > b1 ? 1 : (a1 < b1 ? -1 : 0);
95 }
96 
97 /**
98   Case insensitive comparison of two ascii strings.
99   @param a '\0' ended string.
100   @param b '\0' ended string.
101  */
102 template <typename Char_type>
str_icmp(const Char_type * a,const Char_type * b)103 int str_icmp(const Char_type *a, const Char_type *b) {
104   int ret = 0, i;
105 
106   for (i = 0; a[i] != 0 && b[i] != 0; i++)
107     if ((ret = char_icmp(a[i], b[i]))) return ret;
108   if (a[i] == 0 && b[i] != 0) return -1;
109   if (a[i] != 0 && b[i] == 0) return 1;
110   return 0;
111 }
112 
113 /*
114   Convert strategies stored in String objects into Strategy_setting objects.
115 */
set_strategies()116 void Item_func_buffer::set_strategies() {
117   for (int i = 0; i < num_strats; i++) {
118     String *pstr = strategies[i];
119     const uchar *pstrat = pointer_cast<const uchar *>(pstr->ptr());
120 
121     uint32 snum = 0;
122 
123     if (pstr->length() != 12 ||
124         !((snum = uint4korr(pstrat)) > invalid_strategy &&
125           snum <= max_strategy)) {
126       my_error(ER_WRONG_ARGUMENTS, MYF(0), "st_buffer");
127       null_value = true;
128       return;
129     }
130 
131     const enum_buffer_strategies strat = (enum_buffer_strategies)snum;
132     double value = float8get(pstrat + 4);
133     enum_buffer_strategy_types strategy_type = invalid_strategy_type;
134 
135     switch (strat) {
136       case end_round:
137       case end_flat:
138         strategy_type = end_strategy;
139         break;
140       case join_round:
141       case join_miter:
142         strategy_type = join_strategy;
143         break;
144       case point_circle:
145       case point_square:
146         strategy_type = point_strategy;
147         break;
148       default:
149         my_error(ER_WRONG_ARGUMENTS, MYF(0), "st_buffer");
150         null_value = true;
151         return;
152         break;
153     }
154 
155     // Each strategy option can be set no more than once for every ST_Buffer()
156     // call.
157     if (settings[strategy_type].strategy != invalid_strategy) {
158       my_error(ER_WRONG_ARGUMENTS, MYF(0), "st_buffer");
159       null_value = true;
160       return;
161     } else {
162       settings[strategy_type].strategy = (enum_buffer_strategies)snum;
163       settings[strategy_type].value = value;
164     }
165   }
166 }
167 
Item_func_buffer_strategy(const POS & pos,PT_item_list * ilist)168 Item_func_buffer_strategy::Item_func_buffer_strategy(const POS &pos,
169                                                      PT_item_list *ilist)
170     : Item_str_func(pos, ilist) {
171   // Here we want to use the String::set(const char*, ..) version.
172   const char *pbuf = tmp_buffer;
173   tmp_value.set(pbuf, 0, nullptr);
174 }
175 
resolve_type(THD *)176 bool Item_func_buffer_strategy::resolve_type(THD *) {
177   collation.set(&my_charset_bin);
178   decimals = 0;
179   max_length = 16;
180   maybe_null = true;
181   return false;
182 }
183 
val_str(String *)184 String *Item_func_buffer_strategy::val_str(String * /* str_arg */) {
185   String str;
186   String *strat_name = args[0]->val_str_ascii(&str);
187   if ((null_value = args[0]->null_value)) {
188     DBUG_ASSERT(maybe_null);
189     return nullptr;
190   }
191 
192   // Get the NULL-terminated ascii string.
193   const char *pstrat_name = strat_name->c_ptr_safe();
194 
195   bool found = false;
196 
197   tmp_value.set_charset(&my_charset_bin);
198   // The tmp_value is supposed to always stores a {uint32,double} pair,
199   // and it uses a char tmp_buffer[16] array data member.
200   uchar *result_buf = pointer_cast<uchar *>(tmp_value.ptr());
201 
202   // Although the result of this item node is never persisted, we still have to
203   // use portable endianess access otherwise unaligned access will crash
204   // on sparc CPUs.
205   for (uint32 i = 0; i <= Item_func_buffer::max_strategy; i++) {
206     // The above var_str_ascii() call makes the strat_name an ascii string so
207     // we can do below comparison.
208     if (str_icmp(pstrat_name, buffer_strategy_names[i]) != 0) continue;
209 
210     int4store(result_buf, i);
211     result_buf += 4;
212     Item_func_buffer::enum_buffer_strategies istrat =
213         static_cast<Item_func_buffer::enum_buffer_strategies>(i);
214 
215     /*
216       The end_flat and point_square strategies must have no more arguments;
217       The rest strategies must have 2nd parameter which must be a positive
218       numeric value, and we will store it as a double.
219       We use float8store to ensure that the value is independent of endianness.
220     */
221     if (istrat != Item_func_buffer::end_flat &&
222         istrat != Item_func_buffer::point_square) {
223       if (arg_count != 2) {
224         my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
225         return error_str();
226       }
227 
228       double val = args[1]->val_real();
229       if ((null_value = args[1]->null_value)) {
230         DBUG_ASSERT(maybe_null);
231         return nullptr;
232       }
233       if (val <= 0) {
234         my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
235         return error_str();
236       }
237 
238       if (istrat != Item_func_buffer::join_miter &&
239           val > current_thd->variables.max_points_in_geometry) {
240         my_error(ER_GIS_MAX_POINTS_IN_GEOMETRY_OVERFLOWED, MYF(0),
241                  "points_per_circle",
242                  current_thd->variables.max_points_in_geometry, func_name());
243         return error_str();
244       }
245 
246       float8store(result_buf, val);
247     } else if (arg_count != 1) {
248       my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
249       return error_str();
250     } else
251       float8store(result_buf, 0.0);
252 
253     found = true;
254 
255     break;
256   }
257 
258   // Unrecognized strategy names, report error.
259   if (!found) {
260     my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
261     return error_str();
262   }
263   tmp_value.length(12);
264 
265   return &tmp_value;
266 }
267 
268 #define CALL_BG_BUFFER(result, geom, geom_out, dist_strategy, side_strategy, \
269                        join_strategy, end_strategy, point_strategy)          \
270   do {                                                                       \
271     (result) = false;                                                        \
272     switch ((geom)->get_type()) {                                            \
273       case Geometry::wkb_point: {                                            \
274         BG_models<bgcs::cartesian>::Point bg(                                \
275             (geom)->get_data_ptr(), (geom)->get_data_size(),                 \
276             (geom)->get_flags(), (geom)->get_srid());                        \
277         bg::buffer(bg, (geom_out), (dist_strategy), (side_strategy),         \
278                    (join_strategy), (end_strategy), (point_strategy));       \
279         break;                                                               \
280       }                                                                      \
281       case Geometry::wkb_multipoint: {                                       \
282         BG_models<bgcs::cartesian>::Multipoint bg(                           \
283             (geom)->get_data_ptr(), (geom)->get_data_size(),                 \
284             (geom)->get_flags(), (geom)->get_srid());                        \
285         bg::buffer(bg, (geom_out), (dist_strategy), (side_strategy),         \
286                    (join_strategy), (end_strategy), (point_strategy));       \
287         break;                                                               \
288       }                                                                      \
289       case Geometry::wkb_linestring: {                                       \
290         BG_models<bgcs::cartesian>::Linestring bg(                           \
291             (geom)->get_data_ptr(), (geom)->get_data_size(),                 \
292             (geom)->get_flags(), (geom)->get_srid());                        \
293         bg::buffer(bg, (geom_out), (dist_strategy), (side_strategy),         \
294                    (join_strategy), (end_strategy), (point_strategy));       \
295         break;                                                               \
296       }                                                                      \
297       case Geometry::wkb_multilinestring: {                                  \
298         BG_models<bgcs::cartesian>::Multilinestring bg(                      \
299             (geom)->get_data_ptr(), (geom)->get_data_size(),                 \
300             (geom)->get_flags(), (geom)->get_srid());                        \
301         bg::buffer(bg, (geom_out), (dist_strategy), (side_strategy),         \
302                    (join_strategy), (end_strategy), (point_strategy));       \
303         break;                                                               \
304       }                                                                      \
305       case Geometry::wkb_polygon: {                                          \
306         const void *data_ptr = (geom)->normalize_ring_order();               \
307         if (data_ptr == NULL) {                                              \
308           my_error(ER_GIS_INVALID_DATA, MYF(0), "st_buffer");                \
309           (result) = true;                                                   \
310           break;                                                             \
311         }                                                                    \
312         BG_models<bgcs::cartesian>::Polygon bg(                              \
313             data_ptr, (geom)->get_data_size(), (geom)->get_flags(),          \
314             (geom)->get_srid());                                             \
315         bg::buffer(bg, (geom_out), (dist_strategy), (side_strategy),         \
316                    (join_strategy), (end_strategy), (point_strategy));       \
317         break;                                                               \
318       }                                                                      \
319       case Geometry::wkb_multipolygon: {                                     \
320         const void *data_ptr = (geom)->normalize_ring_order();               \
321         if (data_ptr == NULL) {                                              \
322           my_error(ER_GIS_INVALID_DATA, MYF(0), "st_buffer");                \
323           (result) = true;                                                   \
324           break;                                                             \
325         }                                                                    \
326         BG_models<bgcs::cartesian>::Multipolygon bg(                         \
327             data_ptr, (geom)->get_data_size(), (geom)->get_flags(),          \
328             (geom)->get_srid());                                             \
329         bg::buffer(bg, (geom_out), (dist_strategy), (side_strategy),         \
330                    (join_strategy), (end_strategy), (point_strategy));       \
331         break;                                                               \
332       }                                                                      \
333       default:                                                               \
334         DBUG_ASSERT(false);                                                  \
335         break;                                                               \
336     }                                                                        \
337   } while (0)
338 
Item_func_buffer(const POS & pos,PT_item_list * ilist)339 Item_func_buffer::Item_func_buffer(const POS &pos, PT_item_list *ilist)
340     : Item_geometry_func(pos, ilist) {
341   num_strats = 0;
342   memset(settings, 0, sizeof(settings));
343   memset(strategies, 0, sizeof(strategies));
344 }
345 
346 namespace bgst = boost::geometry::strategy::buffer;
347 
val_str(String * str_value_arg)348 String *Item_func_buffer::val_str(String *str_value_arg) {
349   DBUG_TRACE;
350   DBUG_ASSERT(fixed == 1);
351   String strat_bufs[side_strategy + 1];
352 
353   String *obj = args[0]->val_str(&tmp_value);
354   if (!obj || args[0]->null_value) return error_str();
355 
356   double dist = args[1]->val_real();
357   if (args[1]->null_value) return error_str();
358 
359   Geometry_buffer buffer;
360   Geometry *geom;
361   String *str_result = str_value_arg;
362 
363   null_value = false;
364   bg_resbuf_mgr.free_result_buffer();
365 
366   // Reset the two arrays, set_strategies() requires the settings array to
367   // be brand new on every ST_Buffer() call.
368   memset(settings, 0, sizeof(settings));
369   memset(strategies, 0, sizeof(strategies));
370 
371   // Strategies options start from 3rd argument, the 1st two arguments are
372   // never strategies: the 1st is input geometry, and the 2nd is distance.
373   num_strats = arg_count - 2;
374   for (uint i = 2; i < arg_count; i++) {
375     strategies[i - 2] = args[i]->val_str(&strat_bufs[i]);
376     if (strategies[i - 2] == nullptr || args[i]->null_value) return error_str();
377   }
378 
379   /*
380     Do this before simplify_multi_geometry() in order to exclude invalid
381     WKB/WKT data.
382    */
383   if (!(geom = Geometry::construct(&buffer, obj))) {
384     my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
385     return error_str();
386   }
387 
388   if (geom->get_srid() != 0) {
389     THD *thd = current_thd;
390     std::unique_ptr<dd::cache::Dictionary_client::Auto_releaser> releaser(
391         new dd::cache::Dictionary_client::Auto_releaser(thd->dd_client()));
392     Srs_fetcher fetcher(thd);
393     const dd::Spatial_reference_system *srs = nullptr;
394     if (fetcher.acquire(geom->get_srid(), &srs))
395       return error_str();  // Error has already been flagged.
396 
397     if (srs == nullptr) {
398       my_error(ER_SRS_NOT_FOUND, MYF(0), geom->get_srid());
399       return error_str();
400     }
401 
402     if (!srs->is_cartesian()) {
403       DBUG_ASSERT(srs->is_geographic());
404       std::string parameters(geom->get_class_info()->m_name.str);
405       parameters.append(", ...");
406       my_error(ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS, MYF(0), func_name(),
407                parameters.c_str());
408       return error_str();
409     }
410   }
411 
412   /*
413     If the input geometry is a multi-geometry or geometry collection that has
414     only one component, extract that component as input argument.
415   */
416   Geometry::wkbType geom_type = geom->get_type();
417   if (geom_type == Geometry::wkb_multipoint ||
418       geom_type == Geometry::wkb_multipolygon ||
419       geom_type == Geometry::wkb_multilinestring ||
420       geom_type == Geometry::wkb_geometrycollection) {
421     /*
422       Make a copy of the geometry byte string argument to work on it,
423       don't modify the original one since it is assumed to be stable.
424       Simplifying the argument is worth the effort because buffer computation
425       is less expensive with simplified geometry.
426 
427       The copy's buffer may be directly returned as result so it has to be a
428       data member.
429 
430       Here we assume that if obj->is_alloced() is false, obj's referring to some
431       geometry data stored somewhere else so here we cache the simplified
432       version into m_tmp_geombuf without modifying obj's original referred copy;
433       otherwise we believe the geometry data
434       is solely owned by obj and that each call of this ST_Buffer() is given
435       a valid GEOMETRY byte string, i.e. it is structually valid and if it was
436       simplified before, the obj->m_length was correctly set to the new length
437       after the simplification operation.
438      */
439     const bool use_buffer = !obj->is_alloced();
440     if (simplify_multi_geometry(obj, (use_buffer ? &m_tmp_geombuf : nullptr)) &&
441         use_buffer)
442       obj = &m_tmp_geombuf;
443 
444     if (!(geom = Geometry::construct(&buffer, obj))) {
445       my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
446       return error_str();
447     }
448   }
449 
450   /*
451     If distance passed to ST_Buffer is too small, then we return the
452     original geometry as its buffer. This is needed to avoid division
453     overflow in buffer calculation, as well as for performance purposes.
454   */
455   if (std::abs(dist) <= GIS_ZERO || is_empty_geocollection(geom)) {
456     null_value = false;
457     str_result = obj;
458     return str_result;
459   }
460 
461   Geometry::wkbType gtype = geom->get_type();
462   if (dist < 0 && gtype != Geometry::wkb_polygon &&
463       gtype != Geometry::wkb_multipolygon &&
464       gtype != Geometry::wkb_geometrycollection) {
465     my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
466     return error_str();
467   }
468 
469   set_strategies();
470   if (null_value) return error_str();
471 
472   /*
473     str_result will refer to BG object's memory directly if any, here we remove
474     last call's remainings so that if this call doesn't produce any result,
475     this call won't note down last address(already freed above) and
476     next call won't free already free'd memory.
477   */
478   str_result->set(NullS, 0, &my_charset_bin);
479   bool had_except = false;
480 
481   try {
482     Strategy_setting ss1 = settings[end_strategy];
483     Strategy_setting ss2 = settings[join_strategy];
484     Strategy_setting ss3 = settings[point_strategy];
485 
486     const bool is_pts =
487         (gtype == Geometry::wkb_point || gtype == Geometry::wkb_multipoint);
488 
489     const bool is_plygn =
490         (gtype == Geometry::wkb_polygon || gtype == Geometry::wkb_multipolygon);
491     const bool is_ls = (gtype == Geometry::wkb_linestring ||
492                         gtype == Geometry::wkb_multilinestring);
493 
494     /*
495       Some strategies can be applied to only part of the geometry types and
496       coordinate systems. For now we only have cartesian coordinate system
497       so no check for them.
498     */
499     if ((is_pts && (ss1.strategy != invalid_strategy ||
500                     ss2.strategy != invalid_strategy)) ||
501         (is_plygn && (ss1.strategy != invalid_strategy ||
502                       ss3.strategy != invalid_strategy)) ||
503         (is_ls && ss3.strategy != invalid_strategy)) {
504       my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
505       return error_str();
506     }
507 
508     bgst::distance_symmetric<double> dist_strat(dist);
509     bgst::side_straight side_strat;
510     bgst::end_round bgst_end_round(
511         ss1.strategy == invalid_strategy ? 32 : ss1.value);
512     bgst::end_flat bgst_end_flat;
513     bgst::join_round bgst_join_round(
514         ss2.strategy == invalid_strategy ? 32 : ss2.value);
515     bgst::join_miter bgst_join_miter(ss2.value);
516     bgst::point_circle bgst_point_circle(
517         ss3.strategy == invalid_strategy ? 32 : ss3.value);
518     bgst::point_square bgst_point_square;
519 
520     /*
521       Default strategies if not specified:
522       end_round(32), join_round(32), point_circle(32)
523       The order of enum items in enum enum_buffer_strategies is crucial for
524       this setting to be correct, don't modify it.
525 
526       Although point strategy isn't needed for linear and areal geometries,
527       we have to specify it because of bg::buffer interface, and BG will
528       silently ignore it. Similarly for other strategies.
529     */
530     int strats_combination = 0;
531     if (ss1.strategy == end_flat) strats_combination |= 1;
532     if (ss2.strategy == join_miter) strats_combination |= 2;
533     if (ss3.strategy == point_square) strats_combination |= 4;
534 
535     BG_models<bgcs::cartesian>::Multipolygon result;
536     result.set_srid(geom->get_srid());
537 
538     if (geom->get_type() != Geometry::wkb_geometrycollection) {
539       bool ret = false;
540       switch (strats_combination) {
541         case 0:
542           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
543                          bgst_join_round, bgst_end_round, bgst_point_circle);
544           break;
545         case 1:
546           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
547                          bgst_join_round, bgst_end_flat, bgst_point_circle);
548           break;
549         case 2:
550           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
551                          bgst_join_miter, bgst_end_round, bgst_point_circle);
552           break;
553         case 3:
554           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
555                          bgst_join_miter, bgst_end_flat, bgst_point_circle);
556           break;
557         case 4:
558           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
559                          bgst_join_round, bgst_end_round, bgst_point_square);
560           break;
561         case 5:
562           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
563                          bgst_join_round, bgst_end_flat, bgst_point_square);
564           break;
565         case 6:
566           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
567                          bgst_join_miter, bgst_end_round, bgst_point_square);
568           break;
569         case 7:
570           CALL_BG_BUFFER(ret, geom, result, dist_strat, side_strat,
571                          bgst_join_miter, bgst_end_flat, bgst_point_square);
572           break;
573         default:
574           DBUG_ASSERT(false);
575           break;
576       }
577 
578       if (ret) return error_str();
579 
580       if (result.size() == 0) {
581         str_result->reserve(GEOM_HEADER_SIZE + 4);
582         write_geometry_header(str_result, geom->get_srid(),
583                               Geometry::wkb_geometrycollection, 0);
584         return str_result;
585       } else if (post_fix_result(&bg_resbuf_mgr, result, str_result))
586         return error_str();
587       bg_resbuf_mgr.set_result_buffer(str_result->ptr());
588     } else {
589       // Compute buffer for a geometry collection(GC). We first compute buffer
590       // for each component of the GC, and put the buffer polygons into another
591       // collection, finally merge components of the collection.
592       BG_geometry_collection bggc, bggc2;
593       bggc.fill(geom);
594 
595       for (BG_geometry_collection::Geometry_list::iterator i =
596                bggc.get_geometries().begin();
597            i != bggc.get_geometries().end(); ++i) {
598         BG_models<bgcs::cartesian>::Multipolygon res;
599         String temp_result;
600 
601         res.set_srid((*i)->get_srid());
602         Geometry::wkbType g_type = (*i)->get_type();
603         if (dist < 0 && g_type != Geometry::wkb_multipolygon &&
604             g_type != Geometry::wkb_polygon) {
605           my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
606           return error_str();
607         }
608 
609         bool ret = false;
610         switch (strats_combination) {
611           case 0:
612             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
613                            bgst_join_round, bgst_end_round, bgst_point_circle);
614             break;
615           case 1:
616             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
617                            bgst_join_round, bgst_end_flat, bgst_point_circle);
618             break;
619           case 2:
620             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
621                            bgst_join_miter, bgst_end_round, bgst_point_circle);
622             break;
623           case 3:
624             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
625                            bgst_join_miter, bgst_end_flat, bgst_point_circle);
626             break;
627           case 4:
628             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
629                            bgst_join_round, bgst_end_round, bgst_point_square);
630             break;
631           case 5:
632             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
633                            bgst_join_round, bgst_end_flat, bgst_point_square);
634             break;
635           case 6:
636             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
637                            bgst_join_miter, bgst_end_round, bgst_point_square);
638             break;
639           case 7:
640             CALL_BG_BUFFER(ret, *i, res, dist_strat, side_strat,
641                            bgst_join_miter, bgst_end_flat, bgst_point_square);
642             break;
643           default:
644             DBUG_ASSERT(false);
645             break;
646         }
647 
648         if (ret) return error_str();
649         if (res.size() == 0) continue;
650         if (post_fix_result(&bg_resbuf_mgr, res, &temp_result))
651           return error_str();
652 
653         // A single component's buffer is computed above and stored here.
654         bggc2.fill(&res);
655       }
656 
657       // Merge the accumulated polygons because they may overlap.
658       bggc2.merge_components<bgcs::cartesian>(&null_value);
659       Gis_geometry_collection *gc = bggc2.as_geometry_collection(str_result);
660       delete gc;
661     }
662 
663     /*
664       If the result geometry is a multi-geometry or geometry collection that has
665       only one component, extract that component as result.
666     */
667     simplify_multi_geometry(str_result, nullptr);
668   } catch (...) {
669     had_except = true;
670     handle_gis_exception("st_buffer");
671   }
672 
673   if (had_except) return error_str();
674   return str_result;
675 }
676