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