MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gcalc_tools.cc
1 /* Copyright (c) 2000, 2011, 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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 
17 #include "my_global.h"
18 
19 #ifdef HAVE_SPATIAL
20 
21 #include "sql_string.h"
22 #include "gcalc_tools.h"
23 #include "gstream.h" // Gis_read_stream
24 #include "spatial.h"
25 #include "sql_class.h" // THD
26 
27 #define float_to_coord(d) ((double) d)
28 
29 
30 /*
31  Adds new shape to the relation.
32  After that it can be used as an argument of an operation.
33 */
34 
35 gcalc_shape_info Gcalc_function::add_new_shape(uint32 shape_id,
36  shape_type shape_kind)
37 {
38  shapes_buffer.q_append((uint32) shape_kind);
39  return n_shapes++;
40 }
41 
42 
43 /*
44  Adds new operation to the constructed relation.
45  To construct the complex relation one has to specify operations
46  in prefix style.
47 */
48 
49 void Gcalc_function::add_operation(op_type operation, uint32 n_operands)
50 {
51  uint32 op_code= (uint32 ) operation + n_operands;
52  function_buffer.q_append(op_code);
53 }
54 
55 
56 /*
57  Sometimes the number of arguments is unknown at the moment the operation
58  is added. That allows to specify it later.
59 */
60 
61 void Gcalc_function::add_operands_to_op(uint32 operation_pos, uint32 n_operands)
62 {
63  uint32 op_code= uint4korr(function_buffer.ptr() + operation_pos) + n_operands;
64  function_buffer.write_at_position(operation_pos, op_code);
65 }
66 
67 
68 void Gcalc_function::set_operands_to_op(uint32 operation_pos, uint32 n_operands)
69 {
70  uint32 op_code= (uint4korr(function_buffer.ptr() + operation_pos) & op_any) +
71  n_operands;
72  function_buffer.write_at_position(operation_pos, op_code);
73 }
74 
75 
76 /*
77  Just like the add_operation() but the result will be the inverted
78  value of an operation.
79 */
80 
81 void Gcalc_function::add_not_operation(op_type operation, uint32 n_operands)
82 {
83  uint32 op_code= ((uint32) op_not | (uint32 ) operation) + n_operands;
84  function_buffer.q_append(op_code);
85 }
86 
87 
88 int Gcalc_function::single_shape_op(shape_type shape_kind, gcalc_shape_info *si)
89 {
90  if (reserve_shape_buffer(1) || reserve_op_buffer(1))
91  return 1;
92  *si= add_new_shape(0, shape_kind);
93  add_operation(op_shape, *si);
94  return 0;
95 }
96 
97 
98 /*
99  Specify how many arguments we're going to have.
100 */
101 
102 int Gcalc_function::reserve_shape_buffer(uint n_shapes)
103 {
104  return shapes_buffer.reserve(n_shapes * shape_buffer_item_size, 512);
105 }
106 
107 
108 /*
109  Specify how many operations we're going to have.
110 */
111 
112 int Gcalc_function::reserve_op_buffer(uint n_ops)
113 {
114  return function_buffer.reserve(n_ops * function_buffer_item_size, 512);
115 }
116 
117 
118 int Gcalc_function::alloc_states()
119 {
120  if (function_buffer.reserve((n_shapes+1) * sizeof(int)))
121  return 1;
122  i_states= (int *) (function_buffer.ptr() + ALIGN_SIZE(function_buffer.length()));
123  return 0;
124 }
125 
126 
127 
128 #ifndef DBUG_OFF
129 
132 const char *Gcalc_function::op_name(int code)
133 {
134  enum op_type type= (enum op_type) code;
135  switch (type)
136  {
137  case op_shape: return "op_shape";
138  case op_not: return "op_not";
139  case op_union: return "op_union";
140  case op_intersection: return "op_intersection";
141  case op_symdifference: return "op_symdifference";
142  case op_difference: return "op_difference";
143  case op_backdifference: return "op_backdifference";
144  case op_any: return "op_any";
145  }
146  return "op_unknown";
147 }
148 
149 
150 const char *Gcalc_function::shape_name(int code)
151 {
152  switch (code)
153  {
154  case shape_point: return "shape_point";
155  case shape_line: return "shape_line";
156  case shape_polygon: return "shape_polygon";
157  case shape_hole: return "shape_hole";
158  }
159  return "shape_unknown";
160 }
161 
162 
168 {
169  int i, nelements= function_buffer.length() / function_buffer_item_size;
170  THD *thd= current_thd;
171  DBUG_PRINT("info", ("nelements=%d", nelements));
172  for (i= 0; i < nelements; i++)
173  {
174  int c_op= uint4korr(function_buffer.ptr() + function_buffer_item_size * i);
175  int func= (c_op & op_any);
176  int mask= (c_op & op_not) ? 1 : 0;
177  int n_ops= c_op & ~op_any;
178  const char *c_op_name= op_name(func);
179  const char *s_name= (func == op_shape) ?
180  shape_name(uint4korr(shapes_buffer.ptr() +
181  shape_buffer_item_size * n_ops)) : "";
182  DBUG_PRINT("info", ("[%d]=%d c_op=%d (%s) mask=%d n_ops=%d",
183  i, c_op, func, c_op_name, mask, n_ops));
184  if (thd->get_gis_debug())
185  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
186  ER_UNKNOWN_ERROR, "[%d] %s[%d]%s",
187  i, c_op_name, n_ops, s_name);
188  }
189 }
190 #endif
191 
192 
193 int Gcalc_function::count_internal()
194 {
195  int c_op= uint4korr(cur_func);
196  op_type next_func= (op_type) (c_op & op_any);
197  int mask= (c_op & op_not) ? 1:0;
198  int n_ops= c_op & ~op_any;
199  int result;
200 
201  cur_func+= function_buffer_item_size;
202  if (next_func == op_shape)
203  return i_states[c_op & ~(op_any | op_not)] ^ mask;
204 
205  result= count_internal();
206 
207  while (--n_ops)
208  {
209  int next_res= count_internal();
210  switch (next_func)
211  {
212  case op_union:
213  result= result | next_res;
214  break;
215  case op_intersection:
216  result= result & next_res;
217  break;
218  case op_symdifference:
219  result= result ^ next_res;
220  break;
221  case op_difference:
222  result= result & !next_res;
223  break;
224  case op_backdifference:
225  result= !result & next_res;
226  break;
227  default:
228  DBUG_ASSERT(FALSE);
229  };
230  }
231 
232  return result ^ mask;
233 }
234 
235 
236 /*
237  Clear the state of the object.
238 */
239 
240 void Gcalc_function::reset()
241 {
242  n_shapes= 0;
243  shapes_buffer.length(0);
244  function_buffer.length(0);
245 }
246 
247 
248 int Gcalc_function::find_function(Gcalc_scan_iterator &scan_it)
249 {
250  while (scan_it.more_points())
251  {
252  if (scan_it.step())
253  return -1;
254  Gcalc_scan_events ev= scan_it.get_event();
255  const Gcalc_scan_iterator::point *evpos= scan_it.get_event_position();
256  if (ev & (scev_point | scev_end | scev_two_ends))
257  continue;
258 
259  clear_state();
260  for (Gcalc_point_iterator pit(&scan_it); pit.point() != evpos; ++pit)
261  {
262  gcalc_shape_info si= pit.point()->get_shape();
263  if ((get_shape_kind(si) == Gcalc_function::shape_polygon))
264  invert_state(si);
265  }
266  invert_state(evpos->get_shape());
267 
268  if (ev == scev_intersection)
269  {
270  const Gcalc_scan_iterator::point *evnext= evpos->c_get_next();
271  if ((get_shape_kind(evpos->get_shape()) !=
272  Gcalc_function::shape_polygon) ||
273  (get_shape_kind(evnext->get_shape()) !=
274  Gcalc_function::shape_polygon))
275  invert_state(evnext->get_shape());
276  }
277 
278  if (count())
279  return 1;
280  }
281  return 0;
282 }
283 
284 
285 int Gcalc_operation_transporter::single_point(Gcalc_shape_status *st,
286  double x, double y)
287 {
288  DBUG_PRINT("info", ("Gcalc_operation_transporter::single_point: %g %g", x, y));
289  gcalc_shape_info si;
290  return m_fn->single_shape_op(Gcalc_function::shape_point, &si) ||
291  int_single_point(si, x, y);
292 }
293 
294 
295 int Gcalc_operation_transporter::start_line(Gcalc_shape_status *st)
296 {
297  DBUG_PRINT("info", ("Gcalc_operation_transporter::start_line"));
298  int_start_line();
299  return m_fn->single_shape_op(Gcalc_function::shape_line, &m_si);
300 }
301 
302 
303 int Gcalc_operation_transporter::complete_line(Gcalc_shape_status *st)
304 {
305  DBUG_PRINT("info", ("Gcalc_operation_transporter::complete_line"));
306  int_complete_line();
307  return 0;
308 }
309 
310 
311 int Gcalc_operation_transporter::start_poly(Gcalc_shape_status *st)
312 {
313  DBUG_PRINT("info", ("Gcalc_operation_transporter::start_poly"));
314  int_start_poly();
315  return m_fn->single_shape_op(Gcalc_function::shape_polygon, &m_si);
316 }
317 
318 
319 int Gcalc_operation_transporter::complete_poly(Gcalc_shape_status *st)
320 {
321  DBUG_PRINT("info", ("Gcalc_operation_transporter::complete_poly"));
322  int_complete_poly();
323  return 0;
324 }
325 
326 
327 int Gcalc_operation_transporter::start_ring(Gcalc_shape_status *st)
328 {
329  DBUG_PRINT("info", ("Gcalc_operation_transporter::start_ring"));
330  int_start_ring();
331  return 0;
332 }
333 
334 
335 int Gcalc_operation_transporter::complete_ring(Gcalc_shape_status *st)
336 {
337  DBUG_PRINT("info", ("Gcalc_operation_transporter::complete_ring"));
338  int_complete_ring();
339  return 0;
340 }
341 
342 
343 int Gcalc_operation_transporter::add_point(Gcalc_shape_status *st,
344  double x, double y)
345 {
346  DBUG_PRINT("info", ("Gcalc_operation_transporter::add_point %g %g", x, y));
347  return int_add_point(m_si, x, y);
348 }
349 
350 
351 int Gcalc_operation_transporter::start_collection(Gcalc_shape_status *st,
352  int n_objects)
353 {
354  DBUG_PRINT("info", ("Gcalc_operation_transporter::start_collection"));
355  if (m_fn->reserve_shape_buffer(n_objects) || m_fn->reserve_op_buffer(1))
356  return 1;
357  m_fn->add_operation(Gcalc_function::op_union, n_objects);
358  return 0;
359 }
360 
361 
362 int Gcalc_operation_transporter::complete_collection(Gcalc_shape_status *st)
363 {
364  DBUG_PRINT("info", ("Gcalc_operation_transporter::complete_collection"));
365  return 0;
366 }
367 
368 
369 int Gcalc_operation_transporter::collection_add_item(Gcalc_shape_status
370  *st_collection,
372  *st_item)
373 {
374  DBUG_PRINT("info", ("Gcalc_operation_transporter::collection_add_item"));
375  return 0;
376 }
377 
378 
379 int Gcalc_result_receiver::start_shape(Gcalc_function::shape_type shape)
380 {
381  DBUG_ENTER("Gcalc_result_receiver::start_shape");
382  DBUG_PRINT("info", ("%s", Gcalc_function::shape_name(shape)));
383  if (buffer.reserve(4*2, 512))
384  DBUG_RETURN(1);
385  cur_shape= shape;
386  shape_pos= buffer.length();
387  buffer.length(shape_pos + ((shape == Gcalc_function::shape_point) ? 4:8));
388  n_points= 0;
389  shape_area= 0.0;
390 
391  DBUG_RETURN(0);
392 }
393 
394 
395 int Gcalc_result_receiver::add_point(double x, double y)
396 {
397  DBUG_ENTER("Gcalc_result_receiver::add_point");
398  DBUG_PRINT("info", ("xy=(%g,%g)", x, y));
399  if (n_points && x == prev_x && y == prev_y)
400  DBUG_RETURN(0);
401 
402  if (!n_points++)
403  {
404  prev_x= first_x= x;
405  prev_y= first_y= y;
406  DBUG_RETURN(0);
407  }
408 
409  shape_area+= prev_x*y - prev_y*x;
410 
411  if (buffer.reserve(8*2, 512))
412  DBUG_RETURN(1);
413  buffer.q_append(prev_x);
414  buffer.q_append(prev_y);
415  prev_x= x;
416  prev_y= y;
417  DBUG_RETURN(0);
418 }
419 
420 
421 int Gcalc_result_receiver::complete_shape()
422 {
423  DBUG_ENTER("Gcalc_result_receiver::complete_shape");
424  DBUG_PRINT("info", ("n_points=%u", (uint) n_points));
425  if (n_points == 0)
426  {
427  buffer.length(shape_pos);
428  DBUG_RETURN(0);
429  }
430  if (n_points == 1)
431  {
432  if (cur_shape == Gcalc_function::shape_hole ||
433  cur_shape == Gcalc_function::shape_polygon)
434  {
435  /*
436  All points of a hole (or a polygon) have the same
437  coordinates - remove the shape.
438  */
439  buffer.length(shape_pos);
440  DBUG_RETURN(0);
441  }
442  if (cur_shape != Gcalc_function::shape_point)
443  {
444  cur_shape= Gcalc_function::shape_point;
445  buffer.length(buffer.length()-4);
446  }
447  }
448  else
449  {
450  DBUG_ASSERT(cur_shape != Gcalc_function::shape_point);
451  if (cur_shape == Gcalc_function::shape_hole ||
452  cur_shape == Gcalc_function::shape_polygon)
453  {
454  shape_area+= prev_x*first_y - prev_y*first_x;
455  /* Remove a hole (or a polygon) if its area == 0. */
456  if (fabs(shape_area) < 1e-8)
457  {
458  buffer.length(shape_pos);
459  DBUG_RETURN(0);
460  }
461  if (prev_x == first_x && prev_y == first_y)
462  {
463  n_points--;
464  buffer.write_at_position(shape_pos + 4, n_points);
465  goto do_complete;
466  }
467  }
468  buffer.write_at_position(shape_pos+4, n_points);
469  }
470 
471  if (buffer.reserve(8*2, 512))
472  DBUG_RETURN(1);
473  buffer.q_append(prev_x);
474  buffer.q_append(prev_y);
475 
476 do_complete:
477  buffer.write_at_position(shape_pos, (uint32) cur_shape);
478 
479  if (!n_shapes++)
480  {
481  DBUG_ASSERT(cur_shape != Gcalc_function::shape_hole);
482  common_shapetype= cur_shape;
483  }
484  else if (cur_shape == Gcalc_function::shape_hole)
485  {
486  ++n_holes;
487  }
488  else if (!collection_result && (cur_shape != common_shapetype))
489  {
490  collection_result= true;
491  }
492  DBUG_PRINT("info",
493  ("n_shapes=%u cur_shape=%s common_shapetype=%s",
494  (uint) n_shapes, Gcalc_function::shape_name(cur_shape),
495  Gcalc_function::shape_name(common_shapetype)));
496  DBUG_RETURN(0);
497 }
498 
499 
500 int Gcalc_result_receiver::single_point(double x, double y)
501 {
502  DBUG_PRINT("info", ("single_point xy=(%g,%g)", x, y));
503  return start_shape(Gcalc_function::shape_point) ||
504  add_point(x, y) ||
505  complete_shape();
506 }
507 
508 
509 int Gcalc_result_receiver::done()
510 {
511  DBUG_ENTER("Gcalc_result_receiver::done");
512  DBUG_RETURN(0);
513 }
514 
515 
516 void Gcalc_result_receiver::reset()
517 {
518  DBUG_ENTER("Gcalc_result_receiver::reset");
519  buffer.length(0);
520  collection_result= FALSE;
521  n_shapes= n_holes= 0;
522  DBUG_VOID_RETURN;
523 }
524 
525 
526 int Gcalc_result_receiver::get_result_typeid()
527 {
528  DBUG_ENTER("Gcalc_result_receiver::get_result_typeid");
529  if (!n_shapes)
530  DBUG_RETURN(0);
531 
532  if (collection_result)
533  DBUG_RETURN(Geometry::wkb_geometrycollection);
534  switch (common_shapetype)
535  {
536  case Gcalc_function::shape_polygon:
537  DBUG_RETURN((n_shapes - n_holes == 1) ? Geometry::wkb_polygon :
538  Geometry::wkb_multipolygon);
539  case Gcalc_function::shape_point:
540  DBUG_RETURN((n_shapes == 1) ? Geometry::wkb_point :
541  Geometry::wkb_multipoint);
542  case Gcalc_function::shape_line:
543  DBUG_RETURN((n_shapes == 1) ? Geometry::wkb_linestring :
544  Geometry::wkb_multilinestring);
545  default:
546  DBUG_ASSERT(0);
547  }
548  DBUG_RETURN(0);
549 }
550 
551 
552 Gcalc_operation_reducer::Gcalc_operation_reducer(size_t blk_size) :
553  Gcalc_dyn_list(blk_size, sizeof(res_point)),
554  m_res_hook((Gcalc_dyn_list::Item **)&m_result),
555  m_first_active_thread(NULL)
556 {
557  // We use sizeof(res_point) in constructor, the other items must be smaller
558  DBUG_ASSERT(sizeof(res_point) >= sizeof(active_thread));
559 }
560 
561 
562 void Gcalc_operation_reducer::init(Gcalc_function *fn, modes mode)
563 {
564  DBUG_ENTER("Gcalc_result_receiver::init");
565  m_fn= fn;
566  m_mode= mode;
567  m_first_active_thread= NULL;
568  DBUG_VOID_RETURN;
569 }
570 
571 
572 Gcalc_operation_reducer::
573 Gcalc_operation_reducer(Gcalc_function *fn, modes mode, size_t blk_size) :
574  Gcalc_dyn_list(blk_size, sizeof(res_point)),
575  m_res_hook((Gcalc_dyn_list::Item **)&m_result)
576 {
577  DBUG_ENTER("Gcalc_operation_reducer::Gcalc_operation_reducer");
578  init(fn, mode);
579  DBUG_VOID_RETURN;
580 }
581 
582 
583 inline int Gcalc_operation_reducer::continue_range(active_thread *t,
584  const Gcalc_heap::Info *p)
585 {
586  DBUG_ENTER("Gcalc_operation_reducer::continue_range");
587  DBUG_ASSERT(t->result_range);
588  res_point *rp= add_res_point(p);
589  if (!rp)
590  DBUG_RETURN(1);
591  rp->glue= NULL;
592  rp->down= t->rp;
593  t->rp->up= rp;
594  t->rp= rp;
595  DBUG_RETURN(0);
596 }
597 
598 
599 inline int Gcalc_operation_reducer::continue_i_range(active_thread *t,
600  const Gcalc_heap::Info *p,
601  double x, double y)
602 {
603  DBUG_ENTER("Gcalc_operation_reducer::continue_i_range");
604  DBUG_ASSERT(t->result_range);
605  res_point *rp= add_res_i_point(p, x, y);
606  if (!rp)
607  DBUG_RETURN(1);
608  rp->glue= NULL;
609  rp->down= t->rp;
610  t->rp->up= rp;
611  t->rp= rp;
612  DBUG_RETURN(0);
613 }
614 
615 inline int Gcalc_operation_reducer::start_range(active_thread *t,
616  const Gcalc_heap::Info *p)
617 {
618  DBUG_ENTER("Gcalc_operation_reducer::start_range");
619  res_point *rp= add_res_point(p);
620  if (!rp)
621  DBUG_RETURN(1);
622  rp->glue= rp->down= NULL;
623  t->result_range= 1;
624  t->rp= rp;
625  DBUG_RETURN(0);
626 }
627 
628 inline int Gcalc_operation_reducer::start_i_range(active_thread *t,
629  const Gcalc_heap::Info *p,
630  double x, double y)
631 {
632  DBUG_ENTER("Gcalc_operation_reducer::start_i_range");
633  res_point *rp= add_res_i_point(p, x, y);
634  if (!rp)
635  DBUG_RETURN(1);
636  rp->glue= rp->down= NULL;
637  t->result_range= 1;
638  t->rp= rp;
639  DBUG_RETURN(0);
640 }
641 
642 inline int Gcalc_operation_reducer::end_range(active_thread *t,
643  const Gcalc_heap::Info *p)
644 {
645  DBUG_ENTER("Gcalc_operation_reducer::end_range");
646  res_point *rp= add_res_point(p);
647  if (!rp)
648  DBUG_RETURN(1);
649  rp->glue= rp->up= NULL;
650  rp->down= t->rp;
651  t->rp->up= rp;
652  t->result_range= 0;
653  DBUG_RETURN(0);
654 }
655 
656 inline int Gcalc_operation_reducer::end_i_range(active_thread *t,
657  const Gcalc_heap::Info *p,
658  double x, double y)
659 {
660  DBUG_ENTER("Gcalc_operation_reducer::end_i_range");
661  res_point *rp= add_res_i_point(p, x, y);
662  if (!rp)
663  DBUG_RETURN(1);
664  rp->glue= rp->up= NULL;
665  rp->down= t->rp;
666  t->rp->up= rp;
667  t->result_range= 0;
668  DBUG_RETURN(0);
669 }
670 
671 int Gcalc_operation_reducer::start_couple(active_thread *t0, active_thread *t1,
672  const Gcalc_heap::Info *p,
673  const active_thread *prev_range)
674 {
675  DBUG_ENTER("Gcalc_operation_reducer::start_couple");
676  res_point *rp0, *rp1;
677  if (!(rp0= add_res_point(p)) || !(rp1= add_res_point(p)))
678  DBUG_RETURN(1);
679  rp0->glue= rp1;
680  rp1->glue= rp0;
681  rp0->down= rp1->down= NULL;
682  t0->rp= rp0;
683  t1->rp= rp1;
684  if (prev_range)
685  {
686  rp0->set_outer_poly(prev_range->thread_start());
687  t1->set_thread_start(prev_range->thread_start());
688  }
689  else
690  {
691  rp0->set_outer_poly(NULL);
692  t0->set_thread_start(rp0);
693  }
694  DBUG_RETURN(0);
695 }
696 
697 int Gcalc_operation_reducer::start_i_couple(active_thread *t0, active_thread *t1,
698  const Gcalc_heap::Info *p0,
699  const Gcalc_heap::Info *p1,
700  double x, double y,
701  const active_thread *prev_range)
702 {
703  DBUG_ENTER("Gcalc_operation_reducer::start_i_couple");
704  res_point *rp0, *rp1;
705  if (!(rp0= add_res_i_point(p0, x, y)) || !(rp1= add_res_i_point(p1, x, y)))
706  DBUG_RETURN(1);
707  rp0->glue= rp1;
708  rp1->glue= rp0;
709  rp0->down= rp1->down= NULL;
710  t0->result_range= t1->result_range= 1;
711  t0->rp= rp0;
712  t1->rp= rp1;
713  if (prev_range)
714  {
715  rp0->set_outer_poly(prev_range->thread_start());
716  t1->set_thread_start(prev_range->thread_start());
717  }
718  else
719  {
720  rp0->set_outer_poly(NULL);
721  t0->set_thread_start(rp0);
722  }
723  DBUG_RETURN(0);
724 }
725 
726 int Gcalc_operation_reducer::end_couple(active_thread *t0, active_thread *t1,
727  const Gcalc_heap::Info *p)
728 {
729  DBUG_ENTER("Gcalc_operation_reducer::end_couple");
730  res_point *rp0, *rp1;
731  DBUG_ASSERT(t1->result_range);
732  if (!(rp0= add_res_point(p)) || !(rp1= add_res_point(p)))
733  DBUG_RETURN(1);
734  rp0->down= t0->rp;
735  rp1->down= t1->rp;
736  rp1->glue= rp0;
737  rp0->glue= rp1;
738  rp0->up= rp1->up= NULL;
739  t0->rp->up= rp0;
740  t1->rp->up= rp1;
741  t0->result_range= t1->result_range= 0;
742  DBUG_RETURN(0);
743 }
744 
745 int Gcalc_operation_reducer::end_i_couple(active_thread *t0, active_thread *t1,
746  const Gcalc_heap::Info *p0,
747  const Gcalc_heap::Info *p1,
748  double x, double y)
749 {
750  DBUG_ENTER("Gcalc_operation_reducer::end_i_couple");
751  res_point *rp0, *rp1;
752  if (!(rp0= add_res_i_point(p0, x, y)) || !(rp1= add_res_i_point(p1, x, y)))
753  DBUG_RETURN(1);
754  rp0->down= t0->rp;
755  rp1->down= t1->rp;
756  rp1->glue= rp0;
757  rp0->glue= rp1;
758  rp0->up= rp1->up= NULL;
759  t0->result_range= t1->result_range= 0;
760  t0->rp->up= rp0;
761  t1->rp->up= rp1;
762  DBUG_RETURN(0);
763 }
764 
765 int Gcalc_operation_reducer::add_single_point(const Gcalc_heap::Info *p)
766 {
767  DBUG_ENTER("Gcalc_operation_reducer::add_single_point");
768  res_point *rp= add_res_single_point(p);
769  if (!rp)
770  DBUG_RETURN(1);
771  rp->glue= rp->up= rp->down= NULL;
772  DBUG_RETURN(0);
773 }
774 
775 int Gcalc_operation_reducer::add_i_single_point(const Gcalc_heap::Info *p,
776  double x, double y)
777 {
778  DBUG_ENTER("Gcalc_operation_reducer::add_i_single_point");
779  res_point *rp= add_res_i_point(p, x, y);
780  if (!rp)
781  DBUG_RETURN(1);
782  rp->glue= rp->up= rp->down= NULL;
783  DBUG_RETURN(0);
784 }
785 
786 int Gcalc_operation_reducer::
787 handle_lines_intersection(active_thread *t0, active_thread *t1,
788  const Gcalc_heap::Info *p0, const Gcalc_heap::Info *p1,
789  double x, double y)
790 {
791  DBUG_ENTER("Gcalc_operation_reducer::handle_lines_intersection");
792  DBUG_PRINT("info", ("p=(%g,%g,#%u) p1=(%g,%g,#%u) xy=(%g,%g)",
793  p0->x, p0->y, p0->shape, p1->x, p1->y, p1->shape, x, y));
794  m_fn->invert_state(p0->shape);
795  m_fn->invert_state(p1->shape);
796  int intersection_state= m_fn->count();
797  if ((t0->result_range | t1->result_range) == intersection_state)
798  DBUG_RETURN(0);
799 
800  if (t0->result_range &&
801  (end_i_range(t0, p1, x, y) || start_i_range(t0, p1, x, y)))
802  DBUG_RETURN(1);
803 
804  if (t1->result_range &&
805  (end_i_range(t1, p0, x, y) || start_i_range(t1, p0, x, y)))
806  DBUG_RETURN(1);
807 
808  if (intersection_state &&
809  add_i_single_point(p0, x, y))
810  DBUG_RETURN(1);
811 
812  DBUG_RETURN(0);
813 }
814 
815 inline int Gcalc_operation_reducer::
816 handle_line_polygon_intersection(active_thread *l, const Gcalc_heap::Info *pl,
817  int line_state, int poly_state,
818  double x, double y)
819 {
820  DBUG_ENTER("Gcalc_operation_reducer::handle_line_polygon_intersection");
821  DBUG_PRINT("info", ("p=(%g,%g,#%u) xy=(%g,%g)",
822  pl->x, pl->y, pl->shape, x, y));
823  int range_after= ~poly_state & line_state;
824  if (l->result_range == range_after)
825  DBUG_RETURN(0);
826  DBUG_RETURN(range_after ? start_i_range(l, pl, x, y) :
827  end_i_range(l, pl, x, y));
828 }
829 
830 static inline void switch_athreads(Gcalc_operation_reducer::active_thread *t0,
832  Gcalc_dyn_list::Item **hook)
833 {
834  *hook= t1;
835  t0->next= t1->next;
836  t1->next= t0;
837 }
838 
839 inline int Gcalc_operation_reducer::
840 handle_polygons_intersection(active_thread *t0, active_thread *t1,
841  Gcalc_dyn_list::Item **t_hook,
842  const Gcalc_heap::Info *p0,
843  const Gcalc_heap::Info *p1,
844  int prev_state, double x, double y,
845  const active_thread *prev_range)
846 {
847  DBUG_ENTER("Gcalc_operation_reducer::handle_polygons_intersection");
848  DBUG_PRINT("info", ("p0=(%g,%g,#%u) p1=(%g,%g,#%u) xy=(%g,%g)",
849  p0->x, p0->y, p0->shape, p1->x, p1->y, p1->shape, x, y));
850  m_fn->invert_state(p0->shape);
851  int state_11= m_fn->count();
852  m_fn->invert_state(p1->shape);
853  int state_2= m_fn->count();
854  int state_01= prev_state ^ t0->result_range;
855  if ((prev_state == state_01) && (prev_state == state_2))
856  {
857  if (state_11 == prev_state)
858  {
859  switch_athreads(t0, t1, t_hook);
860  DBUG_RETURN(0);
861  }
862  DBUG_RETURN(start_i_couple(t0, t1, p0, p1, x, y, prev_range));
863  }
864  if (prev_state == state_2)
865  {
866  if (state_01 == state_11)
867  {
868  if (m_mode & polygon_selfintersections_allowed)
869  {
870  switch_athreads(t0, t1, t_hook);
871  DBUG_RETURN(0);
872  }
873  if (prev_state != (m_mode & prefer_big_with_holes))
874  DBUG_RETURN(continue_i_range(t0, p0, x, y) ||
875  continue_i_range(t1, p1, x, y));
876  DBUG_RETURN(end_i_couple(t0, t1, p0, p1, x, y) ||
877  start_i_couple(t0, t1, p0, p1, x, y, prev_range));
878  }
879  else
880  DBUG_RETURN(end_i_couple(t0, t1, p0, p1, x, y));
881  }
882  if (state_01 ^ state_11)
883  {
884  switch_athreads(t0, t1, t_hook);
885  DBUG_RETURN(0);
886  }
887 
888  active_thread *thread_to_continue;
889  const Gcalc_heap::Info *way_to_go;
890  if (prev_state == state_01)
891  {
892  thread_to_continue= t1;
893  way_to_go= p1;
894  }
895  else
896  {
897  thread_to_continue= t0;
898  way_to_go= p0;
899  }
900  DBUG_RETURN(continue_i_range(thread_to_continue, way_to_go, x, y));
901 }
902 
903 int Gcalc_operation_reducer::count_slice(Gcalc_scan_iterator *si)
904 {
905  DBUG_ENTER("Gcalc_operation_reducer::count_slice");
906  Gcalc_point_iterator pi(si);
907  active_thread *cur_t= m_first_active_thread;
908  Gcalc_dyn_list::Item **at_hook= (Gcalc_dyn_list::Item **)&m_first_active_thread;
909  const active_thread *prev_range;
910  int prev_state;
911 
912  if (si->get_event() & (scev_point | scev_end | scev_two_ends))
913  {
914  for (; pi.point() != si->get_event_position(); ++pi, cur_t= cur_t->get_next())
915  at_hook= &cur_t->next;
916 
917  switch (si->get_event())
918  {
919  case scev_point:
920  {
921  DBUG_PRINT("Gcalc_operation_reducer", ("event=scev_point"));
922  if (cur_t->result_range &&
923  continue_range(cur_t, pi.get_pi()))
924  DBUG_RETURN(1);
925  break;
926  }
927  case scev_end:
928  {
929  DBUG_PRINT("Gcalc_operation_reducer", ("event=scev_end"));
930  if (cur_t->result_range &&
931  end_range(cur_t, pi.get_pi()))
932  DBUG_RETURN(1);
933  *at_hook= cur_t->next;
934  free_item(cur_t);
935  break;
936  }
937  case scev_two_ends:
938  {
939  DBUG_PRINT("Gcalc_operation_reducer", ("event=scev_two_ends"));
940  active_thread *cur_t1= cur_t->get_next();
941  if (cur_t->result_range &&
942  end_couple(cur_t, cur_t1, pi.get_pi()))
943  DBUG_RETURN(1);
944 
945  *at_hook= cur_t1->next;
946  free_list(cur_t, &cur_t1->next);
947  break;
948  }
949  default:
950  DBUG_ASSERT(0);
951  }
952  DBUG_RETURN(0);
953  }
954 
955  prev_state= 0;
956  prev_range= 0;
957 
958  m_fn->clear_state();
959  for (; pi.point() != si->get_event_position(); ++pi, cur_t= cur_t->get_next())
960  {
961  if (m_fn->get_shape_kind(pi.get_shape()) == Gcalc_function::shape_polygon)
962  {
963  m_fn->invert_state(pi.get_shape());
964  prev_state^= cur_t->result_range;
965  }
966  at_hook= &cur_t->next;
967  if (cur_t->result_range)
968  prev_range= prev_state ? cur_t : 0;
969  }
970 
971  switch (si->get_event())
972  {
973  case scev_thread:
974  {
975  DBUG_PRINT("info", ("event=scev_thread"));
976  active_thread *new_t= new_active_thread();
977  if (!new_t)
978  DBUG_RETURN(1);
979  m_fn->invert_state(pi.get_shape());
980  new_t->result_range= prev_state ^ m_fn->count();
981  new_t->next= *at_hook;
982  *at_hook= new_t;
983  if (new_t->result_range &&
984  start_range(new_t, pi.get_pi()))
985  DBUG_RETURN(1);
986  break;
987  }
988  case scev_two_threads:
989  {
990  DBUG_PRINT("info", ("event=scev_two_threads"));
991  active_thread *new_t0, *new_t1;
992  int fn_result;
993  if (!(new_t0= new_active_thread()) || !(new_t1= new_active_thread()))
994  DBUG_RETURN(1);
995 
996  m_fn->invert_state(pi.get_shape());
997  fn_result= m_fn->count();
998  new_t0->result_range= new_t1->result_range= prev_state ^ fn_result;
999  new_t1->next= *at_hook;
1000  new_t0->next= new_t1;
1001  *at_hook= new_t0;
1002  if (new_t0->result_range &&
1003  start_couple(new_t0, new_t1, pi.get_pi(), prev_range))
1004  DBUG_RETURN(1);
1005  break;
1006  }
1007  case scev_intersection:
1008  {
1009  DBUG_PRINT("info", ("event=scev_intersection"));
1010  active_thread *cur_t1= cur_t->get_next();
1011  const Gcalc_heap::Info *p0, *p1;
1012  p0= pi.get_pi();
1013  ++pi;
1014  p1= pi.get_pi();
1015  bool line0= m_fn->get_shape_kind(p0->shape) == Gcalc_function::shape_line;
1016  bool line1= m_fn->get_shape_kind(p1->shape) == Gcalc_function::shape_line;
1017 
1018  if (!line0 && !line1) /* two polygons*/
1019  {
1020  if (handle_polygons_intersection(cur_t, cur_t1, at_hook, p0, p1,
1021  prev_state, pi.get_x(), si->get_y(),
1022  prev_range))
1023  DBUG_RETURN(1);
1024  }
1025  else if (line0 && line1)
1026  {
1027  if (!prev_state &&
1028  handle_lines_intersection(cur_t, cur_t1,
1029  p0, p1, pi.get_x(), si->get_y()))
1030  DBUG_RETURN(1);
1031  switch_athreads(cur_t, cur_t1, at_hook);
1032  }
1033  else
1034  {
1035  int poly_state;
1036  int line_state;
1037  const Gcalc_heap::Info *line;
1038  active_thread *line_t;
1039  m_fn->invert_state(p0->shape);
1040  if (line0)
1041  {
1042  line_state= m_fn->count();
1043  poly_state= prev_state;
1044  line= p0;
1045  line_t= cur_t1;
1046  }
1047  else
1048  {
1049  poly_state= m_fn->count();
1050  m_fn->invert_state(p1->shape);
1051  line_state= m_fn->count();
1052  line= p1;
1053  line_t= cur_t;
1054  }
1055  if (handle_line_polygon_intersection(line_t, line,
1056  line_state, poly_state,
1057  pi.get_x(), si->get_y()))
1058  DBUG_RETURN(1);
1059  switch_athreads(cur_t, cur_t1, at_hook);
1060  }
1061  break;
1062  }
1063  case scev_single_point:
1064  {
1065  DBUG_PRINT("info", ("event=scev_single_point"));
1066  m_fn->invert_state(pi.get_shape());
1067  if ((prev_state ^ m_fn->count()) &&
1068  add_single_point(pi.get_pi()))
1069  DBUG_RETURN(1);
1070  break;
1071  }
1072  default:
1073  DBUG_ASSERT(0);
1074  }
1075 
1076  DBUG_RETURN(0);
1077 }
1078 
1079 int Gcalc_operation_reducer::count_all(Gcalc_heap *hp)
1080 {
1081  DBUG_ENTER("Gcalc_operation_reducer::count_all");
1083  si.init(hp);
1084  while (si.more_points())
1085  {
1086  if (si.step())
1087  DBUG_RETURN(1);
1088  if (count_slice(&si))
1089  DBUG_RETURN(1);
1090  }
1091  DBUG_RETURN(0);
1092 }
1093 
1094 inline void Gcalc_operation_reducer::free_result(res_point *res)
1095 {
1096  DBUG_ENTER("Gcalc_result_receiver::free_result");
1097  if ((*res->prev_hook= res->next))
1098  {
1099  res->get_next()->prev_hook= res->prev_hook;
1100  }
1101  free_item(res);
1102  DBUG_VOID_RETURN;
1103 }
1104 
1105 
1106 inline int Gcalc_operation_reducer::get_single_result(res_point *res,
1107  Gcalc_result_receiver *storage)
1108 {
1109  DBUG_ENTER("Gcalc_operation_reducer::get_single_result");
1110  if (res->intersection_point)
1111  {
1112  if (storage->single_point(float_to_coord(res->x),
1113  float_to_coord(res->y)))
1114  DBUG_RETURN(1);
1115  }
1116  else
1117  if (storage->single_point(res->x, res->y))
1118  DBUG_RETURN(1);
1119  free_result(res);
1120  DBUG_RETURN(0);
1121 }
1122 
1123 
1124 int Gcalc_operation_reducer::get_result_thread(res_point *cur,
1125  Gcalc_result_receiver *storage,
1126  int move_upward)
1127 {
1128  DBUG_ENTER("Gcalc_operation_reducer::get_result_thread");
1129  res_point *next;
1130  bool glue_step= false;
1131  res_point *first_poly_node= cur;
1132  double x, y;
1133  while (cur)
1134  {
1135  if (!glue_step)
1136  {
1137  if (cur->intersection_point)
1138  {
1139  x= float_to_coord(cur->x);
1140  y= float_to_coord(cur->y);
1141  }
1142  else
1143  {
1144  x= cur->pi->x;
1145  y= cur->pi->y;
1146  }
1147  if (storage->add_point(x, y))
1148  DBUG_RETURN(1);
1149  }
1150 
1151  next= move_upward ? cur->up : cur->down;
1152  if (!next && !glue_step)
1153  {
1154  next= cur->glue;
1155  move_upward^= 1;
1156  glue_step= true;
1157  if (next)
1158  next->glue= NULL;
1159  }
1160  else
1161  glue_step= false;
1162 
1163  cur->first_poly_node= first_poly_node;
1164  free_result(cur);
1165  cur= next;
1166  }
1167  DBUG_RETURN(0);
1168 }
1169 
1170 
1171 int Gcalc_operation_reducer::get_polygon_result(res_point *cur,
1172  Gcalc_result_receiver *storage)
1173 {
1174  DBUG_ENTER("Gcalc_operation_reducer::get_polygon_result");
1175  res_point *glue= cur->glue;
1176  glue->up->down= NULL;
1177  free_result(glue);
1178  DBUG_RETURN(get_result_thread(cur, storage, 1) ||
1179  storage->complete_shape());
1180 }
1181 
1182 
1183 int Gcalc_operation_reducer::get_line_result(res_point *cur,
1184  Gcalc_result_receiver *storage)
1185 {
1186  DBUG_ENTER("Gcalc_operation_reducer::get_line_result");
1187  res_point *next;
1188  int move_upward= 1;
1189  if (cur->glue)
1190  {
1191  /* Here we have to find the beginning of the line */
1192  next= cur->up;
1193  move_upward= 1;
1194  while (next)
1195  {
1196  cur= next;
1197  next= move_upward ? next->up : next->down;
1198  if (!next)
1199  {
1200  next= cur->glue;
1201  move_upward^= 1;
1202  }
1203  }
1204  }
1205 
1206  DBUG_RETURN(get_result_thread(cur, storage, move_upward) ||
1207  storage->complete_shape());
1208 }
1209 
1210 
1211 static int chunk_info_cmp(const Gcalc_result_receiver::chunk_info *a1,
1213 {
1214  if (a1->first_point != a2->first_point)
1215  return a1->first_point < a2->first_point ? -1 : 1;
1216  if (a1->is_poly_hole != a2->is_poly_hole)
1217  return a1->is_poly_hole < a2->is_poly_hole ? -1 : 1;
1218  return (int) a1->order - (int) a2->order;
1219 }
1220 
1221 
1222 #ifndef DBUG_OFF
1223 void Gcalc_result_receiver::chunk_info::dbug_print() const
1224 {
1225  DBUG_PRINT("info", ("first_point=%p order=%d position=%d length=%d",
1226  first_point, (int) order, (int) position, (int) length));
1227 }
1228 #endif
1229 
1230 
1234 int Gcalc_result_receiver::reorder_chunks(chunk_info *chunks, int nchunks)
1235 {
1236  DBUG_ENTER("Gcalc_result_receiver::sort_polygon_rings");
1237 
1238  String tmp;
1239  uint32 reserve_length= buffer.length();
1240  if (tmp.reserve(reserve_length, MY_ALIGN(reserve_length, 512)))
1241  DBUG_RETURN(1);
1242 
1243  // Put shape data in the required order
1244  for (chunk_info *chunk= chunks, *end= chunks + nchunks; chunk < end; chunk++)
1245  {
1246 #ifndef DBUG_OFF
1247  chunk->dbug_print();
1248 #endif
1249  tmp.append(buffer.ptr() + chunk->position, (size_t) chunk->length);
1250  }
1251  // Make sure all chunks were put
1252  DBUG_ASSERT(tmp.length() == buffer.length());
1253  // Get all data from tmp and unlink tmp from its buffer.
1254  buffer.takeover(tmp);
1255  DBUG_RETURN(0);
1256 }
1257 
1258 
1259 int Gcalc_operation_reducer::get_result(Gcalc_result_receiver *storage)
1260 {
1261  DBUG_ENTER("Gcalc_operation_reducer::get_result");
1263  bool polygons_found= false;
1264 
1265  *m_res_hook= NULL;
1266  while (m_result)
1267  {
1268  Gcalc_function::shape_type shape;
1270 
1271  chunk.first_point= m_result;
1272  chunk.order= chunks.elements();
1273  chunk.position= storage->position();
1274  chunk.is_poly_hole= false;
1275 
1276  if (!m_result->up)
1277  {
1278  if (get_single_result(m_result, storage))
1279  DBUG_RETURN(1);
1280  goto end_shape;
1281  }
1282 
1283  shape= m_fn->get_shape_kind(m_result->pi->shape);
1284  if (shape == Gcalc_function::shape_polygon)
1285  {
1286  polygons_found= true;
1287  if (m_result->get_outer_poly()) // Inner ring (hole)
1288  {
1289  chunk.first_point= m_result->get_outer_poly();
1290  chunk.is_poly_hole= true;
1291  shape= Gcalc_function::shape_hole;
1292  }
1293  storage->start_shape(shape);
1294  if (get_polygon_result(m_result, storage))
1295  DBUG_RETURN(1);
1296  chunk.first_point= ((res_point*) chunk.first_point)->first_poly_node;
1297  }
1298  else
1299  {
1300  storage->start_shape(shape);
1301  if (get_line_result(m_result, storage))
1302  DBUG_RETURN(1);
1303  }
1304 
1305 end_shape:
1306  chunk.length= storage->position() - chunk.position;
1307  chunks.append(chunk);
1308  }
1309 
1310  /*
1311  In case if some polygons where found, we need to reorder polygon rings
1312  in the output buffer to make all hole rings go after their outer rings.
1313  */
1314  if (polygons_found && chunks.elements() > 1)
1315  {
1316  chunks.sort(chunk_info_cmp);
1317  if (storage->reorder_chunks(chunks.front(), chunks.elements()))
1318  DBUG_RETURN(1);
1319  }
1320 
1321  m_res_hook= (Gcalc_dyn_list::Item **)&m_result;
1322  storage->done();
1323  DBUG_RETURN(0);
1324 }
1325 
1326 
1327 void Gcalc_operation_reducer::reset()
1328 {
1329  DBUG_ENTER("Gcalc_operation_reducer::reset");
1330  free_list(m_result, m_res_hook);
1331  m_res_hook= (Gcalc_dyn_list::Item **)&m_result;
1332  free_list(m_first_active_thread);
1333  DBUG_VOID_RETURN;
1334 }
1335 
1336 #endif /*HAVE_SPATIAL*/
1337