Commit fc9d34ca authored by Alexey Botchkov's avatar Alexey Botchkov

bug #901655 ST_BUFFER asserts with a coplicated shape.

        Coinciding nodes can appear as a result of DOUBLE inaccuracy.
        We should test that before we start the loop.

        Also the spatial relations can be calculated faster if we check
        MBR relations first. And we do have the shape's MBR-s now.

per-file comments:
  sql/gcalc_slicescan.cc
        set_extent() method added.
bug #901655 ST_BUFFER asserts with a coplicated shape.
  sql/gcalc_slicescan.h
        set_extent() method declared.
bug #901655 ST_BUFFER asserts with a coplicated shape.
  sql/gcalc_tools.cc
bug #901655 ST_BUFFER asserts with a coplicated shape.
        checks for equal nodes added.
  sql/item_geofunc.cc
bug #901655 ST_BUFFER asserts with a coplicated shape.
        MBR for the shapes calculated, and MBR checks added before we
        start the heavy calculations.
  sql/spatial.h
bug #901655 ST_BUFFER asserts with a coplicated shape.
        MBR::buffer() method implemented.
parent ae480437
......@@ -406,7 +406,7 @@ static int de_weak_check(long double a, long double b, long double ex)
static int de_check(long double a, long double b)
{
return de_weak_check(a, b, (long double) 1e-10);
return de_weak_check(a, b, (long double) 1e-9);
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
......@@ -792,10 +792,51 @@ static int cmp_intersections(const Gcalc_heap::Info *i1,
/* Internal coordinates implementation end */
#define GCALC_SCALE_1 1e18
static double find_scale(double extent)
{
double scale= 1e-2;
while (scale < extent)
scale*= (double ) 10;
return GCALC_SCALE_1 / scale / 10;
}
void Gcalc_heap::set_extent(double xmin, double xmax, double ymin, double ymax)
{
xmin= fabs(xmin);
xmax= fabs(xmax);
ymin= fabs(ymin);
ymax= fabs(ymax);
if (xmax < xmin)
xmax= xmin;
if (ymax < ymin)
ymax= ymin;
coord_extent= xmax > ymax ? xmax : ymax;
coord_extent= find_scale(coord_extent);
#ifdef GCALC_CHECK_WITH_FLOAT
gcalc_coord_extent= &coord_extent;
#endif /*GCALC_CHECK_WITH_FLOAT*/
}
void Gcalc_heap::free_point_info(Gcalc_heap::Info *i,
Gcalc_dyn_list::Item **i_hook)
{
if (m_hook == &i->next)
m_hook= i_hook;
*i_hook= i->next;
free_item(i);
m_n_points--;
}
Gcalc_heap::Info *Gcalc_heap::new_point_info(double x, double y,
gcalc_shape_info shape)
{
double abs= fabs(x);
Info *result= (Info *)new_item();
if (!result)
return NULL;
......@@ -806,17 +847,8 @@ Gcalc_heap::Info *Gcalc_heap::new_point_info(double x, double y,
result->shape= shape;
result->top_node= 1;
result->type= nt_shape_node;
if (m_n_points)
{
if (abs > coord_extent)
coord_extent= abs;
}
else
coord_extent= abs;
abs= fabs(y);
if (abs > coord_extent)
coord_extent= abs;
gcalc_set_double(result->ix, x, coord_extent);
gcalc_set_double(result->iy, y, coord_extent);
m_n_points++;
return result;
......@@ -927,32 +959,12 @@ static int compare_point_info(const void *e0, const void *e1)
}
#define GCALC_SCALE_1 1e18
static double find_scale(double extent)
{
double scale= 1e-2;
while (scale < extent)
scale*= (double ) 10;
return GCALC_SCALE_1 / scale / 10;
}
void Gcalc_heap::prepare_operation()
{
Info *cur;
GCALC_DBUG_ASSERT(m_hook);
coord_extent= find_scale(coord_extent);
#ifdef GCALC_CHECK_WITH_FLOAT
gcalc_coord_extent= &coord_extent;
#endif /*GCALC_CHECK_WITH_FLOAT*/
*m_hook= NULL;
m_hook= NULL; /* just to check it's not called twice */
for (cur= get_first(); cur; cur= cur->get_next())
{
gcalc_set_double(cur->ix, cur->x, coord_extent);
gcalc_set_double(cur->iy, cur->y, coord_extent);
}
m_first= sort_list(compare_point_info, m_first, m_n_points);
/* TODO - move this to the 'normal_scan' loop */
......@@ -990,18 +1002,28 @@ int Gcalc_shape_transporter::int_add_point(gcalc_shape_info Info,
double x, double y)
{
Gcalc_heap::Info *point;
GCALC_DBUG_ASSERT(!m_prev || m_prev->x != x || m_prev->y != y);
Gcalc_dyn_list::Item **hook;
hook= m_heap->get_cur_hook();
if (!(point= m_heap->new_point_info(x, y, Info)))
return 1;
if (m_first)
{
if (cmp_point_info(m_prev, point) == 0)
{
/* Coinciding points, do nothing */
m_heap->free_point_info(point, hook);
return 0;
}
GCALC_DBUG_ASSERT(!m_prev || m_prev->x != x || m_prev->y != y);
m_prev->left= point;
point->right= m_prev;
}
else
m_first= point;
m_prev= point;
m_prev_hook= hook;
return 0;
}
......@@ -1029,10 +1051,20 @@ void Gcalc_shape_transporter::int_complete()
return;
}
GCALC_DBUG_ASSERT(m_prev->x != m_first->x || m_prev->y != m_first->y);
/* polygon */
m_first->right= m_prev;
m_prev->left= m_first;
if (cmp_point_info(m_first, m_prev) == 0)
{
/* Coinciding points, remove the last one from the list */
m_prev->right->left= m_first;
m_first->right= m_prev->right;
m_heap->free_point_info(m_prev, m_prev_hook);
}
else
{
GCALC_DBUG_ASSERT(m_prev->x != m_first->x || m_prev->y != m_first->y);
m_first->right= m_prev;
m_prev->left= m_first;
}
}
......
......@@ -229,7 +229,9 @@ class Gcalc_heap : public Gcalc_dyn_list
Gcalc_dyn_list(blk_size, sizeof(Info)),
m_hook(&m_first), m_n_points(0)
{}
void set_extent(double xmin, double xmax, double ymin, double ymax);
Info *new_point_info(double x, double y, gcalc_shape_info shape);
void free_point_info(Info *i, Gcalc_dyn_list::Item **i_hook);
Info *new_intersection(const Info *p1, const Info *p2,
const Info *p3, const Info *p4);
void prepare_operation();
......@@ -242,6 +244,8 @@ class Gcalc_heap : public Gcalc_dyn_list
long double get_double(const Gcalc_internal_coord *c) const;
#endif /*GCALC_CHECK_WITH_FLOAT*/
double coord_extent;
Gcalc_dyn_list::Item **get_cur_hook() { return m_hook; }
private:
Gcalc_dyn_list::Item *m_first;
Gcalc_dyn_list::Item **m_hook;
......@@ -269,6 +273,7 @@ class Gcalc_shape_transporter
private:
Gcalc_heap::Info *m_first;
Gcalc_heap::Info *m_prev;
Gcalc_dyn_list::Item **m_prev_hook;
int m_shape_started;
void int_complete();
protected:
......
......@@ -650,15 +650,6 @@ Gcalc_operation_reducer(Gcalc_function *fn, modes mode, size_t blk_size) :
}
#ifdef TMP_BLOCK
void Gcalc_operation_reducer::res_point::set(const Gcalc_scan_iterator *si)
{
if ((intersection_point= si->intersection_step()))
ii= si->get_cur_ii();
else
pi= si->get_cur_pi();
}
#endif /*TMP_BLOCK*/
void Gcalc_operation_reducer::res_point::set(const Gcalc_scan_iterator *si)
{
intersection_point= si->intersection_step();
......
......@@ -657,140 +657,6 @@ static double distance_points(const Gcalc_heap::Info *a,
}
/*
Calculates the distance between objects.
*/
#ifdef TMP_BLOCK
static int calc_distance(double *result, Gcalc_heap *collector, uint obj2_si,
Gcalc_function *func, Gcalc_scan_iterator *scan_it)
{
bool above_cur_point, cur_point_edge;
const Gcalc_scan_iterator::point *evpos;
const Gcalc_heap::Info *cur_point= NULL;
const Gcalc_heap::Info *dist_point;
const Gcalc_scan_iterator::point *ev;
double t, distance, cur_distance;
double ex, ey, vx, vy, e_sqrlen;
int o1, o2;
DBUG_ENTER("calc_distance");
above_cur_point= false;
distance= DBL_MAX;
while (scan_it->more_points())
{
if (scan_it->step())
goto mem_error;
evpos= scan_it->get_event_position();
ev= scan_it->get_events();
cur_point= NULL;
if (ev->simple_event())
{
cur_point= ev->pi;
goto calculate_distance;
}
/*
handling intersection we only need to check if it's the intersecion
of objects 1 and 2. In this case distance is 0
*/
o1= 0;
o2= 0;
for (; ev; ev= ev->get_next())
{
if (ev->event != scev_intersection)
cur_point= ev->pi;
if (ev->pi->shape >= obj2_si)
o2= 1;
else
o1= 1;
if (o1 && o2)
{
distance= 0;
goto exit;
}
}
if (!cur_point)
continue;
#ifdef TO_REMOVE
goto calculate_distance;
/*
having these events we need to check for possible intersection
of objects
scev_thread | scev_two_threads | scev_single_point
*/
DBUG_ASSERT(ev & (scev_thread | scev_two_threads | scev_single_point));
func->clear_state();
for (Gcalc_point_iterator pit(scan_it); pit.point() != evpos; ++pit)
{
gcalc_shape_info si= pit.point()->get_shape();
if ((func->get_shape_kind(si) == Gcalc_function::shape_polygon))
func->invert_state(si);
}
func->invert_state(evpos->get_shape());
if (func->count())
{
/* Point of one object is inside the other - intersection found */
distance= 0;
goto exit;
}
#endif /*TO_REMOVE*/
calculate_distance:
if (cur_point->shape >= obj2_si)
continue;
cur_point_edge= !cur_point->is_bottom();
for (dist_point= collector->get_first(); dist_point; dist_point= dist_point->get_next())
{
/* We only check vertices of object 2 */
if (dist_point->shape < obj2_si)
continue;
/* if we have an edge to check */
if (dist_point->left)
{
t= count_edge_t(dist_point, dist_point->left, cur_point,
ex, ey, vx, vy, e_sqrlen);
if ((t > 0.0) && (t < 1.0))
{
cur_distance= distance_to_line(ex, ey, vx, vy, e_sqrlen);
if (distance > cur_distance)
distance= cur_distance;
}
}
if (cur_point_edge)
{
t= count_edge_t(cur_point, cur_point->left, dist_point,
ex, ey, vx, vy, e_sqrlen);
if ((t > 0.0) && (t < 1.0))
{
cur_distance= distance_to_line(ex, ey, vx, vy, e_sqrlen);
if (distance > cur_distance)
distance= cur_distance;
}
}
cur_distance= distance_points(cur_point, dist_point);
if (distance > cur_distance)
distance= cur_distance;
}
}
exit:
*result= distance;
DBUG_RETURN(0);
mem_error:
DBUG_RETURN(1);
}
#endif /*TMP_BLOCK*/
#define GIS_ZERO 0.00000000001
longlong Item_func_spatial_rel::val_int()
......@@ -804,6 +670,8 @@ longlong Item_func_spatial_rel::val_int()
int result= 0;
int mask= 0;
uint shape_a, shape_b;
MBR umbr, mbr1, mbr2;
const char *c_end;
res1= args[0]->val_str(&tmp_value1);
res2= args[1]->val_str(&tmp_value2);
......@@ -818,19 +686,35 @@ longlong Item_func_spatial_rel::val_int()
!(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())))))
goto exit;
g1->get_mbr(&mbr1, &c_end);
g2->get_mbr(&mbr2, &c_end);
umbr= mbr1;
umbr.add_mbr(&mbr2);
collector.set_extent(umbr.xmin, umbr.xmax, umbr.ymin, umbr.ymax);
mbr1.buffer(1e-5);
switch (spatial_rel) {
case SP_CONTAINS_FUNC:
if (!mbr1.contains(&mbr2))
goto exit;
mask= 1;
func.add_operation(Gcalc_function::op_difference, 2);
/* Mind the g2 goes first. */
null_value= g2->store_shapes(&trn) || g1->store_shapes(&trn);
break;
case SP_WITHIN_FUNC:
mbr2.buffer(2e-5);
if (!mbr1.within(&mbr2))
goto exit;
mask= 1;
func.add_operation(Gcalc_function::op_difference, 2);
null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn);
break;
case SP_EQUALS_FUNC:
if (!mbr1.contains(&mbr2))
goto exit;
mask= 1;
func.add_operation(Gcalc_function::op_symdifference, 2);
null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn);
......@@ -841,6 +725,8 @@ longlong Item_func_spatial_rel::val_int()
null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn);
break;
case SP_INTERSECTS_FUNC:
if (!mbr1.intersects(&mbr2))
goto exit;
func.add_operation(Gcalc_function::op_intersection, 2);
null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn);
break;
......@@ -898,15 +784,6 @@ longlong Item_func_spatial_rel::val_int()
scan_it.init(&collector);
scan_it.killed= (int *) &(current_thd->killed);
#ifdef TMP_BLOCK
if (spatial_rel == SP_EQUALS_FUNC)
{
result= (g1->get_class_info()->m_type_id == g1->get_class_info()->m_type_id) &&
func_equals();
goto exit;
}
#endif /*TMP_BLOCK*/
if (func.alloc_states())
goto exit;
......@@ -935,6 +812,8 @@ String *Item_func_spatial_operation::val_str(String *str_value)
Geometry *g1, *g2;
uint32 srid= 0;
Gcalc_operation_transporter trn(&func, &collector);
MBR mbr1, mbr2;
const char *c_end;
if (func.reserve_op_buffer(1))
DBUG_RETURN(0);
......@@ -943,14 +822,23 @@ String *Item_func_spatial_operation::val_str(String *str_value)
if ((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(&trn) || g2->store_shapes(&trn))))
!(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())))))
{
str_value= 0;
goto exit;
}
g1->get_mbr(&mbr1, &c_end);
g2->get_mbr(&mbr2, &c_end);
mbr1.add_mbr(&mbr2);
collector.set_extent(mbr1.xmin, mbr1.xmax, mbr1.ymin, mbr1.ymax);
if ((null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn)))
{
str_value= 0;
goto exit;
}
collector.prepare_operation();
if (func.alloc_states())
goto exit;
......@@ -1374,12 +1262,18 @@ String *Item_func_buffer::val_str(String *str_value)
uint32 srid= 0;
String *str_result= NULL;
Transporter trn(&func, &collector, dist);
MBR mbr;
const char *c_end;
null_value= 1;
if (args[0]->null_value || args[1]->null_value ||
!(g= Geometry::construct(&buffer, obj->ptr(), obj->length())))
!(g= Geometry::construct(&buffer, obj->ptr(), obj->length())) ||
g->get_mbr(&mbr, &c_end))
goto mem_error;
if (dist > 0.0)
mbr.buffer(dist);
collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
/*
If the distance given is 0, the Buffer function is in fact NOOP,
so it's natural just to return the argument1.
......@@ -1446,6 +1340,8 @@ longlong Item_func_issimple::val_int()
Geometry *g;
int result= 1;
const Gcalc_scan_iterator::event_point *ev;
MBR mbr;
const char *c_end;
DBUG_ENTER("Item_func_issimple::val_int");
DBUG_ASSERT(fixed == 1);
......@@ -1454,6 +1350,8 @@ longlong Item_func_issimple::val_int()
!(g= Geometry::construct(&buffer, swkb->ptr(), swkb->length())))
DBUG_RETURN(0);
g->get_mbr(&mbr, &c_end);
collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
if (g->get_class_info()->m_type_id == Geometry::wkb_point)
DBUG_RETURN(1);
......@@ -1682,6 +1580,8 @@ double Item_func_distance::val_real()
String *res2= args[1]->val_str(&tmp_value2);
Geometry_buffer buffer1, buffer2;
Geometry *g1, *g2;
MBR mbr1, mbr2;
const char *c_end;
if ((null_value= (args[0]->null_value || args[1]->null_value ||
......@@ -1689,6 +1589,11 @@ double Item_func_distance::val_real()
!(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())))))
goto mem_error;
g1->get_mbr(&mbr1, &c_end);
g2->get_mbr(&mbr2, &c_end);
mbr1.add_mbr(&mbr2);
collector.set_extent(mbr1.xmin, mbr1.xmax, mbr1.ymin, mbr1.ymax);
if ((g1->get_class_info()->m_type_id == Geometry::wkb_point) &&
(g2->get_class_info()->m_type_id == Geometry::wkb_point))
{
......
......@@ -98,6 +98,13 @@ struct MBR
if (mbr->ymax > ymax)
ymax= mbr->ymax;
}
void buffer(double d)
{
xmin-= d;
ymin-= d;
xmax+= d;
ymax+= d;
}
int equals(const MBR *mbr)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment