List:Commits« Previous MessageNext Message »
From:Alexey Botchkov Date:December 20 2009 10:41am
Subject:bzr commit into mysql-5.1-bugteam branch (holyfoot:2665) Bug#45883
View as plain text  
#At file:///home/hf/work/mysql_common/gis-bugfix/ based on revid:holyfoot@stripped

 2665 Alexey Botchkov	2009-12-20
      Bug#45883      Buffer function crashes mysqld
        When two next nodes of an polygon have exactly same coordinates,
        the calculation of the buffer can crash.
      
      modified:
        sql/gcalc_slicescan.cc
      Bug#45883      Buffer function crashes mysqld
        sql/gcalc_slicescan.h
      Bug#45883      Buffer function crashes mysqld
        sql/gcalc_tools.cc
      Bug#45883      Buffer function crashes mysqld
        sql/gcalc_tools.h
      Bug#45883      Buffer function crashes mysqld
        sql/item_geofunc.cc
      Bug#45883      Buffer function crashes mysqld
        sql/item_geofunc.h
      Bug#45883      Buffer function crashes mysqld
        sql/spatial.cc
      Bug#45883      Buffer function crashes mysqld
        sql/spatial.h
      Bug#45883      Buffer function crashes mysqld

    modified:
      sql/gcalc_slicescan.cc
      sql/gcalc_slicescan.h
      sql/gcalc_tools.cc
      sql/gcalc_tools.h
      sql/item_geofunc.cc
      sql/item_geofunc.h
      sql/spatial.cc
      sql/spatial.h
=== modified file 'sql/gcalc_slicescan.cc'
--- a/sql/gcalc_slicescan.cc	2009-12-05 08:58:34 +0000
+++ b/sql/gcalc_slicescan.cc	2009-12-20 10:39:40 +0000
@@ -203,6 +203,18 @@ void gcalc_dyn_list::reset()
 }
 
 
+/* should be removed
+void gcalc_heap::free_point_info(gcalc_heap::info **pi_hook)
+{
+  DBUG_ASSERT(m_n_points);
+  gcalc_heap::info *pi= *pi_hook;
+  *pi_hook= pi->next;
+  free_item(pi);
+  --m_n_points;
+}
+*/
+
+
 static inline void trim_node(gcalc_heap::info *node, gcalc_heap::info *prev_node)
 {
   if (!node)
@@ -281,6 +293,66 @@ void gcalc_heap::reset()
   m_n_points= 0;
 }
 
+int gcalc_shape_transporter::int_single_point(gcalc_shape_info info,
+                                              double x, double y)
+{
+  gcalc_heap::info *point= m_heap->new_point_info(x, y, info);
+  if (!point)
+    return 1;
+  point->left= point->right= 0;
+  return 0;
+}
+
+
+int gcalc_shape_transporter::int_add_point(gcalc_shape_info info,
+                                           double x, double y)
+{
+  gcalc_heap::info *point;
+  GCALC_DBUG_ADD_POINT(x, y);
+  if (!(point= m_heap->new_point_info(x, y, info)))
+    return 1;
+  if (m_first)
+  {
+    m_prev->left= point;
+    point->right= m_prev;
+  }
+  else
+    m_first= point;
+  m_prev= point;
+  return 0;
+}
+
+
+void gcalc_shape_transporter::int_complete()
+{
+  DBUG_ASSERT(m_shape_started == 1 || m_shape_started == 3);
+  GCALC_DBUG_COMPLETE;
+
+  if (!m_first)
+    return;
+
+  /* simple point */
+  if (m_first == m_prev)
+  {
+    m_first->right= m_first->left= NULL;
+    return;
+  }
+
+  /* line */
+  if (m_shape_started == 1)
+  {
+    m_first->right= NULL;
+    m_prev->left= m_prev->right;
+    m_prev->right= NULL;
+    return;
+  }
+
+  /* polygon */
+  m_first->right= m_prev;
+  m_prev->left= m_first;
+}
+
+
 inline int GET_DX_DY(double *dxdy,
                      const gcalc_heap::info *p0, const gcalc_heap::info *p1)
 {

=== modified file 'sql/gcalc_slicescan.h'
--- a/sql/gcalc_slicescan.h	2009-12-05 08:58:34 +0000
+++ b/sql/gcalc_slicescan.h	2009-12-20 10:39:40 +0000
@@ -128,7 +128,7 @@ public:
 /*  void reset_keep(int (*keep_point)(sc_point_info info, const void *parameter),
 		  const void *parameter);
 */
-  info *new_point_info()
+  info *new_point_info(double x, double y, gcalc_shape_info shape)
   {
     info *result= (info *)new_item();
     if (!result)
@@ -136,8 +136,14 @@ public:
     *m_hook= result;
     m_hook= &result->next;
     m_n_points++;
+    result->x= x;
+    result->y= y;
+    result->shape= shape;
     return result;
   }
+  /* should be removed
+  void free_point_info(info **pi_hook);
+  */
   void prepare_operation();
   inline bool ready() const { return m_hook == NULL; }
   info *get_first() { return (info *)m_first; }
@@ -156,80 +162,74 @@ class gcalc_shape_transporter
 private:
   gcalc_heap::info *m_first;
   gcalc_heap::info *m_prev;
+  int m_shape_started;
+  void int_complete();
+protected:
   gcalc_heap *m_heap;
-  gcalc_shape_info m_shape_info;
-  bool m_line_flag;
-public:
-  gcalc_shape_transporter(gcalc_heap *heap) : m_heap(heap) {}
-
-  int single_point(gcalc_shape_info info, double x, double y)
-  {
-    gcalc_heap::info *point= m_heap->new_point_info();
-    if (!point)
-      return 1;
-    point->x= x;
-    point->y= y;
-    point->shape= info;
-    point->left= point->right= 0;
-    return 0;
-  }
-
-  void start_line(gcalc_shape_info info)
+  int int_single_point(gcalc_shape_info info, double x, double y);
+  int int_add_point(gcalc_shape_info info, double x, double y);
+  void int_start_line()
   {
-    m_shape_info= info;
+    DBUG_ASSERT(!m_shape_started);
+    m_shape_started= 1;
     m_first= m_prev= NULL;
-    m_line_flag= TRUE;
     GCALC_DBUG_START_LINE;
   }
-
-  void start_ring(gcalc_shape_info info)
+  void int_complete_line()
+  {
+    DBUG_ASSERT(m_shape_started== 1);
+    int_complete();
+    m_shape_started= 0;
+  }
+  void int_start_ring()
   {
-    m_shape_info= info;
+    DBUG_ASSERT(m_shape_started== 2);
+    m_shape_started= 3;
     m_first= m_prev= NULL;
-    m_line_flag= FALSE;
     GCALC_DBUG_START_RING;
   }
-
-  int add_point(double x, double y)
+  void int_complete_ring()
   {
-    gcalc_heap::info *point= m_heap->new_point_info();
-    if (!point)
-      return 1;
-    point->x= x;
-    point->y= y;
-    point->shape= m_shape_info;
-    if (m_first)
-    {
-      m_prev->left= point;
-      point->right= m_prev;
-    }
-    else
-      m_first= point;
-    m_prev= point;
-    GCALC_DBUG_ADD_POINT(x, y);
-    return 0;
+    DBUG_ASSERT(m_shape_started== 3);
+    int_complete();
+    m_shape_started= 2;
+  }
+  void int_start_poly()
+  {
+    DBUG_ASSERT(!m_shape_started);
+    m_shape_started= 2;
   }
+  void int_complete_poly()
+  {
+    DBUG_ASSERT(m_shape_started== 2);
+    m_shape_started= 0;
+  }
+  bool line_started() { return m_shape_started == 1; };
+public:
+  gcalc_shape_transporter(gcalc_heap *heap) :
+    m_shape_started(0), m_heap(heap) {}
 
-  void complete()
+  virtual int single_point(double x, double y)=0;
+  virtual int start_line()=0;
+  virtual int complete_line()=0;
+  virtual int start_poly()=0;
+  virtual int complete_poly()=0;
+  virtual int start_ring()=0;
+  virtual int complete_ring()=0;
+  virtual int add_point(double x, double y)=0;
+  virtual int start_collection(int n_objects) { return 0; }
+  int start_simple_poly()
   {
-    if (m_first)
-    {
-      if (m_line_flag)
-      {
-	m_first->right= NULL;
-	m_prev->left= m_prev->right;
-	m_prev->right= NULL;
-      }
-      else
-      {
-	m_first->right= m_prev;
-	m_prev->left= m_first;
-      }
-    }
-    GCALC_DBUG_COMPLETE;
+    return start_poly() || start_ring();
   }
+  int complete_simple_poly()
+  {
+    return complete_ring() || complete_poly();
+  }
+  virtual ~gcalc_shape_transporter() {}
 };
 
+
 enum gcalc_scan_events
 {
   scev_point= 1,         /* Just a new point in thread */

=== modified file 'sql/gcalc_tools.cc'
--- a/sql/gcalc_tools.cc	2009-12-05 08:58:34 +0000
+++ b/sql/gcalc_tools.cc	2009-12-20 10:39:40 +0000
@@ -155,6 +155,71 @@ int gcalc_function::find_function(gcalc_
 }
 
 
+int gcalc_operation_transporter::single_point(double x, double y)
+{
+  gcalc_shape_info si;
+  return m_fn->single_shape_op(gcalc_function::shape_point, &si) ||
+         int_single_point(si, x, y);
+}
+
+
+int gcalc_operation_transporter::start_line()
+{
+  int_start_line();
+  return m_fn->single_shape_op(gcalc_function::shape_line, &m_si);
+}
+
+
+int gcalc_operation_transporter::complete_line()
+{
+  int_complete_line();
+  return 0;
+}
+
+
+int gcalc_operation_transporter::start_poly()
+{
+  int_start_poly();
+  return m_fn->single_shape_op(gcalc_function::shape_polygon, &m_si);
+}
+
+
+int gcalc_operation_transporter::complete_poly()
+{
+  int_complete_poly();
+  return 0;
+}
+
+
+int gcalc_operation_transporter::start_ring()
+{
+  int_start_ring();
+  return 0;
+}
+
+
+int gcalc_operation_transporter::complete_ring()
+{
+  int_complete_ring();
+  return 0;
+}
+
+
+int gcalc_operation_transporter::add_point(double x, double y)
+{
+  return int_add_point(m_si, x, y);
+}
+
+
+int gcalc_operation_transporter::start_collection(int n_objects)
+{
+  if (m_fn->reserve_shape_buffer(n_objects) || m_fn->reserve_op_buffer(1))
+        return 1;
+  m_fn->add_operation(gcalc_function::op_union, n_objects);
+  return 0;
+}
+
+
 gcalc_result_receiver::gcalc_result_receiver() :
   collection_result(FALSE), n_shapes(0), n_holes(0)
 {}

=== modified file 'sql/gcalc_tools.h'
--- a/sql/gcalc_tools.h	2009-12-05 08:58:34 +0000
+++ b/sql/gcalc_tools.h	2009-12-20 10:39:40 +0000
@@ -65,6 +65,27 @@ public:
 };
 
 
+class gcalc_operation_transporter : public gcalc_shape_transporter
+{
+protected:
+  gcalc_function *m_fn;
+  gcalc_shape_info m_si;
+public:
+  gcalc_operation_transporter(gcalc_function *fn, gcalc_heap *heap) :
+    gcalc_shape_transporter(heap), m_fn(fn) {}
+
+  int single_point(double x, double y);
+  int start_line();
+  int complete_line();
+  int start_poly();
+  int complete_poly();
+  int start_ring();
+  int complete_ring();
+  int add_point(double x, double y);
+  int start_collection(int n_objects);
+};
+
+
 /* class to receive the result of the geometry operation */
 /* to store it in appropriate format                     */
 

=== modified file 'sql/item_geofunc.cc'
--- a/sql/item_geofunc.cc	2009-12-05 08:58:34 +0000
+++ b/sql/item_geofunc.cc	2009-12-20 10:39:40 +0000
@@ -641,6 +641,7 @@ longlong Item_func_spatial_rel::val_int(
   Geometry *g1, *g2;
   int result= 0;
   int mask= 0;
+  gcalc_operation_transporter trn(&func, &collector);
 
   if (func.reserve_op_buffer(1))
     DBUG_RETURN(0);
@@ -678,12 +679,10 @@ longlong Item_func_spatial_rel::val_int(
 
 
   if ((null_value=
-       (args[0]->null_value ||
-	args[1]->null_value ||
+       (args[0]->null_value || args[1]->null_value ||
 	!(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) ||
 	!(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())) ||
-	g1->store_shapes(&collector, &func) ||
-        g2->store_shapes(&collector, &func))))
+	g1->store_shapes(&trn) || g2->store_shapes(&trn))))
     goto exit;
 
   collector.prepare_operation();
@@ -722,18 +721,17 @@ String *Item_func_spatial_operation::val
   Geometry_buffer buffer1, buffer2;
   Geometry *g1, *g2;
   uint32 srid= 0;
+  gcalc_operation_transporter trn(&func, &collector);
 
   if (func.reserve_op_buffer(1))
     DBUG_RETURN(0);
   func.add_operation(spatial_op, 2);
 
   if ((null_value=
-       (args[0]->null_value ||
-	args[1]->null_value ||
+       (args[0]->null_value || args[1]->null_value ||
 	!(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) ||
 	!(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())) ||
-	g1->store_shapes(&collector, &func) ||
-        g2->store_shapes(&collector, &func))))
+	g1->store_shapes(&trn) || g2->store_shapes(&trn))))
     goto exit;
 
   
@@ -908,27 +906,42 @@ static void calculate_perpendicular(cons
 }
 
 
-int Item_func_buffer::add_edge_buffer(const gcalc_heap::info *p1,
-                                      const gcalc_heap::info *p2, 
-                                      const gcalc_heap::info *p3,
-                                      double d,
-                                      bool round_p1, bool round_p2)
+static void calculate_perpendicular(
+    double x1, double y1, double x2, double y2, double d,
+    double *ex, double *ey,
+    double *px, double *py)
 {
-  gcalc_shape_transporter trn(&collector);
-  gcalc_shape_info si;
+  double q;
+  *ex= x1 - x2;
+  *ey= y1 - y2;
+  q= d / sqrt((*ex) * (*ex) + (*ey) * (*ey));
+  *px= (*ey) * q;
+  *py= -(*ex) * q;
+}
+
+
+int Item_func_buffer::transporter::single_point(double x, double y)
+{
+  return add_point_buffer(x, y);
+}
+
+
+int Item_func_buffer::transporter::add_edge_buffer(
+  double x3, double y3, bool round_p1, bool round_p2)
+{
+  gcalc_operation_transporter trn(m_fn, m_heap);
   double e1_x, e1_y, e2_x, e2_y, p1_x, p1_y, p2_x, p2_y;
   double e1e2;
   double sin1, cos1;
   double x_n, y_n;
   bool empty_gap1, empty_gap2;
 
-  if (func.single_shape_op(gcalc_function::shape_polygon, &si))
+  ++m_nshapes;
+  if (trn.start_simple_poly())
     return 1;
 
-  trn.start_ring(si);
-
-  calculate_perpendicular(p1, p2, d, &e1_x, &e1_y, &p1_x, &p1_y);
-  calculate_perpendicular(p3, p2, d, &e2_x, &e2_y, &p2_x, &p2_y);
+  calculate_perpendicular(x1, y1, x2, y2, m_d, &e1_x, &e1_y, &p1_x, &p1_y);
+  calculate_perpendicular(x3, y3, x2, y2, m_d, &e2_x, &e2_y, &p2_x, &p2_y);
 
   e1e2= e1_x * e2_y - e2_x * e1_y;
   sin1= n_sinus[1];
@@ -936,161 +949,180 @@ int Item_func_buffer::add_edge_buffer(co
   if (e1e2 < 0)
   {
     empty_gap2= false;
-    x_n= p2->x + p2_x * cos1 - p2_y * sin1;
-    y_n= p2->y + p2_y * cos1 + p2_x * sin1;
-    if (fill_gap(&trn, p2->x,p2->y, -p1_x,-p1_y, p2_x,p2_y, d, &empty_gap1) ||
-        trn.add_point(p2->x + p2_x, p2->y + p2_y) ||
+    x_n= x2 + p2_x * cos1 - p2_y * sin1;
+    y_n= y2 + p2_y * cos1 + p2_x * sin1;
+    if (fill_gap(&trn, x2, y2, -p1_x,-p1_y, p2_x,p2_y, m_d, &empty_gap1) ||
+        trn.add_point(x2 + p2_x, y2 + p2_y) ||
         trn.add_point(x_n, y_n))
       return 1;
   }
   else
   {
-    x_n= p2->x - p2_x * cos1 - p2_y * sin1;
-    y_n= p2->y - p2_y * cos1 + p2_x * sin1;
+    x_n= x2 - p2_x * cos1 - p2_y * sin1;
+    y_n= y2 - p2_y * cos1 + p2_x * sin1;
     if (trn.add_point(x_n, y_n) ||
-        trn.add_point(p2->x - p2_x, p2->y - p2_y) ||
-        fill_gap(&trn, p2->x, p2->y, -p2_x, -p2_y, p1_x, p1_y, d, &empty_gap2))
+        trn.add_point(x2 - p2_x, y2 - p2_y) ||
+        fill_gap(&trn, x2, y2, -p2_x, -p2_y, p1_x, p1_y, m_d, &empty_gap2))
       return 1;
     empty_gap1= false;
   }
-  if ((!empty_gap2 && trn.add_point(p2->x + p1_x, p2->y + p1_y)) ||
-      trn.add_point(p1->x + p1_x, p1->y + p1_y))
+  if ((!empty_gap2 && trn.add_point(x2 + p1_x, y2 + p1_y)) ||
+      trn.add_point(x1 + p1_x, y1 + p1_y))
     return 1;
 
-  if (round_p1 && fill_half_circle(&trn, p1->x, p1->y, p1_x, p1_y))
+  if (round_p1 && fill_half_circle(&trn, x1, y1, p1_x, p1_y))
     return 1;
 
-  if (trn.add_point(p1->x - p1_x, p1->y - p1_y) ||
-      (!empty_gap1 && trn.add_point(p2->x - p1_x, p2->y - p1_y)))
+  if (trn.add_point(x1 - p1_x, y1 - p1_y) ||
+      (!empty_gap1 && trn.add_point(x2 - p1_x, y2 - p1_y)))
     return 1;
-  trn.complete();
-  return 0;
+  return trn.complete_simple_poly();
 }
 
 
-int Item_func_buffer::add_last_edge_buffer(const gcalc_heap::info *p1,
-                                           const gcalc_heap::info *p2, 
-                                           double d)
+int Item_func_buffer::transporter::add_last_edge_buffer()
 {
-  gcalc_shape_transporter trn(&collector);
-  gcalc_shape_info si;
+  gcalc_operation_transporter trn(m_fn, m_heap);
   double e1_x, e1_y, p1_x, p1_y;
 
-  if (func.single_shape_op(gcalc_function::shape_polygon, &si))
+  ++m_nshapes;
+  if (trn.start_simple_poly())
     return 1;
 
-  trn.start_ring(si);
+  calculate_perpendicular(x1, y1, x2, y2, m_d, &e1_x, &e1_y, &p1_x, &p1_y);
 
-  calculate_perpendicular(p1, p2, d, &e1_x, &e1_y, &p1_x, &p1_y);
-
-  if (trn.add_point(p1->x + p1_x, p1->y + p1_y) ||
-      trn.add_point(p1->x - p1_x, p1->y - p1_y) ||
-      trn.add_point(p2->x - p1_x, p2->y - p1_y) ||
-      fill_half_circle(&trn, p2->x, p2->y, -p1_x, -p1_y) ||
-      trn.add_point(p2->x + p1_x, p2->y + p1_y))
+  if (trn.add_point(x1 + p1_x, y1 + p1_y) ||
+      trn.add_point(x1 - p1_x, y1 - p1_y) ||
+      trn.add_point(x2 - p1_x, y2 - p1_y) ||
+      fill_half_circle(&trn, x2, y2, -p1_x, -p1_y) ||
+      trn.add_point(x2 + p1_x, y2 + p1_y))
     return 1;
-  trn.complete();
-  return 0;
+  return trn.complete_simple_poly();
 }
 
 
-int Item_func_buffer::add_point_buffer(const gcalc_heap::info *p, double d)
+int Item_func_buffer::transporter::add_point_buffer(double x, double y)
 {
-  gcalc_shape_transporter trn(&collector);
-  gcalc_shape_info si;
+  gcalc_operation_transporter trn(m_fn, m_heap);
 
-  if (func.single_shape_op(gcalc_function::shape_polygon, &si))
+  m_nshapes++;
+  if (trn.start_simple_poly())
     return 1;
-
-  trn.start_ring(si);
-  if (trn.add_point(p->x - d, p->y) ||
-      fill_half_circle(&trn, p->x, p->y, -d, 0.0) ||
-      trn.add_point(p->x + d, p->y) ||
-      fill_half_circle(&trn, p->x, p->y, d, 0.0))
+  if (trn.add_point(x - m_d, y) ||
+      fill_half_circle(&trn, x, y, -m_d, 0.0) ||
+      trn.add_point(x + m_d, y) ||
+      fill_half_circle(&trn, x, y, m_d, 0.0))
     return 1;
-  trn.complete();
+  return trn.complete_simple_poly();
+}
+
+
+int Item_func_buffer::transporter::start_line()
+{
+  m_npoints= 0;
+  int_start_line();
   return 0;
 }
 
 
-int Item_func_buffer::add_poly_buffer(gcalc_heap::info **ptr_p,
-                                      uint32 *n_p, double d)
+int Item_func_buffer::transporter::start_poly()
 {
-  gcalc_shape_transporter trn(&collector);
-  gcalc_heap::info *p1= *ptr_p;
-  gcalc_heap::info *p_cur= p1;
-  gcalc_heap::info *p_next;
+  ++m_nshapes;
+  return gcalc_operation_transporter::start_poly();
+}
 
-  if (p_cur->left == p1) /* Polygon consists of a single point */
-  {
-    if (add_point_buffer(p_cur, d))
-      return 1;
-    (*n_p)++;
-    return 0;
-  }
 
-  for (;;)
-  {
-    p_next= p_cur->left;
-    if (add_edge_buffer(p_cur, p_next, p_next->left, d, false, false))
-      return 1;
-    (*n_p)++;
-    if (p_next == p1)
-      break;
-    p_cur= p_next;
-  }
-  *ptr_p= p_cur;
-  return 0;
+int Item_func_buffer::transporter::start_ring()
+{
+  m_npoints= 0;
+  return gcalc_operation_transporter::start_ring();
 }
 
 
-int Item_func_buffer::add_line_buffer(gcalc_heap::info **ptr_p,
-                                      uint32 *n_p, double d)
+int Item_func_buffer::transporter::add_point(double x, double y)
 {
-  gcalc_shape_transporter trn(&collector);
-  gcalc_heap::info *p1= *ptr_p;
-  gcalc_heap::info *p_cur= p1;
-  gcalc_heap::info *p_next= p_cur->left;
+  if (m_npoints && x == x2 && y == y2)
+    return 0;
+
+  ++m_npoints;
 
-  if (!p_next) /* Line consists of a single point */
+  if (m_npoints == 1)
   {
-    if (add_point_buffer(p_cur, d))
-      return 1;
-    (*n_p)++;
-    return 0;
+    x00= x;
+    y00= y;
   }
-
-  if (p_next->left == p1) /* Line consists of two points */
+  else if (m_npoints == 2)
   {
-    if (add_edge_buffer(p_cur, p_next, p_next->left, d, true, true))
-      return 1;
-    (*n_p)++;
-    *ptr_p= p_cur->get_next();
-    return 0;
+    x01= x;
+    y01= y;
   }
-
-  if (add_edge_buffer(p_cur, p_next, p_next->left, d, true, false))
+  else if (add_edge_buffer(x, y, (m_npoints == 3) && line_started(), false))
     return 1;
-  (*n_p)++;
 
-  p_cur= p_next;
-  p_next= p_next->left;
-  while (p_next->left != p_cur)
+  x1= x2;
+  y1= y2;
+  x2= x;
+  y2= y;
+
+  return line_started() ? 0 : gcalc_operation_transporter::add_point(x, y);
+}
+
+
+int Item_func_buffer::transporter::complete()
+{
+  if (m_npoints)
   {
-    if (add_edge_buffer(p_cur, p_next, p_next->left, d, false, false))
-      return 1;
-    (*n_p)++;
-    p_cur= p_next;
-    p_next= p_next->left;
+    if (m_npoints == 1)
+    {
+      if (add_point_buffer(x2, y2))
+        return 1;
+    }
+    else if (m_npoints == 2)
+    {
+      if (add_edge_buffer(x1, y1, true, true))
+        return 1;
+    }
+    else if (line_started())
+    {
+      if (add_last_edge_buffer())
+        return 1;
+    }
+    else
+    {
+      if (x2 != x00 && y2 != y00)
+      {
+        if (add_edge_buffer(x00, y00, false, false))
+          return 1;
+        x1= x2;
+        y1= y2;
+        x2= x00;
+        y2= y00;
+      }
+      if (add_edge_buffer(x01, y01, false, false))
+        return 1;
+    }
   }
-  if (add_last_edge_buffer(p_cur, p_next, d))
+
+  return 0;
+}
+
+
+int Item_func_buffer::transporter::complete_line()
+{
+  if (complete())
     return 1;
-  (*n_p)++;
-  *ptr_p= p_next;
+  int_complete_line();
   return 0;
 }
 
 
+int Item_func_buffer::transporter::complete_ring()
+{
+  return complete() ||
+         gcalc_operation_transporter::complete_ring();
+}
+
+
 String *Item_func_buffer::val_str(String *str_value)
 {
   DBUG_ENTER("Item_func_buffer::val_str");
@@ -1099,12 +1131,10 @@ String *Item_func_buffer::val_str(String
   double dist= args[1]->val_real();
   Geometry_buffer buffer;
   Geometry *g;
-  gcalc_heap::info *dist_point;
-  gcalc_dyn_list::item **last_hook; /* used to make loop */
-  uint32 n_operands, union_pos;
+  uint32 union_pos;
   uint32 srid= 0;
   String *str_result= NULL;
-
+  transporter trn(&func, &collector, dist);
 
   null_value= 1;
   if (args[0]->null_value || args[1]->null_value ||
@@ -1113,46 +1143,17 @@ String *Item_func_buffer::val_str(String
 
   if (func.reserve_op_buffer(2))
     goto mem_error;
+  /* will specify operands later */
+  union_pos= func.get_next_operation_pos();
   func.add_operation((dist > 0.0) ? gcalc_function::op_union :
-                                    gcalc_function::op_difference, 2);
+                                    gcalc_function::op_difference, 0);
 
   GCALC_DBUG_STARTFILE("/home/hf/linebuffer");
-  if (g->store_shapes(&collector, &func))
+  if (g->store_shapes(&trn))
     goto mem_error;
 
-  /* will specify operands later */
-  union_pos= func.get_next_operation_pos();
-  func.add_operation(gcalc_function::op_union, 0);
-  dist_point= collector.get_first();
-  last_hook= collector.get_last_hook();
-  n_operands= 0;
-  for (;;)
-  {
-    if (dist_point->left)
-    {
-      if (dist_point->right)
-      {
-        if (add_poly_buffer(&dist_point, &n_operands, dist))
-          goto mem_error;
-      }
-      else
-      {
-        if (add_line_buffer(&dist_point, &n_operands, dist))
-          goto mem_error;
-      }
-    }
-    else
-    {
-      ++n_operands;
-      if (add_point_buffer(dist_point, dist))
-        goto mem_error;
-    }
-    if (last_hook == &dist_point->next)
-      break;
-    dist_point= dist_point->get_next();
-  }
   GCALC_DBUG_STOP;
-  func.add_operands_to_op(union_pos, n_operands);
+  func.add_operands_to_op(union_pos, trn.m_nshapes);
   collector.prepare_operation();
   if (func.alloc_states())
     goto mem_error;
@@ -1424,6 +1425,7 @@ double Item_func_distance::val_real()
   double x1, x2, y1, y2;
   double ex, ey, vx, vy, e_sqrlen;
   uint obj2_si;
+  gcalc_operation_transporter trn(&func, &collector);
 
   DBUG_ENTER("Item_func_distance::val_real");
   DBUG_ASSERT(fixed == 1);
@@ -1453,10 +1455,10 @@ double Item_func_distance::val_real()
     goto mem_error;
   func.add_operation(gcalc_function::op_intersection, 2);
 
-  if (g1->store_shapes(&collector, &func))
+  if (g1->store_shapes(&trn))
     goto mem_error;
   obj2_si= func.get_nshapes();
-  if (g2->store_shapes(&collector, &func) || func.alloc_states())
+  if (g2->store_shapes(&trn) || func.alloc_states())
     goto mem_error;
 
   collector.prepare_operation();

=== modified file 'sql/item_geofunc.h'
--- a/sql/item_geofunc.h	2008-03-14 15:37:08 +0000
+++ b/sql/item_geofunc.h	2009-12-20 10:39:40 +0000
@@ -278,6 +278,29 @@ public:
 class Item_func_buffer: public Item_geometry_func
 {
 protected:
+  class transporter : public gcalc_operation_transporter
+  {
+    int m_npoints;
+    double m_d;
+    double x1,y1,x2,y2;
+    double x00,y00,x01,y01;
+    int add_edge_buffer(double x3, double y3, bool round_p1, bool round_p2);
+    int add_last_edge_buffer();
+    int add_point_buffer(double x, double y);
+    int complete();
+  public:
+    int m_nshapes;
+    transporter(gcalc_function *fn, gcalc_heap *heap, double d) :
+      gcalc_operation_transporter(fn, heap), m_npoints(0), m_d(d), m_nshapes(0)
+    {}
+    int single_point(double x, double y);
+    int start_line();
+    int complete_line();
+    int start_poly();
+    int start_ring();
+    int complete_ring();
+    int add_point(double x, double y);
+  };
   gcalc_heap collector;
   gcalc_function func;
   gcalc_scan_iterator scan_it;
@@ -291,15 +314,6 @@ public:
     Item_geometry_func(obj, distance) {}
   const char *func_name() const { return "Buffer"; }
   String *val_str(String *);
-private:
-  int add_edge_buffer(const gcalc_heap::info *p1, const gcalc_heap::info *p2, 
-                      const gcalc_heap::info *p3, double d,
-                      bool round_p1, bool round_p2);
-  int add_last_edge_buffer(const gcalc_heap::info *p1,
-                           const gcalc_heap::info *p2, double d);
-  int add_point_buffer(const gcalc_heap::info *p, double d);
-  int add_poly_buffer(gcalc_heap::info **ptr_p, uint32 *n_p, double d);
-  int add_line_buffer(gcalc_heap::info **ptr_p, uint32 *n_p, double d);
 };
 
 

=== modified file 'sql/spatial.cc'
--- a/sql/spatial.cc	2009-12-05 08:58:34 +0000
+++ b/sql/spatial.cc	2009-12-20 10:39:40 +0000
@@ -481,15 +481,11 @@ bool Gis_point::get_mbr(MBR *mbr, const 
 }
 
 
-int Gis_point::store_shapes(gcalc_heap *heap, gcalc_function *fn) const
+int Gis_point::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   double x, y;
-  gcalc_shape_info si;
 
-  return fn->single_shape_op(gcalc_function::shape_point, &si) ||
-         get_xy(&x, &y) ||
-         trn.single_point(si, x, y);
+  return get_xy(&x, &y) || trn->single_point(x, y);
 }
 
 
@@ -697,16 +693,11 @@ int Gis_line_string::point_n(uint32 num,
 }
 
 
-int Gis_line_string::store_shapes(gcalc_heap *heap, gcalc_function *fn) const
+int Gis_line_string::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   uint32 n_points;
   double x, y;
   const char *data= m_data;
-  gcalc_shape_info si;
-
-  if (fn->single_shape_op(gcalc_function::shape_line, &si))
-    return 1;
 
   if (no_data(m_data, 4))
     return 1;
@@ -715,18 +706,17 @@ int Gis_line_string::store_shapes(gcalc_
   if (n_points < 1 || no_data(data, POINT_DATA_SIZE * n_points))
     return 1;
 
-  trn.start_line(si);
+  trn->start_line();
 
   while (n_points--)
   {
     get_point(&x, &y, data);
     data+= POINT_DATA_SIZE;
-    if (trn.add_point(x, y))
+    if (trn->add_point(x, y))
       return 1;
   }
 
-  trn.complete();
-  return 0;
+  return trn->complete_line();
 }
 
 
@@ -1118,14 +1108,12 @@ int Gis_polygon::centroid(String *result
 }
 
 
-int Gis_polygon::store_shapes(gcalc_heap *heap, gcalc_function *fn) const
+int Gis_polygon::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   uint32 n_linear_rings;
   const char *data= m_data;
-  gcalc_shape_info si;
 
-  if (fn->single_shape_op(gcalc_function::shape_polygon, &si))
+  if (trn->start_poly())
     return 1;
 
   if (no_data(data, 4))
@@ -1144,19 +1132,20 @@ int Gis_polygon::store_shapes(gcalc_heap
     if (no_data(data, POINT_DATA_SIZE * n_points))
       return 1;
 
-    trn.start_ring(si);
+    trn->start_ring();
     while (--n_points)
     {
       double x, y;
       get_point(&x, &y, data);
       data+= POINT_DATA_SIZE;
-      if (trn.add_point(x, y))
+      if (trn->add_point(x, y))
         return 1;
     }
     data+= POINT_DATA_SIZE;
-    trn.complete();
+    trn->complete_ring();
   }
 
+  trn->complete_poly();
   return 0;
 }
 
@@ -1310,9 +1299,8 @@ int Gis_multi_point::geometry_n(uint32 n
 }
 
 
-int Gis_multi_point::store_shapes(gcalc_heap *heap, gcalc_function *fn) const
+int Gis_multi_point::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   uint32 n_points;
   Gis_point pt;
   const char *data= m_data;
@@ -1322,9 +1310,8 @@ int Gis_multi_point::store_shapes(gcalc_
   n_points= uint4korr(data);
   data+= 4;
 
-  if (fn->reserve_shape_buffer(n_points) || fn->reserve_op_buffer(1))
+  if (trn->start_collection(n_points))
     return 1;
-  fn->add_operation(gcalc_function::op_union, n_points);
 
   while (n_points--)
   {
@@ -1332,7 +1319,7 @@ int Gis_multi_point::store_shapes(gcalc_
       return 1;
     data+= WKB_HEADER_SIZE;
     pt.set_data_ptr(data, (uint32) (m_data_end - data));
-    if (pt.store_shapes(heap, fn))
+    if (pt.store_shapes(trn))
       return 1;
     data+= pt.get_data_size();
   }
@@ -1615,9 +1602,8 @@ int Gis_multi_line_string::is_closed(int
 }
 
 
-int Gis_multi_line_string::store_shapes(gcalc_heap *heap, gcalc_function *fn) const
+int Gis_multi_line_string::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   uint32 n_lines;
   Gis_line_string ls;
   const char *data= m_data;
@@ -1627,9 +1613,8 @@ int Gis_multi_line_string::store_shapes(
   n_lines= uint4korr(data);
   data+= 4;
 
-  if (fn->reserve_shape_buffer(n_lines) || fn->reserve_op_buffer(1))
+  if (trn->start_collection(n_lines))
     return 1;
-  fn->add_operation(gcalc_function::op_union, n_lines);
 
   while (n_lines--)
   {
@@ -1637,7 +1622,7 @@ int Gis_multi_line_string::store_shapes(
       return 1;
     data+= WKB_HEADER_SIZE;
     ls.set_data_ptr(data, (uint32) (m_data_end - data));
-    if (ls.store_shapes(heap, fn))
+    if (ls.store_shapes(trn))
       return 1;
     data+= ls.get_data_size();
   }
@@ -1969,9 +1954,8 @@ int Gis_multi_polygon::centroid(String *
 }
 
 
-int Gis_multi_polygon::store_shapes(gcalc_heap *heap, gcalc_function *fn) const
+int Gis_multi_polygon::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   uint32 n_polygons;
   Gis_polygon p;
   const char *data= m_data;
@@ -1981,9 +1965,8 @@ int Gis_multi_polygon::store_shapes(gcal
   n_polygons= uint4korr(data);
   data+= 4;
 
-  if (fn->reserve_shape_buffer(n_polygons) || fn->reserve_op_buffer(1))
+  if (trn->start_collection(n_polygons))
     return 1;
-  fn->add_operation(gcalc_function::op_union, n_polygons);
 
   while (n_polygons--)
   {
@@ -1991,7 +1974,7 @@ int Gis_multi_polygon::store_shapes(gcal
       return 1;
     data+= WKB_HEADER_SIZE;
     p.set_data_ptr(data, (uint32) (m_data_end - data));
-    if (p.store_shapes(heap, fn))
+    if (p.store_shapes(trn))
       return 1;
     data+= p.get_data_size();
   }
@@ -2320,10 +2303,8 @@ bool Gis_geometry_collection::dimension(
 }
 
 
-int Gis_geometry_collection::store_shapes(gcalc_heap *heap,
-                                          gcalc_function *fn) const
+int Gis_geometry_collection::store_shapes(gcalc_shape_transporter *trn) const
 {
-  gcalc_shape_transporter trn(heap);
   uint32 n_objects;
   const char *data= m_data;
   Geometry_buffer buffer;
@@ -2334,9 +2315,8 @@ int Gis_geometry_collection::store_shape
   n_objects= uint4korr(data);
   data+= 4;
 
-  if (fn->reserve_shape_buffer(n_objects) || fn->reserve_op_buffer(1))
+  if (trn->start_collection(n_objects))
     return 1;
-  fn->add_operation(gcalc_function::op_union, n_objects);
 
   while (n_objects--)
   {
@@ -2349,7 +2329,7 @@ int Gis_geometry_collection::store_shape
     if (!(geom= create_by_typeid(&buffer, wkb_type)))
       return 1;
     geom->set_data_ptr(data, (uint32) (m_data_end - data));
-    if (geom->store_shapes(heap, fn))
+    if (geom->store_shapes(trn))
       return 1;
 
     data+= geom->get_data_size();

=== modified file 'sql/spatial.h'
--- a/sql/spatial.h	2009-11-18 13:40:52 +0000
+++ b/sql/spatial.h	2009-12-20 10:39:40 +0000
@@ -266,7 +266,7 @@ public:
   virtual int point_n(uint32 num, String *result) const { return -1; }
   virtual int interior_ring_n(uint32 num, String *result) const { return -1; }
   virtual int geometry_n(uint32 num, String *result) const { return -1; }
-  virtual int store_shapes(gcalc_heap *heap, gcalc_function *fn) const=0;
+  virtual int store_shapes(gcalc_shape_transporter *trn) const=0;
 
 public:
   static Geometry *create_by_typeid(Geometry_buffer *buffer, int type_id)
@@ -382,7 +382,7 @@ public:
     *end= 0;					/* No default end */
     return 0;
   }
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
 };
 
@@ -411,7 +411,7 @@ public:
     *end= 0;					/* No default end */
     return 0;
   }
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
 };
 
@@ -447,7 +447,7 @@ public:
     *end= 0;					/* No default end */
     return 0;
   }
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
 };
 
@@ -473,7 +473,7 @@ public:
     *end= 0;					/* No default end */
     return 0;
   }
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
 };
 
@@ -501,7 +501,7 @@ public:
     *end= 0;					/* No default end */
     return 0;
   }
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
 };
 
@@ -528,7 +528,7 @@ public:
     *end= 0;					/* No default end */
     return 0;
   }
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
   uint init_from_opresult(String *bin, const char *opres, uint32 n_shapes);
 };
@@ -550,7 +550,7 @@ public:
   int num_geometries(uint32 *num) const;
   int geometry_n(uint32 num, String *result) const;
   bool dimension(uint32 *dim, const char **end) const;
-  int store_shapes(gcalc_heap *heap, gcalc_function *fn) const;
+  int store_shapes(gcalc_shape_transporter *trn) const;
   const Class_info *get_class_info() const;
 };
 


Attachment: [text/bzr-bundle] bzr/holyfoot@mysql.com-20091220103940-to3cgcfcczbfd0gz.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (holyfoot:2665) Bug#45883Alexey Botchkov21 Dec