diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sql/field.cc | 2 | ||||
-rw-r--r-- | sql/gcalc_slicescan.cc | 1966 | ||||
-rw-r--r-- | sql/gcalc_slicescan.h | 595 | ||||
-rw-r--r-- | sql/gcalc_tools.cc | 1429 | ||||
-rw-r--r-- | sql/gcalc_tools.h | 348 | ||||
-rw-r--r-- | sql/gstream.cc | 23 | ||||
-rw-r--r-- | sql/gstream.h | 9 | ||||
-rw-r--r-- | sql/item_create.cc | 355 | ||||
-rw-r--r-- | sql/item_geofunc.cc | 1092 | ||||
-rw-r--r-- | sql/item_geofunc.h | 225 | ||||
-rw-r--r-- | sql/opt_range.cc | 6 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 6 | ||||
-rw-r--r-- | sql/opt_subselect.h | 2 | ||||
-rw-r--r-- | sql/plistsort.c | 166 | ||||
-rw-r--r-- | sql/spatial.cc | 738 | ||||
-rw-r--r-- | sql/spatial.h | 50 | ||||
-rw-r--r-- | sql/sql_base.cc | 2 | ||||
-rw-r--r-- | sql/sql_priv.h | 7 | ||||
-rw-r--r-- | sql/sql_select.cc | 12 |
20 files changed, 6851 insertions, 183 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 6d7b73ffcee..913b82bcb63 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -81,6 +81,7 @@ SET (SQL_SOURCE create_options.cc multi_range_read.cc opt_index_cond_pushdown.cc opt_subselect.cc opt_table_elimination.cc sql_expression_cache.cc + gcalc_slicescan.cc gcalc_tools.cc ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE}) diff --git a/sql/field.cc b/sql/field.cc index 42d71477372..480d36c2e28 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7524,7 +7524,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) goto err; // Check given WKB uint32 wkb_type; - if (length < SRID_SIZE + WKB_HEADER_SIZE + SIZEOF_STORED_DOUBLE*2) + if (length < SRID_SIZE + WKB_HEADER_SIZE + 4) goto err; wkb_type= uint4korr(from + SRID_SIZE + 1); if (wkb_type < (uint32) Geometry::wkb_point || diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc new file mode 100644 index 00000000000..cc2b26a8665 --- /dev/null +++ b/sql/gcalc_slicescan.cc @@ -0,0 +1,1966 @@ +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. + Copyright (C) 2011 Monty Program Ab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include <my_global.h> +#include <my_sys.h> +#include <m_string.h> + +#ifdef HAVE_SPATIAL + +#include "gcalc_slicescan.h" + + +#define PH_DATA_OFFSET 8 +#define coord_to_float(d) ((double) d) +#define coord_eq(a, b) (a == b) + +typedef int (*sc_compare_func)(const void*, const void*); + +#define LS_LIST_ITEM Gcalc_dyn_list::Item +#define LS_COMPARE_FUNC_DECL sc_compare_func compare, +#define LS_COMPARE_FUNC_CALL(list_el1, list_el2) (*compare)(list_el1, list_el2) +#define LS_NEXT(A) (A)->next +#define LS_SET_NEXT(A,val) (A)->next= val +#define LS_P_NEXT(A) &(A)->next +#define LS_NAME sort_list +#define LS_SCOPE static +#define LS_STRUCT_NAME sort_list_stack_struct +#include "plistsort.c" + + +#define GCALC_COORD_MINUS 0x80000000 +#define FIRST_DIGIT(d) ((d) & 0x7FFFFFFF) +#define GCALC_SIGN(d) ((d) & 0x80000000) + +static Gcalc_scan_iterator::point *eq_sp(const Gcalc_heap::Info *pi) +{ + GCALC_DBUG_ASSERT(pi->type == Gcalc_heap::nt_eq_node); + return (Gcalc_scan_iterator::point *) pi->eq_data; +} + + +static Gcalc_scan_iterator::intersection_info *i_data(const Gcalc_heap::Info *pi) +{ + GCALC_DBUG_ASSERT(pi->type == Gcalc_heap::nt_intersection); + return (Gcalc_scan_iterator::intersection_info *) pi->intersection_data; +} + + +#ifndef GCALC_DBUG_OFF + +int gcalc_step_counter= 0; + +void GCALC_DBUG_CHECK_COUNTER() +{ + if (++gcalc_step_counter == 0) + GCALC_DBUG_PRINT(("step_counter_0")); + else + GCALC_DBUG_PRINT(("%d step_counter", gcalc_step_counter)); +} + + +const char *gcalc_ev_name(int ev) +{ + switch (ev) + { + case scev_none: + return "n"; + case scev_thread: + return "t"; + case scev_two_threads: + return "tt"; + case scev_end: + return "e"; + case scev_two_ends: + return "ee"; + case scev_intersection: + return "i"; + case scev_point: + return "p"; + case scev_single_point: + return "sp"; + default:; + }; + GCALC_DBUG_ASSERT(0); + return "unk"; +} + + +static int gcalc_pi_str(char *str, const Gcalc_heap::Info *pi, const char *postfix) +{ + return sprintf(str, "%s %d %d | %s %d %d%s", + GCALC_SIGN(pi->ix[0]) ? "-":"", FIRST_DIGIT(pi->ix[0]),pi->ix[1], + GCALC_SIGN(pi->iy[0]) ? "-":"", FIRST_DIGIT(pi->iy[0]),pi->iy[1], + postfix); + +} + + +static void GCALC_DBUG_PRINT_PI(const Gcalc_heap::Info *pi) +{ + char buf[128]; + int n_buf; + if (pi->type == Gcalc_heap::nt_intersection) + { + const Gcalc_scan_iterator::intersection_info *ic= i_data(pi); + + GCALC_DBUG_PRINT(("intersection point %d %d", + ic->edge_a->thread, ic->edge_b->thread)); + return; + } + if (pi->type == Gcalc_heap::nt_eq_node) + { + const Gcalc_scan_iterator::point *e= eq_sp(pi); + GCALC_DBUG_PRINT(("eq point %d", e->thread)); + return; + } + n_buf= gcalc_pi_str(buf, pi, ""); + buf[n_buf]= 0; + GCALC_DBUG_PRINT(("%s", buf)); +} + + +static void GCALC_DBUG_PRINT_SLICE(const char *header, + const Gcalc_scan_iterator::point *slice) +{ + int nbuf; + char buf[1024]; + nbuf= strlen(header); + strcpy(buf, header); + for (; slice; slice= slice->get_next()) + { + int lnbuf= nbuf; + lnbuf+= sprintf(buf + lnbuf, "%d\t", slice->thread); + lnbuf+= sprintf(buf + lnbuf, "%s\t", gcalc_ev_name(slice->event)); + + lnbuf+= gcalc_pi_str(buf + lnbuf, slice->pi, "\t"); + if (slice->is_bottom()) + lnbuf+= sprintf(buf+lnbuf, "bt\t"); + else + lnbuf+= gcalc_pi_str(buf+lnbuf, slice->next_pi, "\t"); + buf[lnbuf]= 0; + GCALC_DBUG_PRINT(("%s", buf)); + } +} + + +#else +#define GCALC_DBUG_CHECK_COUNTER() do { } while(0) +#define GCALC_DBUG_PRINT_PI(pi) do { } while(0) +#define GCALC_DBUG_PRINT_SLICE(a, b) do { } while(0) +#define GCALC_DBUG_PRINT_INTERSECTIONS(a) do { } while(0) +#define GCALC_DBUG_PRINT_STATE(a) do { } while(0) +#endif /*GCALC_DBUG_OFF*/ + + +Gcalc_dyn_list::Gcalc_dyn_list(size_t blk_size, size_t sizeof_item): + m_blk_size(blk_size - ALLOC_ROOT_MIN_BLOCK_SIZE), + m_sizeof_item(ALIGN_SIZE(sizeof_item)), + m_points_per_blk((m_blk_size - PH_DATA_OFFSET) / m_sizeof_item), + m_blk_hook(&m_first_blk), + m_free(NULL), + m_keep(NULL) +{} + + +void Gcalc_dyn_list::format_blk(void* block) +{ + Item *pi_end, *cur_pi, *first_pi; + GCALC_DBUG_ASSERT(m_free == NULL); + first_pi= cur_pi= (Item *)(((char *)block) + PH_DATA_OFFSET); + pi_end= ptr_add(first_pi, m_points_per_blk - 1); + do { + cur_pi= cur_pi->next= ptr_add(cur_pi, 1); + } while (cur_pi<pi_end); + cur_pi->next= m_free; + m_free= first_pi; +} + + +Gcalc_dyn_list::Item *Gcalc_dyn_list::alloc_new_blk() +{ + void *new_block= my_malloc(m_blk_size, MYF(MY_WME)); + if (!new_block) + return NULL; + *m_blk_hook= new_block; + m_blk_hook= (void**)new_block; + format_blk(new_block); + return new_item(); +} + + +static void free_blk_list(void *list) +{ + void *next_blk; + while (list) + { + next_blk= *((void **)list); + my_free(list); + list= next_blk; + } +} + + +void Gcalc_dyn_list::cleanup() +{ + *m_blk_hook= NULL; + free_blk_list(m_first_blk); + m_first_blk= NULL; + m_blk_hook= &m_first_blk; + m_free= NULL; +} + + +Gcalc_dyn_list::~Gcalc_dyn_list() +{ + cleanup(); +} + + +void Gcalc_dyn_list::reset() +{ + *m_blk_hook= NULL; + if (m_first_blk) + { + free_blk_list(*((void **)m_first_blk)); + m_blk_hook= (void**)m_first_blk; + m_free= NULL; + format_blk(m_first_blk); + } +} + + +/* Internal coordinate operations implementations */ + +void gcalc_set_zero(Gcalc_internal_coord *d, int d_len) +{ + do + { + d[--d_len]= 0; + } while (d_len); +} + + +int gcalc_is_zero(const Gcalc_internal_coord *d, int d_len) +{ + do + { + if (d[--d_len] != 0) + return 0; + } while (d_len); + return 1; +} + + +#ifdef GCALC_CHECK_WITH_FLOAT +static double *gcalc_coord_extent= NULL; + +long double gcalc_get_double(const Gcalc_internal_coord *d, int d_len) +{ + int n= 1; + long double res= (long double) FIRST_DIGIT(d[0]); + do + { + res*= (long double) GCALC_DIG_BASE; + res+= (long double) d[n]; + } while(++n < d_len); + + n= 0; + do + { + if ((n & 1) && gcalc_coord_extent) + res/= *gcalc_coord_extent; + } while(++n < d_len); + + if (GCALC_SIGN(d[0])) + res*= -1.0; + return res; +} +#endif /*GCALC_CHECK_WITH_FLOAT*/ + + +static void do_add(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b) +{ + int n_digit= result_len-1; + gcalc_digit_t carry= 0; + + do + { + if ((result[n_digit]= + a[n_digit] + b[n_digit] + carry) >= GCALC_DIG_BASE) + { + carry= 1; + result[n_digit]-= GCALC_DIG_BASE; + } + else + carry= 0; + } while (--n_digit); + + result[0]= (a[0] + FIRST_DIGIT(b[0]) + carry); + + GCALC_DBUG_ASSERT(FIRST_DIGIT(result[0]) < GCALC_DIG_BASE); +} + + +static void do_sub(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b) +{ + int n_digit= result_len-1; + gcalc_digit_t carry= 0; + gcalc_digit_t cur_b, cur_a; + + do + { + cur_b= b[n_digit] + carry; + cur_a= a[n_digit]; + if (cur_a < cur_b) + { + carry= 1; + result[n_digit]= (GCALC_DIG_BASE - cur_b) + cur_a; + } + else + { + carry= 0; + result[n_digit]= cur_a - cur_b; + } + } while (--n_digit); + + + result[0]= a[0] - FIRST_DIGIT(b[0]) - carry; + + GCALC_DBUG_ASSERT(FIRST_DIGIT(a[0]) >= FIRST_DIGIT(b[0]) + carry); + GCALC_DBUG_ASSERT(!gcalc_is_zero(result, result_len)); +} +/* +static void do_sub(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b) +{ + int n_digit= result_len-1; + gcalc_digit_t carry= 0; + + do + { + if ((result[n_digit]= a[n_digit] - b[n_digit] - carry) < 0) + { + carry= 1; + result[n_digit]+= GCALC_DIG_BASE; + } + else + carry= 0; + } while (--n_digit); + + + result[0]= a[0] - FIRST_DIGIT(b[0]) - carry; + + GCALC_DBUG_ASSERT(FIRST_DIGIT(a[0]) - FIRST_DIGIT(b[0]) - carry >= 0); + GCALC_DBUG_ASSERT(!gcalc_is_zero(result, result_len)); +} +*/ + +static int do_cmp(const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b, int len) +{ + int n_digit= 1; + + if ((FIRST_DIGIT(a[0]) != FIRST_DIGIT(b[0]))) + return FIRST_DIGIT(a[0]) > FIRST_DIGIT(b[0]) ? 1 : -1; + + do + { + if ((a[n_digit] != b[n_digit])) + return a[n_digit] > b[n_digit] ? 1 : -1; + } while (++n_digit < len); + + return 0; +} + + +#ifdef GCALC_CHECK_WITH_FLOAT +static int de_weak_check(long double a, long double b, long double ex) +{ + long double d= a - b; + if (d < ex && d > -ex) + return 1; + + d/= fabsl(a) + fabsl(b); + if (d < ex && d > -ex) + return 1; + return 0; +} + +static int de_check(long double a, long double b) +{ + return de_weak_check(a, b, (long double) 1e-10); +} +#endif /*GCALC_CHECK_WITH_FLOAT*/ + + +void gcalc_mul_coord(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, int a_len, + const Gcalc_internal_coord *b, int b_len) +{ + GCALC_DBUG_ASSERT(result_len == a_len + b_len); + GCALC_DBUG_ASSERT(a_len >= b_len); + int n_a, n_b, n_res; + gcalc_digit_t carry= 0; + + gcalc_set_zero(result, result_len); + + n_a= a_len - 1; + do + { + gcalc_coord2 cur_a= n_a ? a[n_a] : FIRST_DIGIT(a[0]); + n_b= b_len - 1; + do + { + gcalc_coord2 cur_b= n_b ? b[n_b] : FIRST_DIGIT(b[0]); + gcalc_coord2 mul= cur_a * cur_b + carry + result[n_a + n_b + 1]; + result[n_a + n_b + 1]= mul % GCALC_DIG_BASE; + carry= (gcalc_digit_t) (mul / GCALC_DIG_BASE); + } while (n_b--); + if (carry) + { + for (n_res= n_a; (result[n_res]+= carry) >= GCALC_DIG_BASE; + n_res--) + { + result[n_res]-= GCALC_DIG_BASE; + carry= 1; + } + carry= 0; + } + } while (n_a--); + if (!gcalc_is_zero(result, result_len)) + result[0]|= GCALC_SIGN(a[0] ^ b[0]); +#ifdef GCALC_CHECK_WITH_FLOAT + GCALC_DBUG_ASSERT(de_check(gcalc_get_double(a, a_len) * + gcalc_get_double(b, b_len), + gcalc_get_double(result, result_len))); +#endif /*GCALC_CHECK_WITH_FLOAT*/ +} + + +inline void gcalc_mul_coord1(Gcalc_coord1 result, + const Gcalc_coord1 a, const Gcalc_coord1 b) +{ + return gcalc_mul_coord(result, GCALC_COORD_BASE2, + a, GCALC_COORD_BASE, b, GCALC_COORD_BASE); +} + + +void gcalc_add_coord(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b) +{ + if (GCALC_SIGN(a[0]) == GCALC_SIGN(b[0])) + do_add(result, result_len, a, b); + else + { + int cmp_res= do_cmp(a, b, result_len); + if (cmp_res == 0) + gcalc_set_zero(result, result_len); + else if (cmp_res > 0) + do_sub(result, result_len, a, b); + else + do_sub(result, result_len, b, a); + } +#ifdef GCALC_CHECK_WITH_FLOAT + GCALC_DBUG_ASSERT(de_check(gcalc_get_double(a, result_len) + + gcalc_get_double(b, result_len), + gcalc_get_double(result, result_len))); +#endif /*GCALC_CHECK_WITH_FLOAT*/ +} + + +void gcalc_sub_coord(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b) +{ + if (GCALC_SIGN(a[0] ^ b[0])) + do_add(result, result_len, a, b); + else + { + int cmp_res= do_cmp(a, b, result_len); + if (cmp_res == 0) + gcalc_set_zero(result, result_len); + else if (cmp_res > 0) + do_sub(result, result_len, a, b); + else + { + do_sub(result, result_len, b, a); + result[0]^= GCALC_COORD_MINUS; + } + } +#ifdef GCALC_CHECK_WITH_FLOAT + GCALC_DBUG_ASSERT(de_check(gcalc_get_double(a, result_len) - + gcalc_get_double(b, result_len), + gcalc_get_double(result, result_len))); +#endif /*GCALC_CHECK_WITH_FLOAT*/ +} + + +inline void gcalc_sub_coord1(Gcalc_coord1 result, + const Gcalc_coord1 a, const Gcalc_coord1 b) +{ + return gcalc_sub_coord(result, GCALC_COORD_BASE, a, b); +} + + +int gcalc_cmp_coord(const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b, int len) +{ + int n_digit= 0; + int result= 0; + + do + { + if (a[n_digit] == b[n_digit]) + { + n_digit++; + continue; + } + if (a[n_digit] > b[n_digit]) + result= GCALC_SIGN(a[0]) ? -1 : 1; + else + result= GCALC_SIGN(b[0]) ? 1 : -1; + break; + + } while (n_digit < len); + +#ifdef GCALC_CHECK_WITH_FLOAT + if (result == 0) + GCALC_DBUG_ASSERT(de_check(gcalc_get_double(a, len), + gcalc_get_double(b, len))); + else if (result == 1) + GCALC_DBUG_ASSERT(de_check(gcalc_get_double(a, len), + gcalc_get_double(b, len)) || + gcalc_get_double(a, len) > gcalc_get_double(b, len)); + else + GCALC_DBUG_ASSERT(de_check(gcalc_get_double(a, len), + gcalc_get_double(b, len)) || + gcalc_get_double(a, len) < gcalc_get_double(b, len)); +#endif /*GCALC_CHECK_WITH_FLOAT*/ + return result; +} + + +#define gcalc_cmp_coord1(a, b) gcalc_cmp_coord(a, b, GCALC_COORD_BASE) + +int gcalc_set_double(Gcalc_internal_coord *c, double d, double ext) +{ + int sign; + double ds= d * ext; + if ((sign= ds < 0)) + ds= -ds; + c[0]= (gcalc_digit_t) (ds / (double) GCALC_DIG_BASE); + c[1]= (gcalc_digit_t) (ds - ((double) c[0]) * (double) GCALC_DIG_BASE); + if (c[1] >= GCALC_DIG_BASE) + { + c[1]= 0; + c[0]++; + } + if (sign) + c[0]|= GCALC_COORD_MINUS; +#ifdef GCALC_CHECK_WITH_FLOAT + GCALC_DBUG_ASSERT(de_check(d, gcalc_get_double(c, 2))); +#endif /*GCALC_CHECK_WITH_FLOAT*/ + return 0; +} + + +typedef gcalc_digit_t Gcalc_coord4[GCALC_COORD_BASE*4]; +typedef gcalc_digit_t Gcalc_coord5[GCALC_COORD_BASE*5]; + + +void Gcalc_scan_iterator::intersection_info::do_calc_t() +{ + Gcalc_coord1 a2_a1x, a2_a1y; + Gcalc_coord2 x1y2, x2y1; + + gcalc_sub_coord1(a2_a1x, edge_b->pi->ix, edge_a->pi->ix); + gcalc_sub_coord1(a2_a1y, edge_b->pi->iy, edge_a->pi->iy); + + GCALC_DBUG_ASSERT(!gcalc_is_zero(edge_a->dy, GCALC_COORD_BASE) || + !gcalc_is_zero(edge_b->dy, GCALC_COORD_BASE)); + + gcalc_mul_coord1(x1y2, edge_a->dx, edge_b->dy); + gcalc_mul_coord1(x2y1, edge_a->dy, edge_b->dx); + gcalc_sub_coord(t_b, GCALC_COORD_BASE2, x1y2, x2y1); + + + gcalc_mul_coord1(x1y2, a2_a1x, edge_b->dy); + gcalc_mul_coord1(x2y1, a2_a1y, edge_b->dx); + gcalc_sub_coord(t_a, GCALC_COORD_BASE2, x1y2, x2y1); + t_calculated= 1; +} + + +void Gcalc_scan_iterator::intersection_info::do_calc_y() +{ + GCALC_DBUG_ASSERT(t_calculated); + + Gcalc_coord3 a_tb, b_ta; + + gcalc_mul_coord(a_tb, GCALC_COORD_BASE3, + t_b, GCALC_COORD_BASE2, edge_a->pi->iy, GCALC_COORD_BASE); + gcalc_mul_coord(b_ta, GCALC_COORD_BASE3, + t_a, GCALC_COORD_BASE2, edge_a->dy, GCALC_COORD_BASE); + + gcalc_add_coord(y_exp, GCALC_COORD_BASE3, a_tb, b_ta); + y_calculated= 1; +} + + +void Gcalc_scan_iterator::intersection_info::do_calc_x() +{ + GCALC_DBUG_ASSERT(t_calculated); + + Gcalc_coord3 a_tb, b_ta; + + gcalc_mul_coord(a_tb, GCALC_COORD_BASE3, + t_b, GCALC_COORD_BASE2, edge_a->pi->ix, GCALC_COORD_BASE); + gcalc_mul_coord(b_ta, GCALC_COORD_BASE3, + t_a, GCALC_COORD_BASE2, edge_a->dx, GCALC_COORD_BASE); + + gcalc_add_coord(x_exp, GCALC_COORD_BASE3, a_tb, b_ta); + x_calculated= 1; +} + + +static int cmp_node_isc(const Gcalc_heap::Info *node, + const Gcalc_heap::Info *isc) +{ + GCALC_DBUG_ASSERT(node->type == Gcalc_heap::nt_shape_node); + Gcalc_scan_iterator::intersection_info *inf= i_data(isc); + Gcalc_coord3 exp; + int result; + + inf->calc_t(); + inf->calc_y_exp(); + + gcalc_mul_coord(exp, GCALC_COORD_BASE3, + inf->t_b, GCALC_COORD_BASE2, node->iy, GCALC_COORD_BASE); + + result= gcalc_cmp_coord(exp, inf->y_exp, GCALC_COORD_BASE3); +#ifdef GCALC_CHECK_WITH_FLOAT + long double int_x, int_y; + isc->calc_xy_ld(&int_x, &int_y); + if (result < 0) + { + if (!de_check(int_y, node->y) && node->y > int_y) + GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g < %LG", node->y, int_y)); + } + else if (result > 0) + { + if (!de_check(int_y, node->y) && node->y < int_y) + GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g > %LG", node->y, int_y)); + } + else + { + if (!de_check(int_y, node->y)) + GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g == %LG", node->y, int_y)); + } +#endif /*GCALC_CHECK_WITH_FLOAT*/ + if (result) + goto exit; + + + inf->calc_x_exp(); + gcalc_mul_coord(exp, GCALC_COORD_BASE3, + inf->t_b, GCALC_COORD_BASE2, node->ix, GCALC_COORD_BASE); + + result= gcalc_cmp_coord(exp, inf->x_exp, GCALC_COORD_BASE3); +#ifdef GCALC_CHECK_WITH_FLOAT + if (result < 0) + { + if (!de_check(int_x, node->x) && node->x > int_x) + GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g < %LG", + node->x, int_x)); + } + else if (result > 0) + { + if (!de_check(int_x, node->x) && node->x < int_x) + GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g > %LG", + node->x, int_x)); + } + else + { + if (!de_check(int_x, node->x)) + GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g == %LG", + node->x, int_x)); + } +#endif /*GCALC_CHECK_WITH_FLOAT*/ +exit: + return result; +} + + +static int cmp_intersections(const Gcalc_heap::Info *i1, + const Gcalc_heap::Info *i2) +{ + Gcalc_scan_iterator::intersection_info *inf1= i_data(i1); + Gcalc_scan_iterator::intersection_info *inf2= i_data(i2); + Gcalc_coord5 exp_a, exp_b; + int result; + + inf1->calc_t(); + inf2->calc_t(); + + inf1->calc_y_exp(); + inf2->calc_y_exp(); + + gcalc_mul_coord(exp_a, GCALC_COORD_BASE5, + inf1->y_exp, GCALC_COORD_BASE3, inf2->t_b, GCALC_COORD_BASE2); + gcalc_mul_coord(exp_b, GCALC_COORD_BASE5, + inf2->y_exp, GCALC_COORD_BASE3, inf1->t_b, GCALC_COORD_BASE2); + + result= gcalc_cmp_coord(exp_a, exp_b, GCALC_COORD_BASE5); +#ifdef GCALC_CHECK_WITH_FLOAT + long double x1, y1, x2, y2; + i1->calc_xy_ld(&x1, &y1); + i2->calc_xy_ld(&x2, &y2); + + if (result < 0) + { + if (!de_check(y1, y2) && y2 > y1) + GCALC_DBUG_PRINT(("floatcheck cmp_intersections_y failed %LG < %LG", + y2, y1)); + } + else if (result > 0) + { + if (!de_check(y1, y2) && y2 < y1) + GCALC_DBUG_PRINT(("floatcheck cmp_intersections_y failed %LG > %LG", + y2, y1)); + } + else + { + if (!de_check(y1, y2)) + GCALC_DBUG_PRINT(("floatcheck cmp_intersections_y failed %LG == %LG", + y2, y1)); + } +#endif /*GCALC_CHECK_WITH_FLOAT*/ + + if (result != 0) + return result; + + + inf1->calc_x_exp(); + inf2->calc_x_exp(); + gcalc_mul_coord(exp_a, GCALC_COORD_BASE5, + inf1->x_exp, GCALC_COORD_BASE3, inf2->t_b, GCALC_COORD_BASE2); + gcalc_mul_coord(exp_b, GCALC_COORD_BASE5, + inf2->x_exp, GCALC_COORD_BASE3, inf1->t_b, GCALC_COORD_BASE2); + + result= gcalc_cmp_coord(exp_a, exp_b, GCALC_COORD_BASE5); +#ifdef GCALC_CHECK_WITH_FLOAT + if (result < 0) + { + if (!de_check(x1, x2) && x2 > x1) + GCALC_DBUG_PRINT(("floatcheck cmp_intersectionsx failed %LG < %LG", + x2, x1)); + } + else if (result > 0) + { + if (!de_check(x1, x2) && x2 < x1) + GCALC_DBUG_PRINT(("floatcheck cmp_intersectionsx failed %LG > %LG", + x2, x1)); + } + else + { + if (!de_check(x1, x2)) + GCALC_DBUG_PRINT(("floatcheck cmp_intersectionsx failed %LG == %LG", + x2, x1)); + } +#endif /*GCALC_CHECK_WITH_FLOAT*/ + return result; +} +/* Internal coordinates implementation end */ + + +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; + *m_hook= result; + m_hook= &result->next; + result->x= x; + result->y= 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; + + m_n_points++; + return result; +} + + +static Gcalc_heap::Info *new_intersection( + Gcalc_heap *heap, Gcalc_scan_iterator::intersection_info *ii) +{ + Gcalc_heap::Info *isc= (Gcalc_heap::Info *)heap->new_item(); + if (!isc) + return 0; + isc->type= Gcalc_heap::nt_intersection; + isc->p1= ii->edge_a->pi; + isc->p2= ii->edge_a->next_pi; + isc->p3= ii->edge_b->pi; + isc->p4= ii->edge_b->next_pi; + isc->intersection_data= ii; + return isc; +} + + +static Gcalc_heap::Info *new_eq_point( + Gcalc_heap *heap, const Gcalc_heap::Info *p, + Gcalc_scan_iterator::point *edge) +{ + Gcalc_heap::Info *eqp= (Gcalc_heap::Info *)heap->new_item(); + if (!eqp) + return 0; + eqp->type= Gcalc_heap::nt_eq_node; + eqp->node= p; + eqp->eq_data= edge; + return eqp; +} + + +void Gcalc_heap::Info::calc_xy(double *x, double *y) const +{ + double b0_x= p2->x - p1->x; + double b0_y= p2->y - p1->y; + double b1_x= p4->x - p3->x; + double b1_y= p4->y - p3->y; + double b0xb1= b0_x * b1_y - b0_y * b1_x; + double t= (p3->x - p1->x) * b1_y - (p3->y - p1->y) * b1_x; + + t/= b0xb1; + + *x= p1->x + b0_x * t; + *y= p1->y + b0_y * t; +} + + +#ifdef GCALC_CHECK_WITH_FLOAT +void Gcalc_heap::Info::calc_xy_ld(long double *x, long double *y) const +{ + long double b0_x= ((long double) p2->x) - p1->x; + long double b0_y= ((long double) p2->y) - p1->y; + long double b1_x= ((long double) p4->x) - p3->x; + long double b1_y= ((long double) p4->y) - p3->y; + long double b0xb1= b0_x * b1_y - b0_y * b1_x; + long double ax= ((long double) p3->x) - p1->x; + long double ay= ((long double) p3->y) - p1->y; + long double t_a= ax * b1_y - ay * b1_x; + long double hx= (b0xb1 * (long double) p1->x + b0_x * t_a); + long double hy= (b0xb1 * (long double) p1->y + b0_y * t_a); + + if (fabs(b0xb1) < 1e-15) + { + *x= p1->x; + *y= p1->y; + return; + } + + *x= hx/b0xb1; + *y= hy/b0xb1; +} +#endif /*GCALC_CHECK_WITH_FLOAT*/ + + +static int cmp_point_info(const Gcalc_heap::Info *i0, + const Gcalc_heap::Info *i1) +{ + int cmp_y= gcalc_cmp_coord1(i0->iy, i1->iy); + if (cmp_y) + return cmp_y; + return gcalc_cmp_coord1(i0->ix, i1->ix); +} + + +static inline void trim_node(Gcalc_heap::Info *node, Gcalc_heap::Info *prev_node) +{ + if (!node) + return; + node->top_node= 0; + GCALC_DBUG_ASSERT((node->left == prev_node) || (node->right == prev_node)); + if (node->left == prev_node) + node->left= node->right; + node->right= NULL; + GCALC_DBUG_ASSERT(cmp_point_info(node, prev_node)); +} + + +static int compare_point_info(const void *e0, const void *e1) +{ + const Gcalc_heap::Info *i0= (const Gcalc_heap::Info *)e0; + const Gcalc_heap::Info *i1= (const Gcalc_heap::Info *)e1; + return cmp_point_info(i0, i1) > 0; +} + + +#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 */ + for (cur= get_first(); cur; cur= cur->get_next()) + { + trim_node(cur->left, cur); + trim_node(cur->right, cur); + } +} + + +void Gcalc_heap::reset() +{ + if (m_n_points) + { + free_list(m_first); + m_n_points= 0; + } + m_hook= &m_first; +} + + +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_ASSERT(!m_prev || m_prev->x != x || m_prev->y != 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() +{ + GCALC_DBUG_ASSERT(m_shape_started == 1 || m_shape_started == 3); + + 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; + } + + 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; +} + + +inline void calc_dx_dy(Gcalc_scan_iterator::point *p) +{ + gcalc_sub_coord1(p->dx, p->next_pi->ix, p->pi->ix); + gcalc_sub_coord1(p->dy, p->next_pi->iy, p->pi->iy); + if (GCALC_SIGN(p->dx[0])) + { + p->l_border= &p->next_pi->ix; + p->r_border= &p->pi->ix; + } + else + { + p->r_border= &p->next_pi->ix; + p->l_border= &p->pi->ix; + } +} + + +Gcalc_scan_iterator::Gcalc_scan_iterator(size_t blk_size) : + Gcalc_dyn_list(blk_size, sizeof(point) > sizeof(intersection_info) ? + sizeof(point) : + sizeof(intersection_info)) +{ + state.slice= NULL; + m_bottom_points= NULL; + m_bottom_hook= &m_bottom_points; +} + + +void Gcalc_scan_iterator::init(Gcalc_heap *points) +{ + GCALC_DBUG_ASSERT(points->ready()); + GCALC_DBUG_ASSERT(!state.slice); + + if (!(m_cur_pi= points->get_first())) + return; + m_heap= points; + state.event_position_hook= &state.slice; + state.event_end= NULL; +#ifndef GCALC_DBUG_OFF + m_cur_thread= 0; +#endif /*GCALC_DBUG_OFF*/ + GCALC_SET_TERMINATED(killed, 0); +} + +void Gcalc_scan_iterator::reset() +{ + state.slice= NULL; + m_bottom_points= NULL; + m_bottom_hook= &m_bottom_points; + Gcalc_dyn_list::reset(); +} + + +int Gcalc_scan_iterator::point::cmp_dx_dy(const Gcalc_coord1 dx_a, + const Gcalc_coord1 dy_a, + const Gcalc_coord1 dx_b, + const Gcalc_coord1 dy_b) +{ + Gcalc_coord2 dx_a_dy_b; + Gcalc_coord2 dy_a_dx_b; + gcalc_mul_coord1(dx_a_dy_b, dx_a, dy_b); + gcalc_mul_coord1(dy_a_dx_b, dy_a, dx_b); + + return gcalc_cmp_coord(dx_a_dy_b, dy_a_dx_b, GCALC_COORD_BASE2); +} + + +int Gcalc_scan_iterator::point::cmp_dx_dy(const Gcalc_heap::Info *p1, + const Gcalc_heap::Info *p2, + const Gcalc_heap::Info *p3, + const Gcalc_heap::Info *p4) +{ + Gcalc_coord1 dx_a, dy_a, dx_b, dy_b; + gcalc_sub_coord1(dx_a, p2->ix, p1->ix); + gcalc_sub_coord1(dy_a, p2->iy, p1->iy); + gcalc_sub_coord1(dx_b, p4->ix, p3->ix); + gcalc_sub_coord1(dy_b, p4->iy, p3->iy); + return cmp_dx_dy(dx_a, dy_a, dx_b, dy_b); +} + + +int Gcalc_scan_iterator::point::cmp_dx_dy(const point *p) const +{ + GCALC_DBUG_ASSERT(!is_bottom()); + return cmp_dx_dy(dx, dy, p->dx, p->dy); +} + + +#ifdef GCALC_CHECK_WITH_FLOAT +void Gcalc_scan_iterator::point::calc_x(long double *x, long double y, + long double ix) const +{ + long double ddy= gcalc_get_double(dy, GCALC_COORD_BASE); + if (fabsl(ddy) < (long double) 1e-20) + { + *x= ix; + } + else + *x= (ddy * (long double) pi->x + gcalc_get_double(dx, GCALC_COORD_BASE) * + (y - pi->y)) / ddy; +} +#endif /*GCALC_CHECK_WITH_FLOAT*/ + + +static int compare_events(const void *e0, const void *e1) +{ + const Gcalc_scan_iterator::point *p0= (const Gcalc_scan_iterator::point *)e0; + const Gcalc_scan_iterator::point *p1= (const Gcalc_scan_iterator::point *)e1; + return p0->cmp_dx_dy(p1) > 0; +} + + +int Gcalc_scan_iterator::arrange_event(int do_sorting, int n_intersections) +{ + int ev_counter; + point *sp; + point **sp_hook; + + ev_counter= 0; + + *m_bottom_hook= NULL; + for (sp= m_bottom_points; sp; sp= sp->get_next()) + sp->ev_next= sp->get_next(); + + for (sp= state.slice, sp_hook= &state.slice; + sp; sp_hook= sp->next_ptr(), sp= sp->get_next()) + { + if (sp->event) + { + state.event_position_hook= sp_hook; + break; + } + } + + for (sp= *(sp_hook= state.event_position_hook); + sp && sp->event; sp_hook= sp->next_ptr(), sp= sp->get_next()) + { + ev_counter++; + if (sp->get_next() && sp->get_next()->event) + sp->ev_next= sp->get_next(); + else + sp->ev_next= m_bottom_points; + } + +#ifndef GCALC_DBUG_OFF + { + point *cur_p= sp; + for (; cur_p; cur_p= cur_p->get_next()) + GCALC_DBUG_ASSERT(!cur_p->event); + } +#endif /*GCALC_DBUG_OFF*/ + + state.event_end= sp; + + if (ev_counter == 2 && n_intersections == 1) + { + /* If we had only intersection, just swap the two points. */ + sp= *state.event_position_hook; + *state.event_position_hook= sp->get_next(); + sp->next= (*state.event_position_hook)->next; + (*state.event_position_hook)->next= sp; + + /* The list of the events should be restored. */ + (*state.event_position_hook)->ev_next= sp; + sp->ev_next= m_bottom_points; + } + else if (ev_counter == 2 && get_events()->event == scev_two_threads) + { + /* Do nothing. */ + } + else if (ev_counter > 1 && do_sorting) + { + point *cur_p; + *sp_hook= NULL; + sp= (point *) sort_list(compare_events, *state.event_position_hook, + ev_counter); + /* Find last item in the list, it's changed after the sorting. */ + for (cur_p= sp->get_next(); cur_p->get_next(); + cur_p= cur_p->get_next()) + {} + cur_p->next= state.event_end; + *state.event_position_hook= sp; + /* The list of the events should be restored. */ + for (; sp && sp->event; sp= sp->get_next()) + { + if (sp->get_next() && sp->get_next()->event) + sp->ev_next= sp->get_next(); + else + sp->ev_next= m_bottom_points; + } + } + +#ifndef GCALC_DBUG_OFF + { + const event_point *ev= get_events(); + for (; ev && ev->get_next(); ev= ev->get_next()) + { + if (ev->is_bottom() || ev->get_next()->is_bottom()) + break; + GCALC_DBUG_ASSERT(ev->cmp_dx_dy(ev->get_next()) <= 0); + } + } +#endif /*GCALC_DBUG_OFF*/ + return 0; +} + + +int Gcalc_heap::Info::equal_pi(const Info *pi) const +{ + if (type == nt_intersection) + return equal_intersection; + if (pi->type == nt_eq_node) + return 1; + if (type == nt_eq_node || pi->type == nt_intersection) + return 0; + return cmp_point_info(this, pi) == 0; +} + +int Gcalc_scan_iterator::step() +{ + int result= 0; + int do_sorting= 0; + int n_intersections= 0; + point *sp; + GCALC_DBUG_ENTER("Gcalc_scan_iterator::step"); + GCALC_DBUG_ASSERT(more_points()); + + if (GCALC_TERMINATED(killed)) + GCALC_DBUG_RETURN(0xFFFF); + + /* Clear the old event marks. */ + if (m_bottom_points) + { + free_list((Gcalc_dyn_list::Item **) &m_bottom_points, + (Gcalc_dyn_list::Item **) m_bottom_hook); + m_bottom_points= NULL; + m_bottom_hook= &m_bottom_points; + } + for (sp= *state.event_position_hook; + sp != state.event_end; sp= sp->get_next()) + sp->event= scev_none; + +//#ifndef GCALC_DBUG_OFF + state.event_position_hook= NULL; + state.pi= NULL; +//#endif /*GCALC_DBUG_OFF*/ + + do + { +#ifndef GCALC_DBUG_OFF + if (m_cur_pi->type == Gcalc_heap::nt_intersection && + m_cur_pi->get_next()->type == Gcalc_heap::nt_intersection && + m_cur_pi->equal_intersection) + GCALC_DBUG_ASSERT(cmp_intersections(m_cur_pi, m_cur_pi->get_next()) == 0); +#endif /*GCALC_DBUG_OFF*/ + GCALC_DBUG_CHECK_COUNTER(); + GCALC_DBUG_PRINT_SLICE("step:", state.slice); + GCALC_DBUG_PRINT_PI(m_cur_pi); + if (m_cur_pi->type == Gcalc_heap::nt_shape_node) + { + if (m_cur_pi->is_top()) + { + result= insert_top_node(); + if (!m_cur_pi->is_bottom()) + do_sorting++; + } + else if (m_cur_pi->is_bottom()) + remove_bottom_node(); + else + { + do_sorting++; + result= node_scan(); + } + if (result) + GCALC_DBUG_RETURN(result); + state.pi= m_cur_pi; + } + else if (m_cur_pi->type == Gcalc_heap::nt_eq_node) + { + do_sorting++; + eq_scan(); + } + else + { + /* nt_intersection */ + do_sorting++; + n_intersections++; + intersection_scan(); + if (!state.pi || state.pi->type == Gcalc_heap::nt_intersection) + state.pi= m_cur_pi; + } + + m_cur_pi= m_cur_pi->get_next(); + } while (m_cur_pi && state.pi->equal_pi(m_cur_pi)); + + GCALC_DBUG_RETURN(arrange_event(do_sorting, n_intersections)); +} + + +static int node_on_right(const Gcalc_heap::Info *node, + const Gcalc_heap::Info *edge_a, const Gcalc_heap::Info *edge_b) +{ + Gcalc_coord1 a_x, a_y; + Gcalc_coord1 b_x, b_y; + Gcalc_coord2 ax_by, ay_bx; + int result; + + gcalc_sub_coord1(a_x, node->ix, edge_a->ix); + gcalc_sub_coord1(a_y, node->iy, edge_a->iy); + gcalc_sub_coord1(b_x, edge_b->ix, edge_a->ix); + gcalc_sub_coord1(b_y, edge_b->iy, edge_a->iy); + gcalc_mul_coord1(ax_by, a_x, b_y); + gcalc_mul_coord1(ay_bx, a_y, b_x); + result= gcalc_cmp_coord(ax_by, ay_bx, GCALC_COORD_BASE2); +#ifdef GCALC_CHECK_WITH_FLOAT + { + long double dx= gcalc_get_double(edge_b->ix, GCALC_COORD_BASE) - + gcalc_get_double(edge_a->ix, GCALC_COORD_BASE); + long double dy= gcalc_get_double(edge_b->iy, GCALC_COORD_BASE) - + gcalc_get_double(edge_a->iy, GCALC_COORD_BASE); + long double ax= gcalc_get_double(node->ix, GCALC_COORD_BASE) - + gcalc_get_double(edge_a->ix, GCALC_COORD_BASE); + long double ay= gcalc_get_double(node->iy, GCALC_COORD_BASE) - + gcalc_get_double(edge_a->iy, GCALC_COORD_BASE); + long double d= ax * dy - ay * dx; + if (result == 0) + GCALC_DBUG_ASSERT(de_check(d, 0.0)); + else if (result < 0) + GCALC_DBUG_ASSERT(de_check(d, 0.0) || d < 0); + else + GCALC_DBUG_ASSERT(de_check(d, 0.0) || d > 0); + } +#endif /*GCALC_CHECK_WITH_FLOAT*/ + return result; +} + + +static int cmp_tops(const Gcalc_heap::Info *top_node, + const Gcalc_heap::Info *edge_a, const Gcalc_heap::Info *edge_b) +{ + int cmp_res_a, cmp_res_b; + + cmp_res_a= gcalc_cmp_coord1(edge_a->ix, top_node->ix); + cmp_res_b= gcalc_cmp_coord1(edge_b->ix, top_node->ix); + + if (cmp_res_a <= 0 && cmp_res_b > 0) + return -1; + if (cmp_res_b <= 0 && cmp_res_a > 0) + return 1; + if (cmp_res_a == 0 && cmp_res_b == 0) + return 0; + + return node_on_right(edge_a, top_node, edge_b); +} + + +int Gcalc_scan_iterator::insert_top_node() +{ + point *sp= state.slice; + point **prev_hook= &state.slice; + point *sp1= NULL; + point *sp0= new_slice_point(); + int cmp_res; + + GCALC_DBUG_ENTER("Gcalc_scan_iterator::insert_top_node"); + if (!sp0) + GCALC_DBUG_RETURN(1); + sp0->pi= m_cur_pi; + sp0->next_pi= m_cur_pi->left; +#ifndef GCALC_DBUG_OFF + sp0->thread= m_cur_thread++; +#endif /*GCALC_DBUG_OFF*/ + if (m_cur_pi->left) + { + calc_dx_dy(sp0); + if (m_cur_pi->right) + { + if (!(sp1= new_slice_point())) + GCALC_DBUG_RETURN(1); + sp1->event= sp0->event= scev_two_threads; + sp1->pi= m_cur_pi; + sp1->next_pi= m_cur_pi->right; +#ifndef GCALC_DBUG_OFF + sp1->thread= m_cur_thread++; +#endif /*GCALC_DBUG_OFF*/ + calc_dx_dy(sp1); + /* We have two threads so should decide which one will be first */ + cmp_res= cmp_tops(m_cur_pi, m_cur_pi->left, m_cur_pi->right); + if (cmp_res > 0) + { + point *tmp= sp0; + sp0= sp1; + sp1= tmp; + } + else if (cmp_res == 0) + { + /* Exactly same direction of the edges. */ + cmp_res= gcalc_cmp_coord1(m_cur_pi->left->iy, m_cur_pi->right->iy); + if (cmp_res != 0) + { + if (cmp_res < 0) + { + if (add_eq_node(sp0->next_pi, sp1)) + GCALC_DBUG_RETURN(1); + } + else + { + if (add_eq_node(sp1->next_pi, sp0)) + GCALC_DBUG_RETURN(1); + } + } + else + { + cmp_res= gcalc_cmp_coord1(m_cur_pi->left->ix, m_cur_pi->right->ix); + if (cmp_res != 0) + { + if (cmp_res < 0) + { + if (add_eq_node(sp0->next_pi, sp1)) + GCALC_DBUG_RETURN(1); + } + else + { + if (add_eq_node(sp1->next_pi, sp0)) + GCALC_DBUG_RETURN(1); + } + } + } + } + } + else + sp0->event= scev_thread; + } + else + sp0->event= scev_single_point; + + + /* Check if we already have an event - then we'll place the node there */ + for (; sp && !sp->event; prev_hook= sp->next_ptr(), sp=sp->get_next()) + {} + if (!sp) + { + sp= state.slice; + prev_hook= &state.slice; + /* We need to find the place to insert. */ + for (; sp; prev_hook= sp->next_ptr(), sp=sp->get_next()) + { + if (sp->event || gcalc_cmp_coord1(*sp->r_border, m_cur_pi->ix) < 0) + continue; + cmp_res= node_on_right(m_cur_pi, sp->pi, sp->next_pi); + if (cmp_res == 0) + { + /* The top node lies on the edge. */ + /* Nodes of that edge will be handled in other places. */ + sp->event= scev_intersection; + } + else if (cmp_res < 0) + break; + } + } + + if (sp0->event == scev_single_point) + { + /* Add single point to the bottom list. */ + *m_bottom_hook= sp0; + m_bottom_hook= sp0->next_ptr(); + state.event_position_hook= prev_hook; + } + else + { + *prev_hook= sp0; + sp0->next= sp; + if (add_events_for_node(sp0)) + GCALC_DBUG_RETURN(1); + + if (sp0->event == scev_two_threads) + { + *prev_hook= sp1; + sp1->next= sp; + if (add_events_for_node(sp1)) + GCALC_DBUG_RETURN(1); + + sp0->next= sp1; + *prev_hook= sp0; + } + } + + GCALC_DBUG_RETURN(0); +} + + +void Gcalc_scan_iterator::remove_bottom_node() +{ + point *sp= state.slice; + point **sp_hook= &state.slice; + point *first_bottom_point= NULL; + + GCALC_DBUG_ENTER("Gcalc_scan_iterator::remove_bottom_node"); + for (; sp; sp= sp->get_next()) + { + if (sp->next_pi == m_cur_pi) + { + *sp_hook= sp->get_next(); + sp->pi= m_cur_pi; + sp->next_pi= NULL; + if (first_bottom_point) + { + first_bottom_point->event= sp->event= scev_two_ends; + break; + } + first_bottom_point= sp; + sp->event= scev_end; + state.event_position_hook= sp_hook; + } + else + sp_hook= sp->next_ptr(); + } + GCALC_DBUG_ASSERT(first_bottom_point); + *m_bottom_hook= first_bottom_point; + m_bottom_hook= first_bottom_point->next_ptr(); + if (sp) + { + *m_bottom_hook= sp; + m_bottom_hook= sp->next_ptr(); + } + + GCALC_DBUG_VOID_RETURN; +} + + +int Gcalc_scan_iterator::add_events_for_node(point *sp_node) +{ + point *sp= state.slice; + int cur_pi_r, sp_pi_r; + + GCALC_DBUG_ENTER("Gcalc_scan_iterator::add_events_for_node"); + + /* Scan to the event point. */ + for (; sp != sp_node; sp= sp->get_next()) + { + GCALC_DBUG_ASSERT(!sp->is_bottom()); + GCALC_DBUG_PRINT(("left cut_edge %d", sp->thread)); + if (sp->next_pi == sp_node->next_pi || + gcalc_cmp_coord1(*sp->r_border, *sp_node->l_border) < 0) + continue; + sp_pi_r= node_on_right(sp->next_pi, sp_node->pi, sp_node->next_pi); + if (sp_pi_r < 0) + continue; + cur_pi_r= node_on_right(sp_node->next_pi, sp->pi, sp->next_pi); + if (cur_pi_r > 0) + continue; + if (cur_pi_r == 0 && sp_pi_r == 0) + { + int cmp_res= cmp_point_info(sp->next_pi, sp_node->next_pi); + if (cmp_res > 0) + { + if (add_eq_node(sp_node->next_pi, sp)) + GCALC_DBUG_RETURN(1); + } + else if (cmp_res < 0) + { + if (add_eq_node(sp->next_pi, sp_node)) + GCALC_DBUG_RETURN(1); + } + continue; + } + + if (cur_pi_r == 0) + { + if (add_eq_node(sp_node->next_pi, sp)) + GCALC_DBUG_RETURN(1); + continue; + } + else if (sp_pi_r == 0) + { + if (add_eq_node(sp->next_pi, sp_node)) + GCALC_DBUG_RETURN(1); + continue; + } + + if (sp->event) + { +#ifndef GCALC_DBUG_OFF + cur_pi_r= node_on_right(sp_node->pi, sp->pi, sp->next_pi); + GCALC_DBUG_ASSERT(cur_pi_r == 0); +#endif /*GCALC_DBUG_OFF*/ + continue; + } + cur_pi_r= node_on_right(sp_node->pi, sp->pi, sp->next_pi); + GCALC_DBUG_ASSERT(cur_pi_r >= 0); + //GCALC_DBUG_ASSERT(cur_pi_r > 0); /* Is it ever violated? */ + if (cur_pi_r > 0 && add_intersection(sp, sp_node, m_cur_pi)) + GCALC_DBUG_RETURN(1); + } + + /* Scan to the end of the slice */ + sp= sp->get_next(); + + for (; sp; sp= sp->get_next()) + { + GCALC_DBUG_ASSERT(!sp->is_bottom()); + GCALC_DBUG_PRINT(("right cut_edge %d", sp->thread)); + if (sp->next_pi == sp_node->next_pi || + gcalc_cmp_coord1(*sp_node->r_border, *sp->l_border) < 0) + continue; + sp_pi_r= node_on_right(sp->next_pi, sp_node->pi, sp_node->next_pi); + if (sp_pi_r > 0) + continue; + cur_pi_r= node_on_right(sp_node->next_pi, sp->pi, sp->next_pi); + if (cur_pi_r < 0) + continue; + if (cur_pi_r == 0 && sp_pi_r == 0) + { + int cmp_res= cmp_point_info(sp->next_pi, sp_node->next_pi); + if (cmp_res > 0) + { + if (add_eq_node(sp_node->next_pi, sp)) + GCALC_DBUG_RETURN(1); + } + else if (cmp_res < 0) + { + if (add_eq_node(sp->next_pi, sp_node)) + GCALC_DBUG_RETURN(1); + } + continue; + } + if (cur_pi_r == 0) + { + if (add_eq_node(sp_node->next_pi, sp)) + GCALC_DBUG_RETURN(1); + continue; + } + else if (sp_pi_r == 0) + { + if (add_eq_node(sp->next_pi, sp_node)) + GCALC_DBUG_RETURN(1); + continue; + } + + if (sp->event) + { +#ifndef GCALC_DBUG_OFF + cur_pi_r= node_on_right(sp_node->pi, sp->pi, sp->next_pi); + GCALC_DBUG_ASSERT(cur_pi_r == 0); +#endif /*GCALC_DBUG_OFF*/ + continue; + } + cur_pi_r= node_on_right(sp_node->pi, sp->pi, sp->next_pi); + GCALC_DBUG_ASSERT(cur_pi_r <= 0); + //GCALC_DBUG_ASSERT(cur_pi_r < 0); /* Is it ever violated? */ + if (cur_pi_r < 0 && add_intersection(sp_node, sp, m_cur_pi)) + GCALC_DBUG_RETURN(1); + } + + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_scan_iterator::node_scan() +{ + point *sp= state.slice; + Gcalc_heap::Info *cur_pi= m_cur_pi; + + GCALC_DBUG_ENTER("Gcalc_scan_iterator::node_scan"); + + /* Scan to the event point. */ + /* Can be avoided if we add link to the sp to the Info. */ + for (; sp->next_pi != cur_pi; sp= sp->get_next()) + {} + + GCALC_DBUG_PRINT(("node for %d", sp->thread)); + /* Handle the point itself. */ + sp->pi= cur_pi; + sp->next_pi= cur_pi->left; + sp->event= scev_point; + calc_dx_dy(sp); + + GCALC_DBUG_RETURN(add_events_for_node(sp)); +} + + +void Gcalc_scan_iterator::eq_scan() +{ + point *sp= eq_sp(m_cur_pi); + GCALC_DBUG_ENTER("Gcalc_scan_iterator::eq_scan"); + +#ifndef GCALC_DBUG_OFF + { + point *cur_p= state.slice; + for (; cur_p && cur_p != sp; cur_p= cur_p->get_next()) + {} + GCALC_DBUG_ASSERT(cur_p); + } +#endif /*GCALC_DBUG_OFF*/ + if (!sp->event) + { + sp->event= scev_intersection; + sp->ev_pi= m_cur_pi; + } + + GCALC_DBUG_VOID_RETURN; +} + + +void Gcalc_scan_iterator::intersection_scan() +{ + intersection_info *ii= i_data(m_cur_pi); + GCALC_DBUG_ENTER("Gcalc_scan_iterator::intersection_scan"); + +#ifndef GCALC_DBUG_OFF + { + point *sp= state.slice; + for (; sp && sp != ii->edge_a; sp= sp->get_next()) + {} + GCALC_DBUG_ASSERT(sp); + for (; sp && sp != ii->edge_b; sp= sp->get_next()) + {} + GCALC_DBUG_ASSERT(sp); + } +#endif /*GCALC_DBUG_OFF*/ + + ii->edge_a->event= ii->edge_b->event= scev_intersection; + ii->edge_a->ev_pi= ii->edge_b->ev_pi= m_cur_pi; + free_item(ii); + m_cur_pi->intersection_data= NULL; + + GCALC_DBUG_VOID_RETURN; +} + + +int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b, + Gcalc_heap::Info *pi_from) +{ + Gcalc_heap::Info *ii; + intersection_info *i_calc; + int cmp_res; + int skip_next= 0; + + GCALC_DBUG_ENTER("Gcalc_scan_iterator::add_intersection"); + if (!(i_calc= new_intersection_info(sp_a, sp_b)) || + !(ii= new_intersection(m_heap, i_calc))) + GCALC_DBUG_RETURN(1); + + ii->equal_intersection= 0; + + for (; + pi_from->get_next() != sp_a->next_pi && + pi_from->get_next() != sp_b->next_pi; + pi_from= pi_from->get_next()) + { + Gcalc_heap::Info *cur= pi_from->get_next(); + if (skip_next) + { + if (cur->type == Gcalc_heap::nt_intersection) + skip_next= cur->equal_intersection; + else + skip_next= 0; + continue; + } + if (cur->type == Gcalc_heap::nt_intersection) + { + cmp_res= cmp_intersections(cur, ii); + skip_next= cur->equal_intersection; + } + else if (cur->type == Gcalc_heap::nt_eq_node) + continue; + else + cmp_res= cmp_node_isc(cur, ii); + if (cmp_res == 0) + { + ii->equal_intersection= 1; + break; + } + else if (cmp_res > 0) + break; + } + + /* Intersection inserted before the equal point. */ + ii->next= pi_from->get_next(); + pi_from->next= ii; + + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_scan_iterator::add_eq_node(Gcalc_heap::Info *node, point *sp) +{ + Gcalc_heap::Info *en; + + GCALC_DBUG_ENTER("Gcalc_scan_iterator::add_intersection"); + en= new_eq_point(m_heap, node, sp); + if (!en) + GCALC_DBUG_RETURN(1); + + /* eq_node iserted after teh equal point. */ + en->next= node->get_next(); + node->next= en; + + GCALC_DBUG_RETURN(0); +} + + +void calc_t(Gcalc_coord2 t_a, Gcalc_coord2 t_b, + Gcalc_coord1 dxa, Gcalc_coord1 dxb, + const Gcalc_heap::Info *p1, const Gcalc_heap::Info *p2, + const Gcalc_heap::Info *p3, const Gcalc_heap::Info *p4) +{ + Gcalc_coord1 a2_a1x, a2_a1y; + Gcalc_coord2 x1y2, x2y1; + Gcalc_coord1 dya, dyb; + + gcalc_sub_coord1(a2_a1x, p3->ix, p1->ix); + gcalc_sub_coord1(a2_a1y, p3->iy, p1->iy); + + gcalc_sub_coord1(dxa, p2->ix, p1->ix); + gcalc_sub_coord1(dya, p2->iy, p1->iy); + gcalc_sub_coord1(dxb, p4->ix, p3->ix); + gcalc_sub_coord1(dyb, p4->iy, p3->iy); + + gcalc_mul_coord1(x1y2, dxa, dyb); + gcalc_mul_coord1(x2y1, dya, dxb); + gcalc_sub_coord(t_b, GCALC_COORD_BASE2, x1y2, x2y1); + + + gcalc_mul_coord1(x1y2, a2_a1x, dyb); + gcalc_mul_coord1(x2y1, a2_a1y, dxb); + gcalc_sub_coord(t_a, GCALC_COORD_BASE2, x1y2, x2y1); +} + + +double Gcalc_scan_iterator::get_y() const +{ + if (state.pi->type == Gcalc_heap::nt_intersection) + { + Gcalc_coord1 dxa, dya; + Gcalc_coord2 t_a, t_b; + Gcalc_coord3 a_tb, b_ta, y_exp; + calc_t(t_a, t_b, dxa, dya, + state.pi->p1, state.pi->p2, state.pi->p3, state.pi->p4); + + + gcalc_mul_coord(a_tb, GCALC_COORD_BASE3, + t_b, GCALC_COORD_BASE2, state.pi->p1->iy, GCALC_COORD_BASE); + gcalc_mul_coord(b_ta, GCALC_COORD_BASE3, + t_a, GCALC_COORD_BASE2, dya, GCALC_COORD_BASE); + + gcalc_add_coord(y_exp, GCALC_COORD_BASE3, a_tb, b_ta); + + return (get_pure_double(y_exp, GCALC_COORD_BASE3) / + get_pure_double(t_b, GCALC_COORD_BASE2)) / m_heap->coord_extent; + } + else + return state.pi->y; +} + + +double Gcalc_scan_iterator::get_event_x() const +{ + if (state.pi->type == Gcalc_heap::nt_intersection) + { + Gcalc_coord1 dxa, dya; + Gcalc_coord2 t_a, t_b; + Gcalc_coord3 a_tb, b_ta, x_exp; + calc_t(t_a, t_b, dxa, dya, + state.pi->p1, state.pi->p2, state.pi->p3, state.pi->p4); + + + gcalc_mul_coord(a_tb, GCALC_COORD_BASE3, + t_b, GCALC_COORD_BASE2, state.pi->p1->ix, GCALC_COORD_BASE); + gcalc_mul_coord(b_ta, GCALC_COORD_BASE3, + t_a, GCALC_COORD_BASE2, dxa, GCALC_COORD_BASE); + + gcalc_add_coord(x_exp, GCALC_COORD_BASE3, a_tb, b_ta); + + return (get_pure_double(x_exp, GCALC_COORD_BASE3) / + get_pure_double(t_b, GCALC_COORD_BASE2)) / m_heap->coord_extent; + } + else + return state.pi->x; +} + +double Gcalc_scan_iterator::get_h() const +{ + double cur_y= get_y(); + double next_y; + if (state.pi->type == Gcalc_heap::nt_intersection) + { + double x; + state.pi->calc_xy(&x, &next_y); + } + else + next_y= state.pi->y; + return next_y - cur_y; +} + + +double Gcalc_scan_iterator::get_sp_x(const point *sp) const +{ + double dy; + if (sp->event & (scev_end | scev_two_ends | scev_point)) + return sp->pi->x; + dy= sp->next_pi->y - sp->pi->y; + if (fabs(dy) < 1e-12) + return sp->pi->x; + return (sp->next_pi->x - sp->pi->x) * dy; +} + + +double Gcalc_scan_iterator::get_pure_double(const Gcalc_internal_coord *d, + int d_len) +{ + int n= 1; + long double res= (long double) FIRST_DIGIT(d[0]); + do + { + res*= (long double) GCALC_DIG_BASE; + res+= (long double) d[n]; + } while(++n < d_len); + + if (GCALC_SIGN(d[0])) + res*= -1.0; + return res; +} + + +#endif /* HAVE_SPATIAL */ diff --git a/sql/gcalc_slicescan.h b/sql/gcalc_slicescan.h new file mode 100644 index 00000000000..9fc4cea5199 --- /dev/null +++ b/sql/gcalc_slicescan.h @@ -0,0 +1,595 @@ +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. + Copyright (C) 2011 Monty Program Ab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifndef GCALC_SLICESCAN_INCLUDED +#define GCALC_SLICESCAN_INCLUDED + +#ifndef DBUG_OFF +// #define GCALC_CHECK_WITH_FLOAT +#else +#define GCALC_DBUG_OFF +#endif /*DBUG_OFF*/ + +#ifndef GCALC_DBUG_OFF +#define GCALC_DBUG_PRINT(b) DBUG_PRINT("Gcalc", b) +#define GCALC_DBUG_ENTER(a) DBUG_ENTER("Gcalc "a) +#define GCALC_DBUG_RETURN(r) DBUG_RETURN(r) +#define GCALC_DBUG_VOID_RETURN DBUG_VOID_RETURN +#define GCALC_DBUG_ASSERT(r) DBUG_ASSERT(r) +#else +#define GCALC_DBUG_PRINT(b) do {} while(0) +#define GCALC_DBUG_ENTER(a) do {} while(0) +#define GCALC_DBUG_RETURN(r) return (r) +#define GCALC_DBUG_VOID_RETURN do {} while(0) +#define GCALC_DBUG_ASSERT(r) do {} while(0) +#endif /*GCALC_DBUG_OFF*/ + +#define GCALC_TERMINATED(state_var) (state_var && (*state_var)) +#define GCALC_SET_TERMINATED(state_var, val) state_var= val +#define GCALC_DECL_TERMINATED_STATE(varname) \ + volatile int *varname; + +/* + Gcalc_dyn_list class designed to manage long lists of same-size objects + with the possible efficiency. + It allocates fixed-size blocks of memory (blk_size specified at the time + of creation). When new object is added to the list, it occupies part of + this block until it's full. Then the new block is allocated. + Freed objects are chained to the m_free list, and if it's not empty, the + newly added object is taken from this list instead the block. +*/ + +class Gcalc_dyn_list +{ +public: + class Item + { + public: + Item *next; + }; + + Gcalc_dyn_list(size_t blk_size, size_t sizeof_item); + ~Gcalc_dyn_list(); + Item *new_item() + { + Item *result; + if (m_free) + { + result= m_free; + m_free= m_free->next; + } + else + result= alloc_new_blk(); + + return result; + } + inline void free_item(Item *item) + { + item->next= m_free; + m_free= item; + } + inline void free_list(Item **list, Item **hook) + { + *hook= m_free; + m_free= *list; + } + + void free_list(Item *list) + { + Item **hook= &list; + while (*hook) + hook= &(*hook)->next; + free_list(&list, hook); + } + + void reset(); + void cleanup(); + +protected: + size_t m_blk_size; + size_t m_sizeof_item; + unsigned int m_points_per_blk; + void *m_first_blk; + void **m_blk_hook; + Item *m_free; + Item *m_keep; + + Item *alloc_new_blk(); + void format_blk(void* block); + inline Item *ptr_add(Item *ptr, int n_items) + { + return (Item *)(((char*)ptr) + n_items * m_sizeof_item); + } +}; + +/* Internal Gcalc coordinates to provide the precise calculations */ + +#define GCALC_DIG_BASE 1000000000 +typedef uint32 gcalc_digit_t; +typedef unsigned long long gcalc_coord2; +typedef gcalc_digit_t Gcalc_internal_coord; +#define GCALC_COORD_BASE 2 +#define GCALC_COORD_BASE2 4 +#define GCALC_COORD_BASE3 6 +#define GCALC_COORD_BASE4 8 +#define GCALC_COORD_BASE5 10 + +typedef gcalc_digit_t Gcalc_coord1[GCALC_COORD_BASE]; +typedef gcalc_digit_t Gcalc_coord2[GCALC_COORD_BASE*2]; +typedef gcalc_digit_t Gcalc_coord3[GCALC_COORD_BASE*3]; + + +void gcalc_mul_coord(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, int a_len, + const Gcalc_internal_coord *b, int b_len); + +void gcalc_add_coord(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b); + +void gcalc_sub_coord(Gcalc_internal_coord *result, int result_len, + const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b); + +int gcalc_cmp_coord(const Gcalc_internal_coord *a, + const Gcalc_internal_coord *b, int len); + +/* Internal coordinates declarations end. */ + + +typedef uint gcalc_shape_info; + +/* + Gcalc_heap represents the 'dynamic list' of Info objects, that + contain information about vertexes of all the shapes that take + part in some spatial calculation. Can become quite long. + After filled, the list is usually sorted and then walked through + in the slicescan algorithm. + The Gcalc_heap and the algorithm can only operate with two + kinds of shapes - polygon and polyline. So all the spatial + objects should be represented as sets of these two. +*/ + +class Gcalc_heap : public Gcalc_dyn_list +{ +public: + enum node_type + { + nt_shape_node, + nt_intersection, + nt_eq_node + }; + class Info : public Gcalc_dyn_list::Item + { + public: + node_type type; + union + { + struct + { + /* nt_shape_node */ + gcalc_shape_info shape; + Info *left; + Info *right; + double x,y; + Gcalc_coord1 ix, iy; + int top_node; + }; + struct + { + /* nt_intersection */ + /* Line p1-p2 supposed to intersect line p3-p4 */ + const Info *p1; + const Info *p2; + const Info *p3; + const Info *p4; + void *intersection_data; + int equal_intersection; + }; + struct + { + /* nt_eq_node */ + const Info *node; + void *eq_data; + }; + }; + + bool is_bottom() const + { GCALC_DBUG_ASSERT(type == nt_shape_node); return !left; } + bool is_top() const + { GCALC_DBUG_ASSERT(type == nt_shape_node); return top_node; } + bool is_single_node() const + { return is_bottom() && is_top(); } + + void calc_xy(double *x, double *y) const; + int equal_pi(const Info *pi) const; +#ifdef GCALC_CHECK_WITH_FLOAT + void calc_xy_ld(long double *x, long double *y) const; +#endif /*GCALC_CHECK_WITH_FLOAT*/ + + Info *get_next() { return (Info *)next; } + const Info *get_next() const { return (const Info *)next; } + }; + + Gcalc_heap(size_t blk_size=8192) : + Gcalc_dyn_list(blk_size, sizeof(Info)), + m_hook(&m_first), m_n_points(0) + {} + Info *new_point_info(double x, double y, gcalc_shape_info shape); + Info *new_intersection(const Info *p1, const Info *p2, + const Info *p3, const Info *p4); + void prepare_operation(); + inline bool ready() const { return m_hook == NULL; } + Info *get_first() { return (Info *)m_first; } + const Info *get_first() const { return (const Info *)m_first; } + Gcalc_dyn_list::Item **get_last_hook() { return m_hook; } + void reset(); +#ifdef GCALC_CHECK_WITH_FLOAT + long double get_double(const Gcalc_internal_coord *c) const; +#endif /*GCALC_CHECK_WITH_FLOAT*/ + double coord_extent; +private: + Gcalc_dyn_list::Item *m_first; + Gcalc_dyn_list::Item **m_hook; + int m_n_points; +}; + + +/* + the spatial object has to be represented as a set of + simple polygones and polylines to be sent to the slicescan. + + Gcalc_shape_transporter class and his descendants are used to + simplify storing the information about the shape into necessary structures. + This base class only fills the Gcalc_heap with the information about + shapes and vertices. + + Normally the Gcalc_shape_transporter family object is sent as a parameter + to the 'get_shapes' method of an 'spatial' object so it can pass + the spatial information about itself. The virtual methods are + treating this data in a way the caller needs. +*/ + +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; + 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() + { + DBUG_ASSERT(!m_shape_started); + m_shape_started= 1; + m_first= m_prev= NULL; + } + void int_complete_line() + { + DBUG_ASSERT(m_shape_started== 1); + int_complete(); + m_shape_started= 0; + } + void int_start_ring() + { + DBUG_ASSERT(m_shape_started== 2); + m_shape_started= 3; + m_first= m_prev= NULL; + } + void int_complete_ring() + { + 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) {} + + 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; } + virtual int empty_shape() { return 0; } + int start_simple_poly() + { + return start_poly() || start_ring(); + } + int complete_simple_poly() + { + return complete_ring() || complete_poly(); + } + virtual ~Gcalc_shape_transporter() {} +}; + + +enum Gcalc_scan_events +{ + scev_none= 0, + scev_point= 1, /* Just a new point in thread */ + scev_thread= 2, /* Start of the new thread */ + scev_two_threads= 4, /* A couple of new threads started */ + scev_intersection= 8, /* Intersection happened */ + scev_end= 16, /* Single thread finished */ + scev_two_ends= 32, /* A couple of threads finished */ + scev_single_point= 64 /* Got single point */ +}; + + +/* + Gcalc_scan_iterator incapsulates the slisescan algorithm. + It takes filled Gcalc_heap as an datasource. Then can be + iterated trought the vertexes and intersection points with + the step() method. After the 'step()' one usually observes + the current 'slice' to do the necessary calculations, like + looking for intersections, calculating the area, whatever. +*/ + +class Gcalc_scan_iterator : public Gcalc_dyn_list +{ +public: + class point : public Gcalc_dyn_list::Item + { + public: + Gcalc_coord1 dx; + Gcalc_coord1 dy; + Gcalc_heap::Info *pi; + Gcalc_heap::Info *next_pi; + Gcalc_heap::Info *ev_pi; + const Gcalc_coord1 *l_border; + const Gcalc_coord1 *r_border; + point *ev_next; + + Gcalc_scan_events event; + + inline const point *c_get_next() const + { return (const point *)next; } + inline bool is_bottom() const { return !next_pi; } + gcalc_shape_info get_shape() const { return pi->shape; } + inline point *get_next() { return (point *)next; } + inline const point *get_next() const { return (const point *)next; } + /* Compare the dx_dy parameters regarding the horiz_dir */ + /* returns -1 if less, 0 if equal, 1 if bigger */ + static int cmp_dx_dy(const Gcalc_coord1 dx_a, + const Gcalc_coord1 dy_a, + const Gcalc_coord1 dx_b, + const Gcalc_coord1 dy_b); + static int cmp_dx_dy(const Gcalc_heap::Info *p1, + const Gcalc_heap::Info *p2, + const Gcalc_heap::Info *p3, + const Gcalc_heap::Info *p4); + int cmp_dx_dy(const point *p) const; + point **next_ptr() { return (point **) &next; } +#ifndef GCALC_DBUG_OFF + unsigned int thread; +#endif /*GCALC_DBUG_OFF*/ +#ifdef GCALC_CHECK_WITH_FLOAT + void calc_x(long double *x, long double y, long double ix) const; +#endif /*GCALC_CHECK_WITH_FLOAT*/ + }; + + /* That class introduced mostly for the 'typecontrol' reason. */ + /* only difference from the point classis the get_next() function. */ + class event_point : public point + { + public: + inline const event_point *get_next() const + { return (const event_point*) ev_next; } + int simple_event() const + { + return !ev_next ? (event & (scev_point | scev_end)) : + (!ev_next->ev_next && event == scev_two_ends); + } + }; + + class intersection_info : public Gcalc_dyn_list::Item + { + public: + point *edge_a; + point *edge_b; + + Gcalc_coord2 t_a; + Gcalc_coord2 t_b; + int t_calculated; + Gcalc_coord3 x_exp; + int x_calculated; + Gcalc_coord3 y_exp; + int y_calculated; + void calc_t() + {if (!t_calculated) do_calc_t(); } + void calc_y_exp() + { if (!y_calculated) do_calc_y(); } + void calc_x_exp() + { if (!x_calculated) do_calc_x(); } + + void do_calc_t(); + void do_calc_x(); + void do_calc_y(); + }; + + + class slice_state + { + public: + point *slice; + point **event_position_hook; + point *event_end; + const Gcalc_heap::Info *pi; + }; + +public: + Gcalc_scan_iterator(size_t blk_size= 8192); + + GCALC_DECL_TERMINATED_STATE(killed) + + void init(Gcalc_heap *points); /* Iterator can be reused */ + void reset(); + int step(); + + Gcalc_heap::Info *more_points() { return m_cur_pi; } + bool more_trapezoids() + { return m_cur_pi && m_cur_pi->next; } + + const point *get_bottom_points() const + { return m_bottom_points; } + const point *get_event_position() const + { return *state.event_position_hook; } + const point *get_event_end() const + { return state.event_end; } + const event_point *get_events() const + { return (const event_point *) + (*state.event_position_hook == state.event_end ? + m_bottom_points : *state.event_position_hook); } + const point *get_b_slice() const { return state.slice; } + double get_h() const; + double get_y() const; + double get_event_x() const; + double get_sp_x(const point *sp) const; + int intersection_step() const + { return state.pi->type == Gcalc_heap::nt_intersection; } + const Gcalc_heap::Info *get_cur_pi() const + { + return state.pi; + } + +private: + Gcalc_heap *m_heap; + Gcalc_heap::Info *m_cur_pi; + slice_state state; + +#ifndef GCALC_DBUG_OFF + unsigned int m_cur_thread; +#endif /*GCALC_DBUG_OFF*/ + + point *m_bottom_points; + point **m_bottom_hook; + + int node_scan(); + void eq_scan(); + void intersection_scan(); + void remove_bottom_node(); + int insert_top_node(); + int add_intersection(point *sp_a, point *sp_b, + Gcalc_heap::Info *pi_from); + int add_eq_node(Gcalc_heap::Info *node, point *sp); + int add_events_for_node(point *sp_node); + + point *new_slice_point() + { + point *new_point= (point *)new_item(); + return new_point; + } + intersection_info *new_intersection_info(point *a, point *b) + { + intersection_info *ii= (intersection_info *)new_item(); + ii->edge_a= a; + ii->edge_b= b; + ii->t_calculated= ii->x_calculated= ii->y_calculated= 0; + return ii; + } + int arrange_event(int do_sorting, int n_intersections); + static double get_pure_double(const Gcalc_internal_coord *d, int d_len); +}; + + +/* + Gcalc_trapezoid_iterator simplifies the calculations on + the current slice of the Gcalc_scan_iterator. + One can walk through the trapezoids formed between + previous and current slices. +*/ + +#ifdef TMP_BLOCK +class Gcalc_trapezoid_iterator +{ +protected: + const Gcalc_scan_iterator::point *sp0; + const Gcalc_scan_iterator::point *sp1; +public: + Gcalc_trapezoid_iterator(const Gcalc_scan_iterator *scan_i) : + sp0(scan_i->get_b_slice()), + sp1(scan_i->get_t_slice()) + {} + + inline bool more() const { return sp1 && sp1->next; } + + const Gcalc_scan_iterator::point *lt() const { return sp1; } + const Gcalc_scan_iterator::point *lb() const { return sp0; } + const Gcalc_scan_iterator::point *rb() const + { + const Gcalc_scan_iterator::point *result= sp0; + while ((result= result->c_get_next())->is_bottom()) + {} + return result; + } + const Gcalc_scan_iterator::point *rt() const + { return sp1->c_get_next(); } + + void operator++() + { + sp0= rb(); + sp1= rt(); + } +}; +#endif /*TMP_BLOCK*/ + + +/* + Gcalc_point_iterator simplifies the calculations on + the current slice of the Gcalc_scan_iterator. + One can walk through the points on the current slice. +*/ + +class Gcalc_point_iterator +{ +protected: + const Gcalc_scan_iterator::point *sp; +public: + Gcalc_point_iterator(const Gcalc_scan_iterator *scan_i): + sp(scan_i->get_b_slice()) + {} + + inline bool more() const { return sp != NULL; } + inline void operator++() { sp= sp->c_get_next(); } + inline const Gcalc_scan_iterator::point *point() const { return sp; } + inline const Gcalc_heap::Info *get_pi() const { return sp->pi; } + inline gcalc_shape_info get_shape() const { return sp->get_shape(); } + inline void restart(const Gcalc_scan_iterator *scan_i) + { sp= scan_i->get_b_slice(); } +}; + +#endif /*GCALC_SLICESCAN_INCLUDED*/ + diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc new file mode 100644 index 00000000000..187acbacc4c --- /dev/null +++ b/sql/gcalc_tools.cc @@ -0,0 +1,1429 @@ +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. + Copyright (C) 2011 Monty Program Ab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include <my_global.h> + +#ifdef HAVE_SPATIAL + +#include "gcalc_tools.h" +#include "spatial.h" + +#define float_to_coord(d) ((double) d) + + +/* + Adds new shape to the relation. + After that it can be used as an argument of an operation. +*/ + +gcalc_shape_info Gcalc_function::add_new_shape(uint32 shape_id, + shape_type shape_kind) +{ + shapes_buffer.q_append((uint32) shape_kind); + return n_shapes++; +} + + +/* + Adds new operation to the constructed relation. + To construct the complex relation one has to specify operations + in prefix style. +*/ + +void Gcalc_function::add_operation(uint operation, uint32 n_operands) +{ + uint32 op_code= (uint32 ) operation + n_operands; + function_buffer.q_append(op_code); +} + + +/* + Sometimes the number of arguments is unknown at the moment the operation + is added. That allows to specify it later. +*/ + +void Gcalc_function::add_operands_to_op(uint32 operation_pos, uint32 n_operands) +{ + uint32 op_code= uint4korr(function_buffer.ptr() + operation_pos) + n_operands; + function_buffer.write_at_position(operation_pos, op_code); +} + + +/* + Just like the add_operation() but the result will be the inverted + value of an operation. +*/ + +void Gcalc_function::add_not_operation(op_type operation, uint32 n_operands) +{ + uint32 op_code= ((uint32) op_not | (uint32 ) operation) + n_operands; + function_buffer.q_append(op_code); +} + + +int Gcalc_function::single_shape_op(shape_type shape_kind, gcalc_shape_info *si) +{ + if (reserve_shape_buffer(1) || reserve_op_buffer(1)) + return 1; + *si= add_new_shape(0, shape_kind); + add_operation(op_shape, *si); + return 0; +} + + +int Gcalc_function::repeat_expression(uint32 exp_pos) +{ + if (reserve_op_buffer(1)) + return 1; + add_operation(op_repeat, exp_pos); + return 0; +} + + +/* + Specify how many arguments we're going to have. +*/ + +int Gcalc_function::reserve_shape_buffer(uint n_shapes) +{ + return shapes_buffer.reserve(n_shapes * 4, 512); +} + + +/* + Specify how many operations we're going to have. +*/ + +int Gcalc_function::reserve_op_buffer(uint n_ops) +{ + return function_buffer.reserve(n_ops * 4, 512); +} + + +int Gcalc_function::alloc_states() +{ + if (function_buffer.reserve((n_shapes+1) * 2 * sizeof(int))) + return 1; + i_states= (int *) (function_buffer.ptr() + ALIGN_SIZE(function_buffer.length())); + b_states= i_states + (n_shapes + 1); + return 0; +} + + +int Gcalc_function::count_internal(const char *cur_func, uint set_type, + const char **end) +{ + uint c_op= uint4korr(cur_func); + op_type next_func= (op_type) (c_op & op_any); + int mask= (c_op & op_not) ? 1:0; + uint n_ops= c_op & ~(op_any | op_not | v_mask); + uint n_shape= c_op & ~(op_any | op_not | v_mask); /* same as n_ops */ + value v_state= (value) (c_op & v_mask); + int result= 0; + const char *sav_cur_func= cur_func; + + // GCALC_DBUG_ENTER("Gcalc_function::count_internal"); + + cur_func+= 4; + if (next_func == op_shape) + { + if (set_type == 0) + result= i_states[n_shape] | b_states[n_shape]; + else if (set_type == op_border) + result= b_states[n_shape]; + else if (set_type == op_internals) + result= i_states[n_shape] && !b_states[n_shape]; + goto exit; + } + + if (next_func == op_false) + { + result= 0; + goto exit; + } + + if (next_func == op_border || next_func == op_internals) + { + result= count_internal(cur_func, next_func, &cur_func); + goto exit; + } + + if (next_func == op_repeat) + { + result= count_internal(function_buffer.ptr() + n_ops, set_type, 0); + goto exit; + } + + if (n_ops == 0) + return mask; + //GCALC_DBUG_RETURN(mask); + + result= count_internal(cur_func, set_type, &cur_func); + + while (--n_ops) + { + int next_res= count_internal(cur_func, set_type, &cur_func); + switch (next_func) + { + case op_union: + result= result | next_res; + break; + case op_intersection: + result= result & next_res; + break; + case op_symdifference: + result= result ^ next_res; + break; + case op_difference: + result= result & !next_res; + break; + default: + GCALC_DBUG_ASSERT(FALSE); + }; + } + +exit: + result^= mask; + if (v_state != v_empty) + { + switch (v_state) + { + case v_find_t: + if (result) + { + c_op= (c_op & ~v_mask) | v_t_found; + int4store(sav_cur_func, c_op); + }; + break; + case v_find_f: + if (!result) + { + c_op= (c_op & ~v_mask) | v_f_found; + int4store(sav_cur_func, c_op); + }; + break; + case v_t_found: + result= 1; + break; + case v_f_found: + result= 0; + break; + default: + GCALC_DBUG_ASSERT(0); + }; + } + + if (end) + *end= cur_func; + return result; + //GCALC_DBUG_RETURN(result); +} + + +void Gcalc_function::clear_i_states() +{ + for (uint i= 0; i < n_shapes; i++) + i_states[i]= 0; +} + + +void Gcalc_function::clear_b_states() +{ + for (uint i= 0; i < n_shapes; i++) + b_states[i]= 0; +} + + +/* + Clear the state of the object. +*/ + +void Gcalc_function::reset() +{ + n_shapes= 0; + shapes_buffer.length(0); + function_buffer.length(0); +} + + +int Gcalc_function::check_function(Gcalc_scan_iterator &scan_it) +{ + const Gcalc_scan_iterator::point *eq_start, *cur_eq; + const Gcalc_scan_iterator::event_point *events; + GCALC_DBUG_ENTER("Gcalc_function::check_function"); + + while (scan_it.more_points()) + { + if (scan_it.step()) + GCALC_DBUG_RETURN(-1); + events= scan_it.get_events(); + + /* these kinds of events don't change the function */ + Gcalc_point_iterator pit(&scan_it); + clear_b_states(); + clear_i_states(); + /* Walk to the event, marking polygons we met */ + for (; pit.point() != scan_it.get_event_position(); ++pit) + { + gcalc_shape_info si= pit.point()->get_shape(); + if ((get_shape_kind(si) == Gcalc_function::shape_polygon)) + invert_i_state(si); + } + if (events->simple_event()) + { + if (events->event == scev_end) + set_b_state(events->get_shape()); + + if (count()) + GCALC_DBUG_RETURN(1); + clear_b_states(); + continue; + } + + /* Check the status of the event point */ + for (; events; events= events->get_next()) + { + gcalc_shape_info si= events->get_shape(); + if (events->event == scev_thread || + events->event == scev_end || + events->event == scev_single_point || + (get_shape_kind(si) == Gcalc_function::shape_polygon)) + set_b_state(si); + else if (get_shape_kind(si) == Gcalc_function::shape_line) + set_i_state(si); + } + + if (count()) + GCALC_DBUG_RETURN(1); + + /* Set back states changed in the loop above. */ + for (events= scan_it.get_events(); events; events= events->get_next()) + { + gcalc_shape_info si= events->get_shape(); + if (events->event == scev_thread || + events->event == scev_end || + events->event == scev_single_point || + (get_shape_kind(si) == Gcalc_function::shape_polygon)) + clear_b_state(si); + else if (get_shape_kind(si) == Gcalc_function::shape_line) + clear_i_state(si); + } + + if (scan_it.get_event_position() == scan_it.get_event_end()) + continue; + + /* Check the status after the event */ + eq_start= pit.point(); + do + { + ++pit; + if (pit.point() != scan_it.get_event_end() && + eq_start->cmp_dx_dy(pit.point()) == 0) + continue; + for (cur_eq= eq_start; cur_eq != pit.point(); + cur_eq= cur_eq->get_next()) + { + gcalc_shape_info si= cur_eq->get_shape(); + if (get_shape_kind(si) == Gcalc_function::shape_polygon) + set_b_state(si); + else + invert_i_state(si); + } + if (count()) + GCALC_DBUG_RETURN(1); + + for (cur_eq= eq_start; cur_eq != pit.point(); cur_eq= cur_eq->get_next()) + { + gcalc_shape_info si= cur_eq->get_shape(); + if ((get_shape_kind(si) == Gcalc_function::shape_polygon)) + { + clear_b_state(si); + invert_i_state(si); + } + else + invert_i_state(cur_eq->get_shape()); + } + if (count()) + GCALC_DBUG_RETURN(1); + eq_start= pit.point(); + } while (pit.point() != scan_it.get_event_end()); + } + GCALC_DBUG_RETURN(0); +} + + +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; +} + + +int Gcalc_operation_transporter::empty_shape() +{ + if (m_fn->reserve_op_buffer(1)) + return 1; + m_fn->add_operation(Gcalc_function::op_false, 0); + return 0; +} + + +int Gcalc_result_receiver::start_shape(Gcalc_function::shape_type shape) +{ + GCALC_DBUG_ENTER("Gcalc_result_receiver::start_shape"); + if (buffer.reserve(4*2, 512)) + GCALC_DBUG_RETURN(1); + cur_shape= shape; + shape_pos= buffer.length(); + buffer.length(shape_pos + ((shape == Gcalc_function::shape_point) ? 4:8)); + n_points= 0; + shape_area= 0.0; + + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_result_receiver::add_point(double x, double y) +{ + GCALC_DBUG_ENTER("Gcalc_result_receiver::add_point"); + if (n_points && x == prev_x && y == prev_y) + GCALC_DBUG_RETURN(0); + + if (!n_points++) + { + prev_x= first_x= x; + prev_y= first_y= y; + GCALC_DBUG_RETURN(0); + } + + shape_area+= prev_x*y - prev_y*x; + + if (buffer.reserve(8*2, 512)) + GCALC_DBUG_RETURN(1); + buffer.q_append(prev_x); + buffer.q_append(prev_y); + prev_x= x; + prev_y= y; + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_result_receiver::complete_shape() +{ + GCALC_DBUG_ENTER("Gcalc_result_receiver::complete_shape"); + if (n_points == 0) + { + buffer.length(shape_pos); + GCALC_DBUG_RETURN(0); + } + if (n_points == 1) + { + if (cur_shape != Gcalc_function::shape_point) + { + if (cur_shape == Gcalc_function::shape_hole) + { + buffer.length(shape_pos); + GCALC_DBUG_RETURN(0); + } + cur_shape= Gcalc_function::shape_point; + buffer.length(buffer.length()-4); + } + } + else + { + GCALC_DBUG_ASSERT(cur_shape != Gcalc_function::shape_point); + if (cur_shape == Gcalc_function::shape_hole) + { + shape_area+= prev_x*first_y - prev_y*first_x; + if (fabs(shape_area) < 1e-8) + { + buffer.length(shape_pos); + GCALC_DBUG_RETURN(0); + } + } + + if ((cur_shape == Gcalc_function::shape_polygon || + cur_shape == Gcalc_function::shape_hole) && + prev_x == first_x && prev_y == first_y) + { + n_points--; + buffer.write_at_position(shape_pos+4, n_points); + goto do_complete; + } + buffer.write_at_position(shape_pos+4, n_points); + } + + if (buffer.reserve(8*2, 512)) + GCALC_DBUG_RETURN(1); + buffer.q_append(prev_x); + buffer.q_append(prev_y); + +do_complete: + buffer.write_at_position(shape_pos, (uint32) cur_shape); + + if (!n_shapes++) + { + GCALC_DBUG_ASSERT(cur_shape != Gcalc_function::shape_hole); + common_shapetype= cur_shape; + } + else if (cur_shape == Gcalc_function::shape_hole) + { + ++n_holes; + } + else if (!collection_result && (cur_shape != common_shapetype)) + { + collection_result= true; + } + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_result_receiver::single_point(double x, double y) +{ + return start_shape(Gcalc_function::shape_point) || + add_point(x, y) || + complete_shape(); +} + + +int Gcalc_result_receiver::done() +{ + return 0; +} + + +void Gcalc_result_receiver::reset() +{ + buffer.length(0); + collection_result= FALSE; + n_shapes= n_holes= 0; +} + + +int Gcalc_result_receiver::get_result_typeid() +{ + if (!n_shapes || collection_result) + return Geometry::wkb_geometrycollection; + + switch (common_shapetype) + { + case Gcalc_function::shape_polygon: + return (n_shapes - n_holes == 1) ? + Geometry::wkb_polygon : Geometry::wkb_multipolygon; + case Gcalc_function::shape_point: + return (n_shapes == 1) ? Geometry::wkb_point : Geometry::wkb_multipoint; + case Gcalc_function::shape_line: + return (n_shapes == 1) ? Geometry::wkb_linestring : + Geometry::wkb_multilinestring; + default: + GCALC_DBUG_ASSERT(0); + } + return 0; +} + + +int Gcalc_result_receiver::move_hole(uint32 dest_position, uint32 source_position, + uint32 *position_shift) +{ + char *ptr; + int source_len; + GCALC_DBUG_ENTER("Gcalc_result_receiver::move_hole"); + GCALC_DBUG_PRINT(("ps %d %d", dest_position, source_position)); + + *position_shift= source_len= buffer.length() - source_position; + + if (dest_position == source_position) + GCALC_DBUG_RETURN(0); + + if (buffer.reserve(source_len, MY_ALIGN(source_len, 512))) + GCALC_DBUG_RETURN(1); + + ptr= (char *) buffer.ptr(); + memmove(ptr + dest_position + source_len, ptr + dest_position, + buffer.length() - dest_position); + memcpy(ptr + dest_position, ptr + buffer.length(), source_len); + GCALC_DBUG_RETURN(0); +} + + +Gcalc_operation_reducer::Gcalc_operation_reducer(size_t blk_size) : + Gcalc_dyn_list(blk_size, sizeof(res_point)), +#ifndef GCALC_DBUG_OFF + n_res_points(0), +#endif /*GCALC_DBUG_OFF*/ + m_res_hook((Gcalc_dyn_list::Item **)&m_result), + m_first_active_thread(NULL) +{} + + +void Gcalc_operation_reducer::init(Gcalc_function *fn, modes mode) +{ + m_fn= fn; + m_mode= mode; + m_first_active_thread= NULL; + m_lines= NULL; + m_lines_hook= (Gcalc_dyn_list::Item **) &m_lines; + m_poly_borders= NULL; + m_poly_borders_hook= (Gcalc_dyn_list::Item **) &m_poly_borders; + GCALC_SET_TERMINATED(killed, 0); +} + + +Gcalc_operation_reducer:: +Gcalc_operation_reducer(Gcalc_function *fn, modes mode, size_t blk_size) : + Gcalc_dyn_list(blk_size, sizeof(res_point)), + m_res_hook((Gcalc_dyn_list::Item **)&m_result) +{ + init(fn, mode); +} + + +#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(); + pi= si->get_cur_pi(); +} + + +Gcalc_operation_reducer::res_point * + Gcalc_operation_reducer::add_res_point(Gcalc_function::shape_type type) +{ + GCALC_DBUG_ENTER("Gcalc_operation_reducer::add_res_point"); + res_point *result= (res_point *)new_item(); + *m_res_hook= result; + result->prev_hook= m_res_hook; + m_res_hook= &result->next; + result->type= type; +#ifndef GCALC_DBUG_OFF + result->point_n= n_res_points++; +#endif /*GCALC_DBUG_OFF*/ + GCALC_DBUG_RETURN(result); +} + +int Gcalc_operation_reducer::add_line(int incoming, active_thread *t, + const Gcalc_scan_iterator::point *p) +{ + line *l= new_line(); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::add_line"); + if (!l) + GCALC_DBUG_RETURN(1); + l->incoming= incoming; + l->t= t; + l->p= p; + *m_lines_hook= l; + m_lines_hook= &l->next; + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::add_poly_border(int incoming, + active_thread *t, int prev_state, const Gcalc_scan_iterator::point *p) +{ + poly_border *b= new_poly_border(); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::add_poly_border"); + if (!b) + GCALC_DBUG_RETURN(1); + b->incoming= incoming; + b->t= t; + b->prev_state= prev_state; + b->p= p; + *m_poly_borders_hook= b; + m_poly_borders_hook= &b->next; + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::continue_range(active_thread *t, + const Gcalc_heap::Info *p, + const Gcalc_heap::Info *p_next) +{ + res_point *rp= add_res_point(t->rp->type); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::continue_range"); + if (!rp) + GCALC_DBUG_RETURN(1); + rp->glue= NULL; + rp->down= t->rp; + t->rp->up= rp; + rp->intersection_point= false; + rp->pi= p; + t->rp= rp; + t->p1= p; + t->p2= p_next; + GCALC_DBUG_RETURN(0); +} + + +inline int Gcalc_operation_reducer::continue_i_range(active_thread *t, + const Gcalc_heap::Info *ii) +{ + res_point *rp= add_res_point(t->rp->type); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::continue_i_range"); + if (!rp) + GCALC_DBUG_RETURN(1); + rp->glue= NULL; + rp->down= t->rp; + t->rp->up= rp; + rp->intersection_point= true; + rp->pi= ii; + t->rp= rp; + GCALC_DBUG_RETURN(0); +} + +int Gcalc_operation_reducer::end_couple(active_thread *t0, active_thread *t1, + const Gcalc_heap::Info *p) +{ + res_point *rp0, *rp1; + GCALC_DBUG_ENTER("Gcalc_operation_reducer::end_couple"); + GCALC_DBUG_ASSERT(t0->rp->type == t1->rp->type); + if (!(rp0= add_res_point(t0->rp->type)) || + !(rp1= add_res_point(t0->rp->type))) + GCALC_DBUG_RETURN(1); + rp0->down= t0->rp; + rp1->down= t1->rp; + rp1->glue= rp0; + rp0->glue= rp1; + rp0->up= rp1->up= NULL; + t0->rp->up= rp0; + t1->rp->up= rp1; + rp0->intersection_point= rp1->intersection_point= false; + rp0->pi= rp1->pi= p; + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::count_slice(Gcalc_scan_iterator *si) +{ + Gcalc_point_iterator pi(si); + int prev_state= 0; + int sav_prev_state; + active_thread *prev_range= NULL; + const Gcalc_scan_iterator::event_point *events; + const Gcalc_scan_iterator::point *eq_start; + active_thread **cur_t_hook= &m_first_active_thread; + active_thread **starting_t_hook; + active_thread *bottom_threads= NULL; + active_thread *eq_thread, *point_thread;; + GCALC_DBUG_ENTER("Gcalc_operation_reducer::count_slice"); + + m_fn->clear_i_states(); + /* Walk to the event, remembering what is needed. */ + for (; pi.point() != si->get_event_position(); + ++pi, cur_t_hook= (active_thread **) &(*cur_t_hook)->next) + { + active_thread *cur_t= *cur_t_hook; + if (cur_t->enabled() && + cur_t->rp->type == Gcalc_function::shape_polygon) + { + prev_state^= 1; + prev_range= prev_state ? cur_t : 0; + } + if (m_fn->get_shape_kind(pi.get_shape()) == Gcalc_function::shape_polygon) + m_fn->invert_i_state(pi.get_shape()); + } + + events= si->get_events(); + if (events->simple_event()) + { + active_thread *cur_t= *cur_t_hook; + switch (events->event) + { + case scev_point: + { + if (cur_t->enabled() && + continue_range(cur_t, events->pi, events->next_pi)) + GCALC_DBUG_RETURN(1); + break; + } + case scev_end: + { + if (cur_t->enabled() && end_line(cur_t, si)) + GCALC_DBUG_RETURN(1); + *cur_t_hook= cur_t->get_next(); + free_item(cur_t); + break; + } + case scev_two_ends: + { + if (cur_t->enabled() && cur_t->get_next()->enabled()) + { + /* When two threads are ended here */ + if (end_couple(cur_t, cur_t->get_next(), events->pi)) + GCALC_DBUG_RETURN(1); + } + else if (cur_t->enabled() || cur_t->get_next()->enabled()) + { + /* Rare case when edges of a polygon coincide */ + if (end_line(cur_t->enabled() ? cur_t : cur_t->get_next(), si)) + GCALC_DBUG_RETURN(1); + } + *cur_t_hook= cur_t->get_next()->get_next(); + free_item(cur_t->next); + free_item(cur_t); + break; + } + default: + GCALC_DBUG_ASSERT(0); + } + GCALC_DBUG_RETURN(0); + } + + starting_t_hook= cur_t_hook; + sav_prev_state= prev_state; + + /* Walk through the event, collecting all the 'incoming' threads */ + for (; events; events= events->get_next()) + { + active_thread *cur_t= *cur_t_hook; + + if (events->event == scev_single_point) + continue; + + if (events->event == scev_thread || + events->event == scev_two_threads) + { + active_thread *new_t= new_active_thread(); + if (!new_t) + GCALC_DBUG_RETURN(1); + new_t->rp= NULL; + /* Insert into the main thread list before the current */ + new_t->next= cur_t; + *cur_t_hook= new_t; + cur_t_hook= (active_thread **) &new_t->next; + } + else + { + if (events->is_bottom()) + { + /* Move thread from the main list to the bottom_threads. */ + *cur_t_hook= cur_t->get_next(); + cur_t->next= bottom_threads; + bottom_threads= cur_t; + } + if (cur_t->enabled()) + { + if (cur_t->rp->type == Gcalc_function::shape_line) + { + GCALC_DBUG_ASSERT(!prev_state); + add_line(1, cur_t, events); + } + else + { + add_poly_border(1, cur_t, prev_state, events); + prev_state^= 1; + } + if (!events->is_bottom()) + { + active_thread *new_t= new_active_thread(); + if (!new_t) + GCALC_DBUG_RETURN(1); + new_t->rp= NULL; + /* Replace the current thread with the new. */ + new_t->next= cur_t->next; + *cur_t_hook= new_t; + cur_t_hook= (active_thread **) &new_t->next; + /* And move old to the bottom list */ + cur_t->next= bottom_threads; + bottom_threads= cur_t; + } + } + else if (!events->is_bottom()) + cur_t_hook= (active_thread **) &cur_t->next; + } + } + prev_state= sav_prev_state; + cur_t_hook= starting_t_hook; + + eq_start= pi.point(); + eq_thread= point_thread= *starting_t_hook; + m_fn->clear_b_states(); + while (eq_start != si->get_event_end()) + { + const Gcalc_scan_iterator::point *cur_eq; + int in_state, after_state; + + ++pi; + point_thread= point_thread->get_next(); + + if (pi.point() != si->get_event_end() && + eq_start->cmp_dx_dy(pi.point()) == 0) + continue; + + for (cur_eq= eq_start; cur_eq != pi.point(); cur_eq= cur_eq->get_next()) + m_fn->set_b_state(cur_eq->get_shape()); + in_state= m_fn->count(); + + m_fn->clear_b_states(); + for (cur_eq= eq_start; cur_eq != pi.point(); cur_eq= cur_eq->get_next()) + { + gcalc_shape_info si= cur_eq->get_shape(); + if ((m_fn->get_shape_kind(si) == Gcalc_function::shape_polygon)) + m_fn->invert_i_state(si); + } + after_state= m_fn->count(); + if (prev_state != after_state) + { + if (add_poly_border(0, eq_thread, prev_state, eq_start)) + GCALC_DBUG_RETURN(1); + } + else if (!prev_state /* &&!after_state */ && in_state) + { + if (add_line(0, eq_thread, eq_start)) + GCALC_DBUG_RETURN(1); + } + + prev_state= after_state; + eq_start= pi.point(); + eq_thread= point_thread; + } + + if (!sav_prev_state && !m_poly_borders && !m_lines) + { + /* Check if we need to add the event point itself */ + m_fn->clear_i_states(); + /* b_states supposed to be clean already */ + for (pi.restart(si); pi.point() != si->get_event_position(); ++pi) + { + if (m_fn->get_shape_kind(pi.get_shape()) == Gcalc_function::shape_polygon) + m_fn->invert_i_state(pi.get_shape()); + } + for (events= si->get_events(); events; events= events->get_next()) + m_fn->set_b_state(events->get_shape()); + + GCALC_DBUG_RETURN(m_fn->count() ? add_single_point(si) : 0); + } + + if (m_poly_borders) + { + *m_poly_borders_hook= NULL; + while (m_poly_borders) + { + poly_border *pb1, *pb2; + pb1= m_poly_borders; + GCALC_DBUG_ASSERT(m_poly_borders->next); + + pb2= get_pair_border(pb1); + /* Remove pb1 from the list. The pb2 already removed in get_pair_border. */ + m_poly_borders= pb1->get_next(); + if (connect_threads(pb1->incoming, pb2->incoming, + pb1->t, pb2->t, pb1->p, pb2->p, + prev_range, si, Gcalc_function::shape_polygon)) + GCALC_DBUG_RETURN(1); + + free_item(pb1); + free_item(pb2); + } + m_poly_borders_hook= (Gcalc_dyn_list::Item **) &m_poly_borders; + m_poly_borders= NULL; + } + + if (m_lines) + { + *m_lines_hook= NULL; + if (m_lines->get_next() && + !m_lines->get_next()->get_next()) + { + if (connect_threads(m_lines->incoming, m_lines->get_next()->incoming, + m_lines->t, m_lines->get_next()->t, + m_lines->p, m_lines->get_next()->p, + NULL, si, Gcalc_function::shape_line)) + GCALC_DBUG_RETURN(1); + } + else + { + for (line *cur_line= m_lines; cur_line; cur_line= cur_line->get_next()) + { + if (cur_line->incoming) + { + if (end_line(cur_line->t, si)) + GCALC_DBUG_RETURN(1); + } + else + start_line(cur_line->t, cur_line->p, si); + } + } + free_list(m_lines); + m_lines= NULL; + m_lines_hook= (Gcalc_dyn_list::Item **) &m_lines; + } + + if (bottom_threads) + free_list(bottom_threads); + + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::add_single_point(const Gcalc_scan_iterator *si) +{ + res_point *rp= add_res_point(Gcalc_function::shape_point); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::add_single_point"); + if (!rp) + GCALC_DBUG_RETURN(1); + rp->glue= rp->up= rp->down= NULL; + rp->set(si); + GCALC_DBUG_RETURN(0); +} + + +Gcalc_operation_reducer::poly_border + *Gcalc_operation_reducer::get_pair_border(poly_border *b1) +{ + poly_border *prev_b= b1; + poly_border *result= b1->get_next(); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::get_pair_border"); + if (b1->prev_state) + { + if (b1->incoming) + { + /* Find the first outgoing, otherwise the last one. */ + while (result->incoming && result->get_next()) + { + prev_b= result; + result= result->get_next(); + } + } + else + { + /* Get the last one */ + while (result->get_next()) + { + prev_b= result; + result= result->get_next(); + } + } + } + else /* !b1->prev_state */ + { + if (b1->incoming) + { + /* Get the next incoming, otherwise the last one. */ + while (!result->incoming && result->get_next()) + { + prev_b= result; + result= result->get_next(); + } + } + else + { + /* Just pick the next one */ + } + } + /* Delete the result from the list. */ + prev_b->next= result->next; + GCALC_DBUG_RETURN(result); +} + + +int Gcalc_operation_reducer::connect_threads( + int incoming_a, int incoming_b, + active_thread *ta, active_thread *tb, + const Gcalc_scan_iterator::point *pa, const Gcalc_scan_iterator::point *pb, + active_thread *prev_range, + const Gcalc_scan_iterator *si, Gcalc_function::shape_type s_t) +{ + GCALC_DBUG_ENTER("Gcalc_operation_reducer::connect_threads"); + GCALC_DBUG_PRINT(("incoming %d %d", incoming_a, incoming_b)); + if (incoming_a && incoming_b) + { + res_point *rpa, *rpb; + GCALC_DBUG_ASSERT(ta->rp->type == tb->rp->type); + if (!(rpa= add_res_point(ta->rp->type)) || + !(rpb= add_res_point(ta->rp->type))) + GCALC_DBUG_RETURN(1); + rpa->down= ta->rp; + rpb->down= tb->rp; + rpb->glue= rpa; + rpa->glue= rpb; + rpa->up= rpb->up= NULL; + ta->rp->up= rpa; + tb->rp->up= rpb; + rpa->set(si); + rpb->set(si); + ta->rp= tb->rp= NULL; + GCALC_DBUG_RETURN(0); + } + if (!incoming_a) + { + GCALC_DBUG_ASSERT(!incoming_b); + + res_point *rp0, *rp1; + if (!(rp0= add_res_point(s_t)) || !(rp1= add_res_point(s_t))) + GCALC_DBUG_RETURN(1); + rp0->glue= rp1; + rp1->glue= rp0; + rp0->set(si); + rp1->set(si); + rp0->down= rp1->down= NULL; + ta->rp= rp0; + tb->rp= rp1; + ta->p1= pa->pi; + ta->p2= pa->next_pi; + + tb->p1= pb->pi; + tb->p2= pb->next_pi; + + if (prev_range) + { + rp0->outer_poly= prev_range->thread_start; + tb->thread_start= prev_range->thread_start; + /* Chack if needed */ + ta->thread_start= prev_range->thread_start; + } + else + { + rp0->outer_poly= 0; + ta->thread_start= rp0; + /* Chack if needed */ + tb->thread_start= rp0; + } + GCALC_DBUG_RETURN(0); + } + /* else, if only ta is incoming */ + + GCALC_DBUG_ASSERT(tb != ta); + tb->rp= ta->rp; + tb->thread_start= ta->thread_start; + if (Gcalc_scan_iterator::point:: + cmp_dx_dy(ta->p1, ta->p2, pb->pi, pb->next_pi) != 0) + { + if (si->intersection_step() ? + continue_i_range(tb, si->get_cur_pi()) : + continue_range(tb, si->get_cur_pi(), pb->next_pi)) + GCALC_DBUG_RETURN(1); + } + tb->p1= pb->pi; + tb->p2= pb->next_pi; + + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::start_line(active_thread *t, + const Gcalc_scan_iterator::point *p, + const Gcalc_scan_iterator *si) +{ + res_point *rp= add_res_point(Gcalc_function::shape_line); + GCALC_DBUG_ENTER("Gcalc_operation_reducer::start_line"); + if (!rp) + GCALC_DBUG_RETURN(1); + rp->glue= rp->down= NULL; + rp->set(si); + t->rp= rp; + t->p1= p->pi; + t->p2= p->next_pi; + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::end_line(active_thread *t, + const Gcalc_scan_iterator *si) +{ + GCALC_DBUG_ENTER("Gcalc_operation_reducer::end_line"); + GCALC_DBUG_ASSERT(t->rp->type == Gcalc_function::shape_line); + res_point *rp= add_res_point(Gcalc_function::shape_line); + if (!rp) + GCALC_DBUG_RETURN(1); + rp->glue= rp->up= NULL; + rp->down= t->rp; + rp->set(si); + t->rp->up= rp; + t->rp= NULL; + + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::count_all(Gcalc_heap *hp) +{ + Gcalc_scan_iterator si; + GCALC_DBUG_ENTER("Gcalc_operation_reducer::count_all"); + si.init(hp); + GCALC_SET_TERMINATED(si.killed, killed); + while (si.more_points()) + { + if (si.step()) + GCALC_DBUG_RETURN(1); + if (count_slice(&si)) + GCALC_DBUG_RETURN(1); + } + GCALC_DBUG_RETURN(0); +} + +inline void Gcalc_operation_reducer::free_result(res_point *res) +{ + if ((*res->prev_hook= res->next)) + { + res->get_next()->prev_hook= res->prev_hook; + } + free_item(res); +} + + +inline int Gcalc_operation_reducer::get_single_result(res_point *res, + Gcalc_result_receiver *storage) +{ + GCALC_DBUG_ENTER("Gcalc_operation_reducer::get_single_result"); + if (res->intersection_point) + { + double x, y; + res->pi->calc_xy(&x, &y); + if (storage->single_point(x,y)) + GCALC_DBUG_RETURN(1); + } + else + if (storage->single_point(res->pi->x, res->pi->y)) + GCALC_DBUG_RETURN(1); + free_result(res); + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::get_result_thread(res_point *cur, + Gcalc_result_receiver *storage, + int move_upward, + res_point *first_poly_node) +{ + res_point *next; + bool glue_step= false; + double x, y; + GCALC_DBUG_ENTER("Gcalc_operation_reducer::get_result_thread"); + while (cur) + { + if (!glue_step) + { + if (cur->intersection_point) + { + cur->pi->calc_xy(&x, &y); + } + else + { + x= cur->pi->x; + y= cur->pi->y; + } + if (storage->add_point(x, y)) + GCALC_DBUG_RETURN(1); + } + + next= move_upward ? cur->up : cur->down; + if (!next && !glue_step) + { + next= cur->glue; + move_upward^= 1; + glue_step= true; + if (next) + next->glue= NULL; + } + else + glue_step= false; + + cur->first_poly_node= first_poly_node; + free_result(cur); + cur= next; + } + GCALC_DBUG_RETURN(0); +} + + +int Gcalc_operation_reducer::get_polygon_result(res_point *cur, + Gcalc_result_receiver *storage, + res_point *first_poly_node) +{ + GCALC_DBUG_ENTER("Gcalc_operation_reducer::get_polygon_result"); + res_point *glue= cur->glue; + glue->up->down= NULL; + free_result(glue); + GCALC_DBUG_RETURN(get_result_thread(cur, storage, 1, first_poly_node) || + storage->complete_shape()); +} + + +int Gcalc_operation_reducer::get_line_result(res_point *cur, + Gcalc_result_receiver *storage) +{ + res_point *next; + res_point *cur_orig= cur; + int move_upward= 1; + GCALC_DBUG_ENTER("Gcalc_operation_reducer::get_line_result"); + if (cur->glue) + { + /* Here we have to find the beginning of the line */ + next= cur->up; + move_upward= 1; + while (next) + { + cur= next; + next= move_upward ? next->up : next->down; + if (!next) + { + next= cur->glue; + if (next == cur_orig) + { + /* It's the line loop */ + cur= cur_orig; + cur->glue->glue= NULL; + move_upward= 1; + break; + } + move_upward^= 1; + } + } + } + + GCALC_DBUG_RETURN(get_result_thread(cur, storage, move_upward, 0) || + storage->complete_shape()); +} + + +int Gcalc_operation_reducer::get_result(Gcalc_result_receiver *storage) +{ + poly_instance *polygons= NULL; + + GCALC_DBUG_ENTER("Gcalc_operation_reducer::get_result"); + *m_res_hook= NULL; + + while (m_result) + { + Gcalc_function::shape_type shape= m_result->type; + if (shape == Gcalc_function::shape_point) + { + if (get_single_result(m_result, storage)) + GCALC_DBUG_RETURN(1); + continue; + } + if (shape == Gcalc_function::shape_polygon) + { + if (m_result->outer_poly) + { + uint32 insert_position, hole_position, position_shift; + poly_instance *cur_poly; + insert_position= m_result->outer_poly->first_poly_node->poly_position; + GCALC_DBUG_ASSERT(insert_position); + hole_position= storage->position(); + storage->start_shape(Gcalc_function::shape_hole); + if (get_polygon_result(m_result, storage, + m_result->outer_poly->first_poly_node) || + storage->move_hole(insert_position, hole_position, + &position_shift)) + GCALC_DBUG_RETURN(1); + for (cur_poly= polygons; + cur_poly && *cur_poly->after_poly_position >= insert_position; + cur_poly= cur_poly->get_next()) + *cur_poly->after_poly_position+= position_shift; + } + else + { + uint32 *poly_position= &m_result->poly_position; + poly_instance *p= new_poly(); + p->after_poly_position= poly_position; + p->next= polygons; + polygons= p; + storage->start_shape(Gcalc_function::shape_polygon); + if (get_polygon_result(m_result, storage, m_result)) + GCALC_DBUG_RETURN(1); + *poly_position= storage->position(); + } + } + else + { + storage->start_shape(shape); + if (get_line_result(m_result, storage)) + GCALC_DBUG_RETURN(1); + } + } + + m_res_hook= (Gcalc_dyn_list::Item **)&m_result; + storage->done(); + GCALC_DBUG_RETURN(0); +} + + +void Gcalc_operation_reducer::reset() +{ + free_list((Gcalc_heap::Item **) &m_result, m_res_hook); + m_res_hook= (Gcalc_dyn_list::Item **)&m_result; + free_list(m_first_active_thread); +} + +#endif /*HAVE_SPATIAL*/ + diff --git a/sql/gcalc_tools.h b/sql/gcalc_tools.h new file mode 100644 index 00000000000..12ee56732a2 --- /dev/null +++ b/sql/gcalc_tools.h @@ -0,0 +1,348 @@ +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. + Copyright (C) 2011 Monty Program Ab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifndef GCALC_TOOLS_INCLUDED +#define GCALC_TOOLS_INCLUDED + +#include "gcalc_slicescan.h" +#include "sql_string.h" + + +/* + The Gcalc_function class objects are used to check for a binary relation. + The relation can be constructed with the prefix notation using predicates as + op_not (as !A) + op_union ( A || B || C... ) + op_intersection ( A && B && C ... ) + op_symdifference ( A+B+C+... == 1 ) + op_difference ( A && !(B||C||..)) + with the calls of the add_operation(operation, n_operands) method. + The relation is calculated over a set of shapes, that in turn have + to be added with the add_new_shape() method. All the 'shapes' can + be set to 0 with clear_shapes() method and single value + can be changed with the invert_state() method. + Then the value of the relation can be calculated with the count() method. + Frequently used method is find_function(Gcalc_scan_iterator it) that + iterates through the 'it' until the relation becomes TRUE. +*/ + +class Gcalc_function +{ +private: + String shapes_buffer; + String function_buffer; + int *i_states; + int *b_states; + uint32 cur_object_id; + uint n_shapes; + int count_internal(const char *cur_func, uint set_type, + const char **end); +public: + enum value + { + v_empty= 0x0000000, + v_find_t= 0x1000000, + v_find_f= 0x2000000, + v_t_found= 0x3000000, + v_f_found= 0x4000000, + v_mask= 0x7000000 + }; + enum op_type + { + op_not= 0x80000000, + op_shape= 0x00000000, + op_union= 0x10000000, + op_intersection= 0x20000000, + op_symdifference= 0x30000000, + op_difference= 0x40000000, + op_repeat= 0x50000000, + op_border= 0x60000000, + op_internals= 0x70000000, + op_false= 0x08000000, + op_any= 0x78000000 /* The mask to get any of the operations */ + }; + enum shape_type + { + shape_point= 0, + shape_line= 1, + shape_polygon= 2, + shape_hole= 3 + }; + Gcalc_function() : n_shapes(0) {} + gcalc_shape_info add_new_shape(uint32 shape_id, shape_type shape_kind); + /* + Adds the leaf operation that returns the shape value. + Also adds the shape to the list of operands. + */ + int single_shape_op(shape_type shape_kind, gcalc_shape_info *si); + void add_operation(uint operation, uint32 n_operands); + void add_not_operation(op_type operation, uint32 n_operands); + uint32 get_next_expression_pos() { return function_buffer.length(); } + void add_operands_to_op(uint32 operation_pos, uint32 n_operands); + int repeat_expression(uint32 exp_pos); + void set_cur_obj(uint32 cur_obj) { cur_object_id= cur_obj; } + int reserve_shape_buffer(uint n_shapes); + int reserve_op_buffer(uint n_ops); + uint get_nshapes() const { return n_shapes; } + shape_type get_shape_kind(gcalc_shape_info si) const + { + return (shape_type) uint4korr(shapes_buffer.ptr() + (si*4)); + } + + void set_states(int *shape_states) { i_states= shape_states; } + int alloc_states(); + void invert_i_state(gcalc_shape_info shape) { i_states[shape]^= 1; } + void set_i_state(gcalc_shape_info shape) { i_states[shape]= 1; } + void clear_i_state(gcalc_shape_info shape) { i_states[shape]= 0; } + void set_b_state(gcalc_shape_info shape) { b_states[shape]= 1; } + void clear_b_state(gcalc_shape_info shape) { b_states[shape]= 0; } + int get_state(gcalc_shape_info shape) + { return i_states[shape] | b_states[shape]; } + int get_i_state(gcalc_shape_info shape) { return i_states[shape]; } + int get_b_state(gcalc_shape_info shape) { return b_states[shape]; } + int count() + { return count_internal(function_buffer.ptr(), 0, 0); } + void clear_i_states(); + void clear_b_states(); + void reset(); + + int check_function(Gcalc_scan_iterator &scan_it); +}; + + +/* + Gcalc_operation_transporter class extends the Gcalc_shape_transporter. + In addition to the parent's functionality, it fills the Gcalc_function + object so it has the function that determines the proper shape. + For example Multipolyline will be represented as an union of polylines. +*/ + +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); + int empty_shape(); +}; + + +/* + When we calculate the result of an spatial operation like + Union or Intersection, we receive vertexes of the result + one-by-one, and probably need to treat them in variative ways. + So, the Gcalc_result_receiver class designed to get these + vertexes and construct shapes/objects out of them. + and to store the result in an appropriate format +*/ + +class Gcalc_result_receiver +{ + String buffer; + uint32 n_points; + Gcalc_function::shape_type common_shapetype; + bool collection_result; + uint32 n_shapes; + uint32 n_holes; + + Gcalc_function::shape_type cur_shape; + uint32 shape_pos; + double first_x, first_y, prev_x, prev_y; + double shape_area; +public: + Gcalc_result_receiver() : collection_result(FALSE), n_shapes(0), n_holes(0) + {} + int start_shape(Gcalc_function::shape_type shape); + int add_point(double x, double y); + int complete_shape(); + int single_point(double x, double y); + int done(); + void reset(); + + const char *result() { return buffer.ptr(); } + uint length() { return buffer.length(); } + int get_nshapes() { return n_shapes; } + int get_nholes() { return n_holes; } + int get_result_typeid(); + uint32 position() { return buffer.length(); } + int move_hole(uint32 dest_position, uint32 source_position, + uint32 *position_shift); +}; + + +/* + Gcalc_operation_reducer class incapsulates the spatial + operation functionality. It analyses the slices generated by + the slicescan and calculates the shape of the result defined + by some Gcalc_function. +*/ + +class Gcalc_operation_reducer : public Gcalc_dyn_list +{ +public: + enum modes + { + /* Numeric values important here - careful with changing */ + default_mode= 0, + prefer_big_with_holes= 1, + polygon_selfintersections_allowed= 2, /* allowed in the result */ + line_selfintersections_allowed= 4 /* allowed in the result */ + }; + + Gcalc_operation_reducer(size_t blk_size=8192); + void init(Gcalc_function *fn, modes mode= default_mode); + Gcalc_operation_reducer(Gcalc_function *fn, modes mode= default_mode, + size_t blk_size=8192); + GCALC_DECL_TERMINATED_STATE(killed) + int count_slice(Gcalc_scan_iterator *si); + int count_all(Gcalc_heap *hp); + int get_result(Gcalc_result_receiver *storage); + void reset(); + +#ifndef GCALC_DBUG_OFF + int n_res_points; +#endif /*GCALC_DBUG_OFF*/ + class res_point : public Gcalc_dyn_list::Item + { + public: + int intersection_point; + union + { + const Gcalc_heap::Info *pi; + res_point *first_poly_node; + }; + union + { + res_point *outer_poly; + uint32 poly_position; + }; + res_point *up; + res_point *down; + res_point *glue; + Gcalc_function::shape_type type; + Gcalc_dyn_list::Item **prev_hook; +#ifndef GCALC_DBUG_OFF + int point_n; +#endif /*GCALC_DBUG_OFF*/ + void set(const Gcalc_scan_iterator *si); + res_point *get_next() { return (res_point *)next; } + }; + + class active_thread : public Gcalc_dyn_list::Item + { + public: + res_point *rp; + res_point *thread_start; + + const Gcalc_heap::Info *p1, *p2; + res_point *enabled() { return rp; } + active_thread *get_next() { return (active_thread *)next; } + }; + + class poly_instance : public Gcalc_dyn_list::Item + { + public: + uint32 *after_poly_position; + poly_instance *get_next() { return (poly_instance *)next; } + }; + + class line : public Gcalc_dyn_list::Item + { + public: + active_thread *t; + int incoming; + const Gcalc_scan_iterator::point *p; + line *get_next() { return (line *)next; } + }; + + class poly_border : public Gcalc_dyn_list::Item + { + public: + active_thread *t; + int incoming; + int prev_state; + const Gcalc_scan_iterator::point *p; + poly_border *get_next() { return (poly_border *)next; } + }; + + line *m_lines; + Gcalc_dyn_list::Item **m_lines_hook; + poly_border *m_poly_borders; + Gcalc_dyn_list::Item **m_poly_borders_hook; + line *new_line() { return (line *) new_item(); } + poly_border *new_poly_border() { return (poly_border *) new_item(); } + int add_line(int incoming, active_thread *t, + const Gcalc_scan_iterator::point *p); + int add_poly_border(int incoming, active_thread *t, int prev_state, + const Gcalc_scan_iterator::point *p); + +protected: + Gcalc_function *m_fn; + Gcalc_dyn_list::Item **m_res_hook; + res_point *m_result; + int m_mode; + + res_point *result_heap; + active_thread *m_first_active_thread; + + res_point *add_res_point(Gcalc_function::shape_type type); + active_thread *new_active_thread() { return (active_thread *)new_item(); } + + poly_instance *new_poly() { return (poly_instance *) new_item(); } + +private: + int start_line(active_thread *t, const Gcalc_scan_iterator::point *p, + const Gcalc_scan_iterator *si); + int end_line(active_thread *t, const Gcalc_scan_iterator *si); + int connect_threads(int incoming_a, int incoming_b, + active_thread *ta, active_thread *tb, + const Gcalc_scan_iterator::point *pa, + const Gcalc_scan_iterator::point *pb, + active_thread *prev_range, + const Gcalc_scan_iterator *si, + Gcalc_function::shape_type s_t); + int add_single_point(const Gcalc_scan_iterator *si); + poly_border *get_pair_border(poly_border *b1); + int continue_range(active_thread *t, const Gcalc_heap::Info *p, + const Gcalc_heap::Info *p_next); + int continue_i_range(active_thread *t, + const Gcalc_heap::Info *ii); + int end_couple(active_thread *t0, active_thread *t1, const Gcalc_heap::Info *p); + int get_single_result(res_point *res, Gcalc_result_receiver *storage); + int get_result_thread(res_point *cur, Gcalc_result_receiver *storage, + int move_upward, res_point *first_poly_node); + int get_polygon_result(res_point *cur, Gcalc_result_receiver *storage, + res_point *first_poly_node); + int get_line_result(res_point *cur, Gcalc_result_receiver *storage); + + void free_result(res_point *res); +}; + +#endif /*GCALC_TOOLS_INCLUDED*/ + diff --git a/sql/gstream.cc b/sql/gstream.cc index 61263380411..3a9e478c376 100644 --- a/sql/gstream.cc +++ b/sql/gstream.cc @@ -41,6 +41,29 @@ enum Gis_read_stream::enum_tok_types Gis_read_stream::get_next_toc_type() } +bool Gis_read_stream::lookup_next_word(LEX_STRING *res) +{ + const char *cur= m_cur; + + skip_space(); + res->str= (char*) cur; + /* The following will also test for \0 */ + if ((cur >= m_limit) || !my_isvar_start(&my_charset_bin, *cur)) + return 1; + + /* + We can't combine the following increment with my_isvar() because + my_isvar() is a macro that would cause side effects + */ + cur++; + while ((cur < m_limit) && my_isvar(&my_charset_bin, *cur)) + cur++; + + res->length= (uint32) (cur - res->str); + return 0; +} + + bool Gis_read_stream::get_next_word(LEX_STRING *res) { skip_space(); diff --git a/sql/gstream.h b/sql/gstream.h index 533fafa28f3..f10b7e9b830 100644 --- a/sql/gstream.h +++ b/sql/gstream.h @@ -46,6 +46,7 @@ public: } enum enum_tok_types get_next_toc_type(); + bool lookup_next_word(LEX_STRING *res); bool get_next_word(LEX_STRING *); bool get_next_number(double *); bool check_next_symbol(char); @@ -64,6 +65,14 @@ public: m_cur++; return 0; } + /* Returns the next notempty character. */ + char next_symbol() + { + skip_space(); + if (m_cur >= m_limit) + return 0; /* EOL meet. */ + return *m_cur; + } void set_error_msg(const char *msg); // caller should free this pointer diff --git a/sql/item_create.cc b/sql/item_create.cc index 315ca857f6f..9c8d6b73e60 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -580,6 +580,19 @@ protected: #ifdef HAVE_SPATIAL +class Create_func_mbr_contains : public Create_func_arg2 +{ + public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mbr_contains s_singleton; + + protected: + Create_func_mbr_contains() {} + virtual ~Create_func_mbr_contains() {} +}; + + class Create_func_contains : public Create_func_arg2 { public: @@ -820,6 +833,19 @@ protected: #ifdef HAVE_SPATIAL +class Create_func_mbr_disjoint : public Create_func_arg2 +{ + public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mbr_disjoint s_singleton; + + protected: + Create_func_mbr_disjoint() {} + virtual ~Create_func_mbr_disjoint() {} +}; + + class Create_func_disjoint : public Create_func_arg2 { public: @@ -831,6 +857,19 @@ protected: Create_func_disjoint() {} virtual ~Create_func_disjoint() {} }; + + +class Create_func_distance : public Create_func_arg2 +{ + public: + virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_distance s_singleton; + + protected: + Create_func_distance() {} + virtual ~Create_func_distance() {} +}; #endif @@ -904,6 +943,19 @@ protected: #ifdef HAVE_SPATIAL +class Create_func_mbr_equals : public Create_func_arg2 +{ + public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mbr_equals s_singleton; + + protected: + Create_func_mbr_equals() {} + virtual ~Create_func_mbr_equals() {} +}; + + class Create_func_equals : public Create_func_arg2 { public: @@ -1232,6 +1284,19 @@ protected: #ifdef HAVE_SPATIAL +class Create_func_mbr_intersects : public Create_func_arg2 +{ + public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mbr_intersects s_singleton; + + protected: + Create_func_mbr_intersects() {} + virtual ~Create_func_mbr_intersects() {} +}; + + class Create_func_intersects : public Create_func_arg2 { public: @@ -1243,7 +1308,72 @@ protected: Create_func_intersects() {} virtual ~Create_func_intersects() {} }; -#endif + + +class Create_func_intersection : public Create_func_arg2 +{ +public: + virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_intersection s_singleton; + +protected: + Create_func_intersection() {} + virtual ~Create_func_intersection() {} +}; + + +class Create_func_difference : public Create_func_arg2 +{ +public: + virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_difference s_singleton; + +protected: + Create_func_difference() {} + virtual ~Create_func_difference() {} +}; + + +class Create_func_union : public Create_func_arg2 +{ +public: + virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_union s_singleton; + +protected: + Create_func_union() {} + virtual ~Create_func_union() {} +}; + + +class Create_func_symdifference : public Create_func_arg2 +{ +public: + virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_symdifference s_singleton; + +protected: + Create_func_symdifference() {} + virtual ~Create_func_symdifference() {} +}; + + +class Create_func_buffer : public Create_func_arg2 +{ +public: + virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_buffer s_singleton; + +protected: + Create_func_buffer() {} + virtual ~Create_func_buffer() {} +}; +#endif /*HAVE_SPATIAL*/ class Create_func_is_free_lock : public Create_func_arg1 @@ -1703,6 +1833,19 @@ protected: #ifdef HAVE_SPATIAL +class Create_func_mbr_overlaps : public Create_func_arg2 +{ + public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mbr_overlaps s_singleton; + + protected: + Create_func_mbr_overlaps() {} + virtual ~Create_func_mbr_overlaps() {} +}; + + class Create_func_overlaps : public Create_func_arg2 { public: @@ -2323,6 +2466,19 @@ protected: #ifdef HAVE_SPATIAL +class Create_func_mbr_within : public Create_func_arg2 +{ + public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mbr_within s_singleton; + + protected: + Create_func_mbr_within() {} + virtual ~Create_func_mbr_within() {} +}; + + class Create_func_within : public Create_func_arg2 { public: @@ -3015,6 +3171,16 @@ Create_func_connection_id::create_builder(THD *thd) #ifdef HAVE_SPATIAL +Create_func_mbr_contains Create_func_mbr_contains::s_singleton; + +Item* +Create_func_mbr_contains::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_mbr_rel(arg1, arg2, + Item_func::SP_CONTAINS_FUNC); +} + + Create_func_contains Create_func_contains::s_singleton; Item* @@ -3245,6 +3411,16 @@ Create_func_dimension::create_1_arg(THD *thd, Item *arg1) #ifdef HAVE_SPATIAL +Create_func_mbr_disjoint Create_func_mbr_disjoint::s_singleton; + +Item* +Create_func_mbr_disjoint::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_mbr_rel(arg1, arg2, + Item_func::SP_DISJOINT_FUNC); +} + + Create_func_disjoint Create_func_disjoint::s_singleton; Item* @@ -3253,6 +3429,15 @@ Create_func_disjoint::create_2_arg(THD *thd, Item *arg1, Item *arg2) return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, Item_func::SP_DISJOINT_FUNC); } + + +Create_func_distance Create_func_distance::s_singleton; + +Item* +Create_func_distance::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_distance(arg1, arg2); +} #endif @@ -3348,6 +3533,16 @@ Create_func_envelope::create_1_arg(THD *thd, Item *arg1) #ifdef HAVE_SPATIAL +Create_func_mbr_equals Create_func_mbr_equals::s_singleton; + +Item* +Create_func_mbr_equals::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_mbr_rel(arg1, arg2, + Item_func::SP_EQUALS_FUNC); +} + + Create_func_equals Create_func_equals::s_singleton; Item* @@ -3769,6 +3964,16 @@ Create_func_interiorringn::create_2_arg(THD *thd, Item *arg1, Item *arg2) #ifdef HAVE_SPATIAL +Create_func_mbr_intersects Create_func_mbr_intersects::s_singleton; + +Item* +Create_func_mbr_intersects::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_mbr_rel(arg1, arg2, + Item_func::SP_INTERSECTS_FUNC); +} + + Create_func_intersects Create_func_intersects::s_singleton; Item* @@ -3777,7 +3982,56 @@ Create_func_intersects::create_2_arg(THD *thd, Item *arg1, Item *arg2) return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, Item_func::SP_INTERSECTS_FUNC); } -#endif + + +Create_func_intersection Create_func_intersection::s_singleton; + +Item* +Create_func_intersection::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_operation(arg1, arg2, + Gcalc_function::op_intersection); +} + + +Create_func_difference Create_func_difference::s_singleton; + +Item* +Create_func_difference::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_operation(arg1, arg2, + Gcalc_function::op_difference); +} + + +Create_func_union Create_func_union::s_singleton; + +Item* +Create_func_union::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_operation(arg1, arg2, + Gcalc_function::op_union); +} + + +Create_func_symdifference Create_func_symdifference::s_singleton; + +Item* +Create_func_symdifference::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_operation(arg1, arg2, + Gcalc_function::op_symdifference); +} + + +Create_func_buffer Create_func_buffer::s_singleton; + +Item* +Create_func_buffer::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_buffer(arg1, arg2); +} +#endif /*HAVE_SPATAI*/ Create_func_is_free_lock Create_func_is_free_lock::s_singleton; @@ -4258,6 +4512,16 @@ Create_func_ord::create_1_arg(THD *thd, Item *arg1) #ifdef HAVE_SPATIAL +Create_func_mbr_overlaps Create_func_mbr_overlaps::s_singleton; + +Item* +Create_func_mbr_overlaps::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_mbr_rel(arg1, arg2, + Item_func::SP_OVERLAPS_FUNC); +} + + Create_func_overlaps Create_func_overlaps::s_singleton; Item* @@ -4831,6 +5095,16 @@ Create_func_weekofyear::create_1_arg(THD *thd, Item *arg1) #ifdef HAVE_SPATIAL +Create_func_mbr_within Create_func_mbr_within::s_singleton; + +Item* +Create_func_mbr_within::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_mbr_rel(arg1, arg2, + Item_func::SP_WITHIN_FUNC); +} + + Create_func_within Create_func_within::s_singleton; Item* @@ -4964,6 +5238,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)}, { { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)}, { { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)}, + { { C_STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)}, { { C_STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)}, { { C_STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)}, { { C_STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, @@ -4991,7 +5266,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)}, { { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)}, { { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, - { { C_STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)}, + { { C_STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, { { C_STRING_WITH_LEN("ELT") }, BUILDER(Create_func_elt)}, { { C_STRING_WITH_LEN("ENCODE") }, BUILDER(Create_func_encode)}, { { C_STRING_WITH_LEN("ENCRYPT") }, BUILDER(Create_func_encrypt)}, @@ -5028,7 +5303,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)}, { { C_STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)}, { { C_STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)}, - { { C_STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)}, + { { C_STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)}, { { C_STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)}, { { C_STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)}, { { C_STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)}, @@ -5061,13 +5336,13 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)}, { { C_STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)}, { { C_STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)}, - { { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_contains)}, - { { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_disjoint)}, - { { C_STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_equals)}, - { { C_STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_intersects)}, - { { C_STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)}, + { { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)}, + { { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, + { { C_STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_mbr_equals)}, + { { C_STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)}, + { { C_STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, { { C_STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)}, - { { C_STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_within)}, + { { C_STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_mbr_within)}, { { C_STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)}, { { C_STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, { { C_STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, @@ -5090,7 +5365,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)}, { { C_STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_length)}, { { C_STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)}, - { { C_STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)}, + { { C_STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, { { C_STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)}, { { C_STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)}, { { C_STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)}, @@ -5126,6 +5401,64 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)}, { { C_STRING_WITH_LEN("STRCMP") }, BUILDER(Create_func_strcmp)}, { { C_STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)}, + { { C_STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)}, + { { C_STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)}, + { { C_STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)}, + { { C_STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)}, + { { C_STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, + { { C_STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)}, + { { C_STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, + { { C_STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)}, + { { C_STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)}, + { { C_STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)}, + { { C_STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, + { { C_STRING_WITH_LEN("ST_DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)}, + { { C_STRING_WITH_LEN("ST_DISTANCE") }, GEOM_BUILDER(Create_func_distance)}, + { { C_STRING_WITH_LEN("ST_ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)}, + { { C_STRING_WITH_LEN("ST_ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)}, + { { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)}, + { { C_STRING_WITH_LEN("ST_EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)}, + { { C_STRING_WITH_LEN("ST_GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)}, + { { C_STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)}, + { { C_STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)}, + { { C_STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)}, + { { C_STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)}, + { { C_STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)}, + { { C_STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)}, + { { C_STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)}, + { { C_STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)}, + { { C_STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)}, + { { C_STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)}, + { { C_STRING_WITH_LEN("ST_NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)}, + { { C_STRING_WITH_LEN("ST_NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)}, + { { C_STRING_WITH_LEN("ST_OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)}, + { { C_STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)}, + { { C_STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { C_STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { C_STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)}, + { { C_STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)}, + { { C_STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)}, + { { C_STRING_WITH_LEN("ST_TOUCHES") }, GEOM_BUILDER(Create_func_touches)}, + { { C_STRING_WITH_LEN("ST_UNION") }, GEOM_BUILDER(Create_func_union)}, + { { C_STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)}, + { { C_STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)}, + { { C_STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)}, { { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)}, { { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)}, { { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)}, diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 621ddcb1a30..5ccdb198ecb 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. + Copyright (C) 2011 Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +37,7 @@ #ifdef HAVE_SPATIAL #include <m_ctype.h> + Field *Item_geometry_func::tmp_table_field(TABLE *t_arg) { Field *result; @@ -368,8 +370,8 @@ String *Item_func_point::val_str(String *str) uint32 srid= 0; if ((null_value= (args[0]->null_value || - args[1]->null_value || - str->realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE*2)))) + args[1]->null_value || + str->realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2)))) return 0; str->set_charset(&my_charset_bin); @@ -516,7 +518,33 @@ err: Functions for spatial relations */ -longlong Item_func_spatial_rel::val_int() +const char *Item_func_spatial_mbr_rel::func_name() const +{ + switch (spatial_rel) { + case SP_CONTAINS_FUNC: + return "mbrcontains"; + case SP_WITHIN_FUNC: + return "mbrwithin"; + case SP_EQUALS_FUNC: + return "mbrequals"; + case SP_DISJOINT_FUNC: + return "mbrdisjoint"; + case SP_INTERSECTS_FUNC: + return "mbrintersects"; + case SP_TOUCHES_FUNC: + return "mbrtouches"; + case SP_CROSSES_FUNC: + return "mbrcrosses"; + case SP_OVERLAPS_FUNC: + return "mbroverlaps"; + default: + DBUG_ASSERT(0); // Should never happened + return "mbrsp_unknown"; + } +} + + +longlong Item_func_spatial_mbr_rel::val_int() { DBUG_ASSERT(fixed == 1); String *res1= args[0]->val_str(&cmp.value1); @@ -561,6 +589,844 @@ longlong Item_func_spatial_rel::val_int() } +Item_func_spatial_rel::Item_func_spatial_rel(Item *a,Item *b, + enum Functype sp_rel) : + Item_bool_func2(a,b), collector() +{ + spatial_rel = sp_rel; +} + + +Item_func_spatial_rel::~Item_func_spatial_rel() +{ +} + + +const char *Item_func_spatial_rel::func_name() const +{ + switch (spatial_rel) { + case SP_CONTAINS_FUNC: + return "st_contains"; + case SP_WITHIN_FUNC: + return "st_within"; + case SP_EQUALS_FUNC: + return "st_equals"; + case SP_DISJOINT_FUNC: + return "st_disjoint"; + case SP_INTERSECTS_FUNC: + return "st_intersects"; + case SP_TOUCHES_FUNC: + return "st_touches"; + case SP_CROSSES_FUNC: + return "st_crosses"; + case SP_OVERLAPS_FUNC: + return "st_overlaps"; + default: + DBUG_ASSERT(0); // Should never happened + return "sp_unknown"; + } +} + + +static double count_edge_t(const Gcalc_heap::Info *ea, + const Gcalc_heap::Info *eb, + const Gcalc_heap::Info *v, + double &ex, double &ey, double &vx, double &vy, + double &e_sqrlen) +{ + ex= eb->x - ea->x; + ey= eb->y - ea->y; + vx= v->x - ea->x; + vy= v->y - ea->y; + e_sqrlen= ex * ex + ey * ey; + return (ex * vx + ey * vy) / e_sqrlen; +} + + +static double distance_to_line(double ex, double ey, double vx, double vy, + double e_sqrlen) +{ + return fabs(vx * ey - vy * ex) / sqrt(e_sqrlen); +} + + +static double distance_points(const Gcalc_heap::Info *a, + const Gcalc_heap::Info *b) +{ + double x= a->x - b->x; + double y= a->y - b->y; + return sqrt(x * x + y * y); +} + + +/* + 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() +{ + DBUG_ENTER("Item_func_spatial_rel::val_int"); + DBUG_ASSERT(fixed == 1); + String *res1; + String *res2; + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; + int result= 0; + int mask= 0; + uint shape_a, shape_b; + + res1= args[0]->val_str(&tmp_value1); + res2= args[1]->val_str(&tmp_value2); + Gcalc_operation_transporter trn(&func, &collector); + + if (func.reserve_op_buffer(1)) + DBUG_RETURN(0); + + 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()))))) + goto exit; + + switch (spatial_rel) { + case SP_CONTAINS_FUNC: + 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: + 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: + mask= 1; + func.add_operation(Gcalc_function::op_symdifference, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_DISJOINT_FUNC: + mask= 1; + func.add_operation(Gcalc_function::op_intersection, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_INTERSECTS_FUNC: + func.add_operation(Gcalc_function::op_intersection, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_OVERLAPS_FUNC: + case SP_CROSSES_FUNC: + func.add_operation(Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_intersection, 2); + shape_a= func.get_next_expression_pos(); + if ((null_value= g1->store_shapes(&trn))) + break; + shape_b= func.get_next_expression_pos(); + if ((null_value= g2->store_shapes(&trn))) + break; + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_difference, 2); + func.repeat_expression(shape_a); + func.repeat_expression(shape_b); + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_difference, 2); + func.repeat_expression(shape_b); + func.repeat_expression(shape_a); + break; + case SP_TOUCHES_FUNC: + func.add_operation(Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::v_find_f | + Gcalc_function::op_not | + Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::op_internals, 1); + shape_a= func.get_next_expression_pos(); + if ((null_value= g1->store_shapes(&trn))) + break; + func.add_operation(Gcalc_function::op_internals, 1); + shape_b= func.get_next_expression_pos(); + if ((null_value= g2->store_shapes(&trn))) + break; + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::op_border, 1); + func.repeat_expression(shape_a); + func.add_operation(Gcalc_function::op_border, 1); + func.repeat_expression(shape_b); + break; + default: + DBUG_ASSERT(FALSE); + break; + } + + if (null_value) + goto exit; + + collector.prepare_operation(); + 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; + + result= func.check_function(scan_it) ^ mask; + +exit: + collector.reset(); + func.reset(); + scan_it.reset(); + DBUG_RETURN(result); +} + + +Item_func_spatial_operation::~Item_func_spatial_operation() +{ +} + + +String *Item_func_spatial_operation::val_str(String *str_value) +{ + DBUG_ENTER("Item_func_spatial_operation::val_str"); + DBUG_ASSERT(fixed == 1); + String *res1= args[0]->val_str(&tmp_value1); + String *res2= args[1]->val_str(&tmp_value2); + 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 || + !(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) || + !(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())) || + g1->store_shapes(&trn) || g2->store_shapes(&trn)))) + { + str_value= 0; + goto exit; + } + + + collector.prepare_operation(); + if (func.alloc_states()) + goto exit; + + operation.init(&func); + + if (operation.count_all(&collector) || + operation.get_result(&res_receiver)) + goto exit; + + + str_value->set_charset(&my_charset_bin); + if (str_value->reserve(SRID_SIZE, 512)) + goto exit; + str_value->length(0); + str_value->q_append(srid); + + if (!Geometry::create_from_opresult(&buffer1, str_value, res_receiver)) + goto exit; + +exit: + collector.reset(); + func.reset(); + res_receiver.reset(); + DBUG_RETURN(str_value); +} + + +const char *Item_func_spatial_operation::func_name() const +{ + switch (spatial_op) { + case Gcalc_function::op_intersection: + return "st_intersection"; + case Gcalc_function::op_difference: + return "st_difference"; + case Gcalc_function::op_union: + return "st_union"; + case Gcalc_function::op_symdifference: + return "st_symdifference"; + default: + DBUG_ASSERT(0); // Should never happen + return "sp_unknown"; + } +} + + +static const int SINUSES_CALCULATED= 32; +static double n_sinus[SINUSES_CALCULATED+1]= +{ + 0, + 0.04906767432741802, + 0.0980171403295606, + 0.1467304744553618, + 0.1950903220161283, + 0.2429801799032639, + 0.2902846772544623, + 0.3368898533922201, + 0.3826834323650898, + 0.4275550934302821, + 0.4713967368259976, + 0.5141027441932217, + 0.5555702330196022, + 0.5956993044924334, + 0.6343932841636455, + 0.6715589548470183, + 0.7071067811865475, + 0.7409511253549591, + 0.773010453362737, + 0.8032075314806448, + 0.8314696123025452, + 0.8577286100002721, + 0.8819212643483549, + 0.9039892931234433, + 0.9238795325112867, + 0.9415440651830208, + 0.9569403357322089, + 0.970031253194544, + 0.9807852804032304, + 0.989176509964781, + 0.9951847266721968, + 0.9987954562051724, + 1 +}; + + +static void get_n_sincos(int n, double *sinus, double *cosinus) +{ + DBUG_ASSERT(n > 0 && n < SINUSES_CALCULATED*2+1); + if (n < (SINUSES_CALCULATED + 1)) + { + *sinus= n_sinus[n]; + *cosinus= n_sinus[SINUSES_CALCULATED - n]; + } + else + { + n-= SINUSES_CALCULATED; + *sinus= n_sinus[SINUSES_CALCULATED - n]; + *cosinus= -n_sinus[n]; + } +} + + +static int fill_half_circle(Gcalc_shape_transporter *trn, double x, double y, + double ax, double ay) +{ + double n_sin, n_cos; + double x_n, y_n; + for (int n = 1; n < (SINUSES_CALCULATED * 2 - 1); n++) + { + get_n_sincos(n, &n_sin, &n_cos); + x_n= ax * n_cos - ay * n_sin; + y_n= ax * n_sin + ay * n_cos; + if (trn->add_point(x_n + x, y_n + y)) + return 1; + } + return 0; +} + + +static int fill_gap(Gcalc_shape_transporter *trn, + double x, double y, + double ax, double ay, double bx, double by, double d, + bool *empty_gap) +{ + double ab= ax * bx + ay * by; + double cosab= ab / (d * d) + GIS_ZERO; + double n_sin, n_cos; + double x_n, y_n; + int n=1; + + *empty_gap= true; + for (;;) + { + get_n_sincos(n++, &n_sin, &n_cos); + if (n_cos <= cosab) + break; + *empty_gap= false; + x_n= ax * n_cos - ay * n_sin; + y_n= ax * n_sin + ay * n_cos; + if (trn->add_point(x_n + x, y_n + y)) + return 1; + } + return 0; +} + + +/* + Calculates the vector (p2,p1) and + negatively orthogonal to it with the length of d. + The result is (ex,ey) - the vector, (px,py) - the orthogonal. +*/ + +static void calculate_perpendicular( + double x1, double y1, double x2, double y2, double d, + double *ex, double *ey, + double *px, double *py) +{ + 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; + + ++m_nshapes; + if (trn.start_simple_poly()) + return 1; + + 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]; + cos1= n_sinus[31]; + if (e1e2 < 0) + { + empty_gap2= false; + 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= 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(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(x2 + p1_x, y2 + p1_y)) || + trn.add_point(x1 + p1_x, y1 + p1_y)) + return 1; + + if (round_p1 && fill_half_circle(&trn, x1, y1, p1_x, p1_y)) + return 1; + + if (trn.add_point(x1 - p1_x, y1 - p1_y) || + (!empty_gap1 && trn.add_point(x2 - p1_x, y2 - p1_y))) + return 1; + return trn.complete_simple_poly(); +} + + +int Item_func_buffer::Transporter::add_last_edge_buffer() +{ + Gcalc_operation_transporter trn(m_fn, m_heap); + double e1_x, e1_y, p1_x, p1_y; + + ++m_nshapes; + if (trn.start_simple_poly()) + return 1; + + calculate_perpendicular(x1, y1, x2, y2, m_d, &e1_x, &e1_y, &p1_x, &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; + return trn.complete_simple_poly(); +} + + +int Item_func_buffer::Transporter::add_point_buffer(double x, double y) +{ + Gcalc_operation_transporter trn(m_fn, m_heap); + + m_nshapes++; + if (trn.start_simple_poly()) + return 1; + 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; + return trn.complete_simple_poly(); +} + + +int Item_func_buffer::Transporter::start_line() +{ + if (buffer_op == Gcalc_function::op_difference) + { + skip_line= TRUE; + return 0; + } + + m_nshapes= 0; + + if (m_fn->reserve_op_buffer(2)) + return 1; + last_shape_pos= m_fn->get_next_expression_pos(); + m_fn->add_operation(buffer_op, 0); + m_npoints= 0; + int_start_line(); + return 0; +} + + +int Item_func_buffer::Transporter::start_poly() +{ + m_nshapes= 1; + + if (m_fn->reserve_op_buffer(2)) + return 1; + last_shape_pos= m_fn->get_next_expression_pos(); + m_fn->add_operation(buffer_op, 0); + return Gcalc_operation_transporter::start_poly(); +} + + +int Item_func_buffer::Transporter::complete_poly() +{ + if (Gcalc_operation_transporter::complete_poly()) + return 1; + m_fn->add_operands_to_op(last_shape_pos, m_nshapes); + return 0; +} + + +int Item_func_buffer::Transporter::start_ring() +{ + m_npoints= 0; + return Gcalc_operation_transporter::start_ring(); +} + + +int Item_func_buffer::Transporter::start_collection(int n_objects) +{ + if (m_fn->reserve_op_buffer(1)) + return 1; + m_fn->add_operation(Gcalc_function::op_union, n_objects); + return 0; +} + + +int Item_func_buffer::Transporter::add_point(double x, double y) +{ + if (skip_line) + return 0; + + if (m_npoints && x == x2 && y == y2) + return 0; + + ++m_npoints; + + if (m_npoints == 1) + { + x00= x; + y00= y; + } + else if (m_npoints == 2) + { + x01= x; + y01= y; + } + else if (add_edge_buffer(x, y, (m_npoints == 3) && line_started(), false)) + return 1; + + 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 (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; + } + } + + return 0; +} + + +int Item_func_buffer::Transporter::complete_line() +{ + if (!skip_line) + { + if (complete()) + return 1; + int_complete_line(); + m_fn->add_operands_to_op(last_shape_pos, m_nshapes); + } + skip_line= FALSE; + 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"); + DBUG_ASSERT(fixed == 1); + String *obj= args[0]->val_str(&tmp_value); + double dist= args[1]->val_real(); + Geometry_buffer buffer; + Geometry *g; + uint32 srid= 0; + String *str_result= NULL; + Transporter trn(&func, &collector, dist); + + null_value= 1; + if (args[0]->null_value || args[1]->null_value || + !(g= Geometry::construct(&buffer, obj->ptr(), obj->length()))) + goto mem_error; + + /* + If the distance given is 0, the Buffer function is in fact NOOP, + so it's natural just to return the argument1. + Besides, internal calculations here can't handle zero distance anyway. + */ + if (fabs(dist) < GIS_ZERO) + { + null_value= 0; + str_result= obj; + goto mem_error; + } + + if (g->store_shapes(&trn)) + goto mem_error; + + collector.prepare_operation(); + if (func.alloc_states()) + goto mem_error; + operation.init(&func); + operation.killed= (int *) &(current_thd->killed); + + if (operation.count_all(&collector) || + operation.get_result(&res_receiver)) + goto mem_error; + + + str_value->set_charset(&my_charset_bin); + if (str_value->reserve(SRID_SIZE, 512)) + goto mem_error; + str_value->length(0); + str_value->q_append(srid); + + if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver)) + goto mem_error; + + null_value= 0; + str_result= str_value; +mem_error: + collector.reset(); + func.reset(); + res_receiver.reset(); + DBUG_RETURN(str_result); +} + + longlong Item_func_isempty::val_int() { DBUG_ASSERT(fixed == 1); @@ -574,21 +1440,59 @@ longlong Item_func_isempty::val_int() } -/** - @todo - Ramil or Holyfoot, add real IsSimple calculation -*/ longlong Item_func_issimple::val_int() { - DBUG_ASSERT(fixed == 1); - String tmp; String *swkb= args[0]->val_str(&tmp); Geometry_buffer buffer; + Gcalc_operation_transporter trn(&func, &collector); + Geometry *g; + int result= 1; + const Gcalc_scan_iterator::event_point *ev; + + DBUG_ENTER("Item_func_issimple::val_int"); + DBUG_ASSERT(fixed == 1); - null_value= args[0]->null_value || - !(Geometry::construct(&buffer, swkb->ptr(), swkb->length())); - /* TODO: Ramil or Holyfoot, add real IsSimple calculation */ - return 0; + if ((null_value= args[0]->null_value) || + !(g= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))) + DBUG_RETURN(0); + + + if (g->get_class_info()->m_type_id == Geometry::wkb_point) + DBUG_RETURN(1); + + if (g->store_shapes(&trn)) + goto mem_error; + + collector.prepare_operation(); + scan_it.init(&collector); + + while (scan_it.more_points()) + { + if (scan_it.step()) + goto mem_error; + + ev= scan_it.get_events(); + if (ev->simple_event()) + continue; + + if ((ev->event == scev_thread || ev->event == scev_single_point) && + !ev->get_next()) + continue; + + if (ev->event == scev_two_threads && !ev->get_next()->get_next()) + continue; + + result= 0; + break; + } + + collector.reset(); + func.reset(); + scan_it.reset(); + DBUG_RETURN(result); +mem_error: + null_value= 1; + DBUG_RETURN(0); } @@ -736,12 +1640,13 @@ double Item_func_glength::val_real() String *swkb= args[0]->val_str(&value); Geometry_buffer buffer; Geometry *geom; + const char *end; null_value= (!swkb || !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length())) || - geom->geom_length(&res)); + geom->geom_length(&res, &end)); return res; } @@ -760,4 +1665,161 @@ longlong Item_func_srid::val_int() return (longlong) (uint4korr(swkb->ptr())); } + +double Item_func_distance::val_real() +{ + bool cur_point_edge; + const Gcalc_scan_iterator::point *evpos; + const Gcalc_heap::Info *cur_point, *dist_point; + const Gcalc_scan_iterator::event_point *ev; + double t, distance, cur_distance; + 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); + String *res1= args[0]->val_str(&tmp_value1); + String *res2= args[1]->val_str(&tmp_value2); + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; + + + 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()))))) + goto mem_error; + + if ((g1->get_class_info()->m_type_id == Geometry::wkb_point) && + (g2->get_class_info()->m_type_id == Geometry::wkb_point)) + { + if (((Gis_point *) g1)->get_xy(&x1, &y1) || + ((Gis_point *) g2)->get_xy(&x2, &y2)) + goto mem_error; + ex= x2 - x1; + ey= y2 - y1; + DBUG_RETURN(sqrt(ex * ex + ey * ey)); + } + + if (func.reserve_op_buffer(1)) + goto mem_error; + func.add_operation(Gcalc_function::op_intersection, 2); + + if (g1->store_shapes(&trn)) + goto mem_error; + obj2_si= func.get_nshapes(); + if (g2->store_shapes(&trn) || func.alloc_states()) + goto mem_error; + + if (obj2_si == 0 || func.get_nshapes() == obj2_si) + { + distance= 0.0; + null_value= 1; + goto exit; + } + + + collector.prepare_operation(); + scan_it.init(&collector); + + 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(); + + if (ev->simple_event()) + { + cur_point= ev->pi; + goto count_distance; + } + /* + handling intersection we only need to check if it's the intersecion + of objects 1 and 2. In this case distance is 0 + */ + cur_point= NULL; + + /* + having these events we need to check for possible intersection + of objects + scev_thread | scev_two_threads | scev_single_point + */ + func.clear_i_states(); + 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_i_state(si); + } + + func.clear_b_states(); + for (; ev; ev= ev->get_next()) + { + if (ev->event != scev_intersection) + cur_point= ev->pi; + func.set_b_state(ev->get_shape()); + if (func.count()) + { + /* Point of one object is inside the other - intersection found */ + distance= 0; + goto exit; + } + } + + if (!cur_point) + continue; + +count_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: + collector.reset(); + func.reset(); + scan_it.reset(); + DBUG_RETURN(distance); +mem_error: + null_value= 1; + DBUG_RETURN(0); +} + + #endif /*HAVE_SPATIAL*/ diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index c5c1eae2d39..3638d9f62e8 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -1,7 +1,8 @@ #ifndef ITEM_GEOFUNC_INCLUDED #define ITEM_GEOFUNC_INCLUDED -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. + Copyright (C) 2011 Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,8 +14,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ /* This file defines all spatial functions */ @@ -25,6 +26,9 @@ #pragma interface /* gcc class implementation */ #endif +#include "gcalc_slicescan.h" +#include "gcalc_tools.h" + class Item_geometry_func: public Item_str_func { public: @@ -44,7 +48,7 @@ class Item_func_geometry_from_text: public Item_geometry_func public: Item_func_geometry_from_text(Item *a) :Item_geometry_func(a) {} Item_func_geometry_from_text(Item *a, Item *srid) :Item_geometry_func(a, srid) {} - const char *func_name() const { return "geometryfromtext"; } + const char *func_name() const { return "st_geometryfromtext"; } String *val_str(String *); }; @@ -53,7 +57,7 @@ class Item_func_geometry_from_wkb: public Item_geometry_func public: Item_func_geometry_from_wkb(Item *a): Item_geometry_func(a) {} Item_func_geometry_from_wkb(Item *a, Item *srid): Item_geometry_func(a, srid) {} - const char *func_name() const { return "geometryfromwkb"; } + const char *func_name() const { return "st_geometryfromwkb"; } String *val_str(String *); }; @@ -61,7 +65,7 @@ class Item_func_as_wkt: public Item_str_ascii_func { public: Item_func_as_wkt(Item *a): Item_str_ascii_func(a) {} - const char *func_name() const { return "astext"; } + const char *func_name() const { return "st_astext"; } String *val_str_ascii(String *); void fix_length_and_dec(); }; @@ -70,7 +74,7 @@ class Item_func_as_wkb: public Item_geometry_func { public: Item_func_as_wkb(Item *a): Item_geometry_func(a) {} - const char *func_name() const { return "aswkb"; } + const char *func_name() const { return "st_aswkb"; } String *val_str(String *); enum_field_types field_type() const { return MYSQL_TYPE_BLOB; } }; @@ -80,7 +84,7 @@ class Item_func_geometry_type: public Item_str_ascii_func public: Item_func_geometry_type(Item *a): Item_str_ascii_func(a) {} String *val_str_ascii(String *); - const char *func_name() const { return "geometrytype"; } + const char *func_name() const { return "st_geometrytype"; } void fix_length_and_dec() { // "GeometryCollection" is the longest @@ -93,7 +97,7 @@ class Item_func_centroid: public Item_geometry_func { public: Item_func_centroid(Item *a): Item_geometry_func(a) {} - const char *func_name() const { return "centroid"; } + const char *func_name() const { return "st_centroid"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; }; @@ -102,7 +106,7 @@ class Item_func_envelope: public Item_geometry_func { public: Item_func_envelope(Item *a): Item_geometry_func(a) {} - const char *func_name() const { return "envelope"; } + const char *func_name() const { return "st_envelope"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; }; @@ -112,7 +116,7 @@ class Item_func_point: public Item_geometry_func public: Item_func_point(Item *a, Item *b): Item_geometry_func(a, b) {} Item_func_point(Item *a, Item *b, Item *srid): Item_geometry_func(a, b, srid) {} - const char *func_name() const { return "point"; } + const char *func_name() const { return "st_point"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; }; @@ -128,11 +132,11 @@ public: switch (decomp_func) { case SP_STARTPOINT: - return "startpoint"; + return "st_startpoint"; case SP_ENDPOINT: - return "endpoint"; + return "st_endpoint"; case SP_EXTERIORRING: - return "exteriorring"; + return "st_exteriorring"; default: DBUG_ASSERT(0); // Should never happened return "spatial_decomp_unknown"; @@ -152,11 +156,11 @@ public: switch (decomp_func_n) { case SP_POINTN: - return "pointn"; + return "st_pointn"; case SP_GEOMETRYN: - return "geometryn"; + return "st_geometryn"; case SP_INTERIORRINGN: - return "interiorringn"; + return "st_interiorringn"; default: DBUG_ASSERT(0); // Should never happened return "spatial_decomp_n_unknown"; @@ -195,57 +199,53 @@ public: } } - const char *func_name() const { return "multipoint"; } + const char *func_name() const { return "st_multipoint"; } }; + /* Spatial relations */ -class Item_func_spatial_rel: public Item_bool_func2 +class Item_func_spatial_mbr_rel: public Item_bool_func2 { enum Functype spatial_rel; public: - Item_func_spatial_rel(Item *a,Item *b, enum Functype sp_rel) : + Item_func_spatial_mbr_rel(Item *a,Item *b, enum Functype sp_rel) : Item_bool_func2(a,b) { spatial_rel = sp_rel; } longlong val_int(); enum Functype functype() const { - switch (spatial_rel) { - case SP_CONTAINS_FUNC: - return SP_WITHIN_FUNC; - case SP_WITHIN_FUNC: - return SP_CONTAINS_FUNC; - default: - return spatial_rel; - } + return spatial_rel; } enum Functype rev_functype() const { return spatial_rel; } - const char *func_name() const - { - switch (spatial_rel) { - case SP_CONTAINS_FUNC: - return "contains"; - case SP_WITHIN_FUNC: - return "within"; - case SP_EQUALS_FUNC: - return "equals"; - case SP_DISJOINT_FUNC: - return "disjoint"; - case SP_INTERSECTS_FUNC: - return "intersects"; - case SP_TOUCHES_FUNC: - return "touches"; - case SP_CROSSES_FUNC: - return "crosses"; - case SP_OVERLAPS_FUNC: - return "overlaps"; - default: - DBUG_ASSERT(0); // Should never happened - return "sp_unknown"; - } + const char *func_name() const; + virtual inline void print(String *str, enum_query_type query_type) + { + Item_func::print(str, query_type); } + void fix_length_and_dec() { maybe_null= 1; } + bool is_null() { (void) val_int(); return null_value; } +}; + +class Item_func_spatial_rel: public Item_bool_func2 +{ + enum Functype spatial_rel; + Gcalc_heap collector; + Gcalc_scan_iterator scan_it; + Gcalc_function func; + String tmp_value1,tmp_value2; +public: + Item_func_spatial_rel(Item *a,Item *b, enum Functype sp_rel); + virtual ~Item_func_spatial_rel(); + longlong val_int(); + enum Functype functype() const + { + return spatial_rel; + } + enum Functype rev_functype() const { return spatial_rel; } + const char *func_name() const; virtual inline void print(String *str, enum_query_type query_type) { Item_func::print(str, query_type); @@ -255,23 +255,107 @@ public: bool is_null() { (void) val_int(); return null_value; } }; + +/* + Spatial operations +*/ + +class Item_func_spatial_operation: public Item_geometry_func +{ +public: + Gcalc_function::op_type spatial_op; + Gcalc_heap collector; + Gcalc_function func; + + Gcalc_result_receiver res_receiver; + Gcalc_operation_reducer operation; + String tmp_value1,tmp_value2; +public: + Item_func_spatial_operation(Item *a,Item *b, Gcalc_function::op_type sp_op) : + Item_geometry_func(a, b), spatial_op(sp_op) + {} + virtual ~Item_func_spatial_operation(); + String *val_str(String *); + const char *func_name() const; + virtual inline void print(String *str, enum_query_type query_type) + { + Item_func::print(str, query_type); + } +}; + + +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(); + int m_nshapes; + Gcalc_function::op_type buffer_op; + int last_shape_pos; + bool skip_line; + + public: + Transporter(Gcalc_function *fn, Gcalc_heap *heap, double d) : + Gcalc_operation_transporter(fn, heap), m_npoints(0), m_d(d), + m_nshapes(0), buffer_op((d > 0.0) ? Gcalc_function::op_union : + Gcalc_function::op_difference), + skip_line(FALSE) + {} + 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); + }; + Gcalc_heap collector; + Gcalc_function func; + + Gcalc_result_receiver res_receiver; + Gcalc_operation_reducer operation; + String tmp_value; + +public: + Item_func_buffer(Item *obj, Item *distance): + Item_geometry_func(obj, distance) {} + const char *func_name() const { return "st_buffer"; } + String *val_str(String *); +}; + + class Item_func_isempty: public Item_bool_func { public: Item_func_isempty(Item *a): Item_bool_func(a) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } - const char *func_name() const { return "isempty"; } + const char *func_name() const { return "st_isempty"; } void fix_length_and_dec() { maybe_null= 1; } }; class Item_func_issimple: public Item_bool_func { + Gcalc_heap collector; + Gcalc_function func; + Gcalc_scan_iterator scan_it; + String tmp; public: Item_func_issimple(Item *a): Item_bool_func(a) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } - const char *func_name() const { return "issimple"; } + const char *func_name() const { return "st_issimple"; } void fix_length_and_dec() { maybe_null= 1; } }; @@ -281,7 +365,7 @@ public: Item_func_isclosed(Item *a): Item_bool_func(a) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } - const char *func_name() const { return "isclosed"; } + const char *func_name() const { return "st_isclosed"; } void fix_length_and_dec() { maybe_null= 1; } }; @@ -291,7 +375,7 @@ class Item_func_dimension: public Item_int_func public: Item_func_dimension(Item *a): Item_int_func(a) {} longlong val_int(); - const char *func_name() const { return "dimension"; } + const char *func_name() const { return "st_dimension"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -301,7 +385,7 @@ class Item_func_x: public Item_real_func public: Item_func_x(Item *a): Item_real_func(a) {} double val_real(); - const char *func_name() const { return "x"; } + const char *func_name() const { return "st_x"; } void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); @@ -316,7 +400,7 @@ class Item_func_y: public Item_real_func public: Item_func_y(Item *a): Item_real_func(a) {} double val_real(); - const char *func_name() const { return "y"; } + const char *func_name() const { return "st_y"; } void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); @@ -331,7 +415,7 @@ class Item_func_numgeometries: public Item_int_func public: Item_func_numgeometries(Item *a): Item_int_func(a) {} longlong val_int(); - const char *func_name() const { return "numgeometries"; } + const char *func_name() const { return "st_numgeometries"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -342,7 +426,7 @@ class Item_func_numinteriorring: public Item_int_func public: Item_func_numinteriorring(Item *a): Item_int_func(a) {} longlong val_int(); - const char *func_name() const { return "numinteriorrings"; } + const char *func_name() const { return "st_numinteriorrings"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -353,7 +437,7 @@ class Item_func_numpoints: public Item_int_func public: Item_func_numpoints(Item *a): Item_int_func(a) {} longlong val_int(); - const char *func_name() const { return "numpoints"; } + const char *func_name() const { return "st_numpoints"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -364,7 +448,7 @@ class Item_func_area: public Item_real_func public: Item_func_area(Item *a): Item_real_func(a) {} double val_real(); - const char *func_name() const { return "area"; } + const char *func_name() const { return "st_area"; } void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); @@ -379,7 +463,7 @@ class Item_func_glength: public Item_real_func public: Item_func_glength(Item *a): Item_real_func(a) {} double val_real(); - const char *func_name() const { return "glength"; } + const char *func_name() const { return "st_length"; } void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); @@ -398,12 +482,25 @@ public: void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; + +class Item_func_distance: public Item_real_func +{ + String tmp_value1; + String tmp_value2; + Gcalc_heap collector; + Gcalc_function func; + Gcalc_scan_iterator scan_it; +public: + Item_func_distance(Item *a, Item *b): Item_real_func(a, b) {} + double val_real(); + const char *func_name() const { return "st_distance"; } +}; + #define GEOM_NEW(thd, obj_constructor) new (thd->mem_root) obj_constructor #else /*HAVE_SPATIAL*/ #define GEOM_NEW(thd, obj_constructor) NULL -#endif - +#endif /*HAVE_SPATIAL*/ #endif /* ITEM_GEOFUNC_INCLUDED */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 2a922541f72..c6e1bf584e3 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1236,13 +1236,13 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, if (result) { + result->keys_map= result_keys; if (result_keys.is_clear_all()) result->type= SEL_TREE::ALWAYS; if ((result->type == SEL_TREE::MAYBE) || (result->type == SEL_TREE::ALWAYS)) return 1; /* SEL_TREE::IMPOSSIBLE is impossible here */ - result->keys_map= result_keys; *or_tree= result; was_ored= TRUE; } @@ -8656,8 +8656,8 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) continue; SEL_ARG *next=key_and(param, e1->next_key_part, e2->next_key_part, clone_flag); - e1->increment_use_count(1); - e2->increment_use_count(1); + e1->incr_refs(); + e2->incr_refs(); if (!next || next->type != SEL_ARG::IMPOSSIBLE) { SEL_ARG *new_arg= e1->clone_and(e2); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 0aa6fb7e913..02a57a9e7df 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -446,7 +446,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) subquery execution strategies based on optimizer switches and syntactic properties. */ - if (in_subs) + if (in_subs && !in_subs->has_strategy()) { if (is_materialization_applicable(thd, in_subs, select_lex)) { @@ -3920,8 +3920,8 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, if (j != join->const_tables && js_tab->use_quick != 2 && j <= no_jbuf_after && ((js_tab->type == JT_ALL && join_cache_level != 0) || - (join_cache_level > 2 && (tab->type == JT_REF || - tab->type == JT_EQ_REF)))) + (join_cache_level > 2 && (js_tab->type == JT_REF || + js_tab->type == JT_EQ_REF)))) { /* Looks like we'll be using join buffer */ first_table= join->const_tables; diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 5a7416fe929..08af798948d 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -170,7 +170,7 @@ public: PREV_BITS(ulonglong, s->emb_sj_nest->sj_in_exprs) && // (2) (PREV_BITS(key_part_map, max_loose_keypart+1) & // (3) (found_part | loose_scan_keyparts)) == // (3) - (found_part | loose_scan_keyparts) && // (3) + PREV_BITS(key_part_map, max_loose_keypart+1) && // (3) !key_uses_partial_cols(s->table, key)) { /* Ok, can use the strategy */ diff --git a/sql/plistsort.c b/sql/plistsort.c new file mode 100644 index 00000000000..71d287e7b45 --- /dev/null +++ b/sql/plistsort.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* +things to define before including the file: + +#define LS_LIST_ITEM ListItem +#define LS_COMPARE_FUNC_DECL compare_func var_name, +#define LS_COMPARE_FUNC_CALL(list_el1, list_el2) (*var_name)(list_el1, list_el2) +#define LS_NEXT(A) (A)->next +#define LS_SET_NEXT(A,val) (A)->next= val +#define LS_P_NEXT(A) &(A)->next +#define LS_NAME plistsort +#define LS_SCOPE static +#define LS_STRUCT_NAME ls_struct_name +*/ + +typedef struct LS_STRUCT_NAME +{ + LS_LIST_ITEM *list1; + int list_len; + int return_point; +} LS_STRUCT_NAME; + +LS_SCOPE LS_LIST_ITEM* LS_NAME(LS_COMPARE_FUNC_DECL LS_LIST_ITEM *list, int list_len) +{ + LS_LIST_ITEM *list_end; + LS_LIST_ITEM *sorted_list; + + struct LS_STRUCT_NAME stack[63], *sp= stack; + + if (list_len < 2) + return list; + + sp->list_len= list_len; + sp->return_point= 2; + +recursion_point: + + if (sp->list_len < 4) + { + LS_LIST_ITEM *e1, *e2; + sorted_list= list; + e1= LS_NEXT(sorted_list); + list_end= LS_NEXT(e1); + if (LS_COMPARE_FUNC_CALL(sorted_list, e1)) + { + sorted_list= e1; + e1= list; + } + if (sp->list_len == 2) + { + LS_SET_NEXT(sorted_list, e1); + LS_SET_NEXT(e1, NULL); + goto exit_point; + } + e2= list_end; + list_end= LS_NEXT(e2); + if (LS_COMPARE_FUNC_CALL(e1, e2)) + { + { + LS_LIST_ITEM *tmp_e= e1; + e1= e2; + e2= tmp_e; + } + if (LS_COMPARE_FUNC_CALL(sorted_list, e1)) + { + LS_LIST_ITEM *tmp_e= sorted_list; + sorted_list= e1; + e1= tmp_e; + } + } + + LS_SET_NEXT(sorted_list, e1); + LS_SET_NEXT(e1, e2); + LS_SET_NEXT(e2, NULL); + goto exit_point; + } + + { + register struct LS_STRUCT_NAME *sp0= sp++; + sp->list_len= sp0->list_len >> 1; + sp0->list_len-= sp->list_len; + sp->return_point= 0; + } + goto recursion_point; +return_point0: + sp->list1= sorted_list; + { + register struct LS_STRUCT_NAME *sp0= sp++; + list= list_end; + sp->list_len= sp0->list_len; + sp->return_point= 1; + } + goto recursion_point; +return_point1: + { + register LS_LIST_ITEM **hook= &sorted_list; + register LS_LIST_ITEM *list1= sp->list1; + register LS_LIST_ITEM *list2= sorted_list; + + if (LS_COMPARE_FUNC_CALL(list1, list2)) + { + LS_LIST_ITEM *tmp_e= list2; + list2= list1; + list1= tmp_e; + } + for (;;) + { + *hook= list1; + do + { + if (!(list1= *(hook= LS_P_NEXT(list1)))) + { + *hook= list2; + goto exit_point; + } + } while (LS_COMPARE_FUNC_CALL(list2, list1)); + + *hook= list2; + do + { + if (!(list2= *(hook= LS_P_NEXT(list2)))) + { + *hook= list1; + goto exit_point; + } + } while (LS_COMPARE_FUNC_CALL(list1, list2)); + } + } + +exit_point: + switch ((sp--)->return_point) + { + case 0: goto return_point0; + case 1: goto return_point1; + default:; + } + + return sorted_list; +} + + +#undef LS_LIST_ITEM +#undef LS_NEXT +#undef LS_SET_NEXT +#undef LS_P_NEXT +#undef LS_NAME +#undef LS_STRUCT_NAME +#undef LS_SCOPE +#undef LS_COMPARE_FUNC_DECL +#undef LS_COMPARE_FUNC_CALL + diff --git a/sql/spatial.cc b/sql/spatial.cc index ee701d82657..4dfc20716fc 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2002, 2011, Oracle and/or its affiliates. + Copyright (c) 2011, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,6 +23,27 @@ #ifdef HAVE_SPATIAL +/* + exponential notation : + 1 sign + 1 number before the decimal point + 1 decimal point + 14 number of significant digits (see String::qs_append(double)) + 1 'e' sign + 1 exponent sign + 3 exponent digits + == + 22 + + "f" notation : + 1 optional 0 + 1 sign + 14 number significant digits (see String::qs_append(double) ) + 1 decimal point + == + 17 +*/ + #define MAX_DIGITS_IN_DOUBLE MY_GCVT_MAX_FIELD_WIDTH /***************************** Gis_class_info *******************************/ @@ -162,6 +184,7 @@ Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer, { LEX_STRING name; Class_info *ci; + char next_sym; if (trs->get_next_word(&name)) { @@ -174,9 +197,13 @@ Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer, Geometry *result= (*ci->m_create_func)(buffer->data); wkt->q_append((char) wkb_ndr); wkt->q_append((uint32) result->get_class_info()->m_type_id); - if (trs->check_next_symbol('(') || + if (!(next_sym= trs->next_symbol())) + return NULL; + if (!(next_sym= trs->next_symbol())) + return NULL; + if ((next_sym == '(' && trs->check_next_symbol('(')) || result->init_from_wkt(trs, wkt) || - trs->check_next_symbol(')')) + (next_sym == '(' && trs->check_next_symbol(')'))) return NULL; if (init_stream) { @@ -187,6 +214,22 @@ Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer, } +int Geometry::as_wkt(String *wkt, const char **end) +{ + uint32 len= (uint) get_class_info()->m_name.length; + if (wkt->reserve(len + 2, 512)) + return 1; + wkt->qs_append(get_class_info()->m_name.str, len); + if (get_class_info() != &geometrycollection_class) + wkt->qs_append('('); + if (get_data_as_wkt(wkt, end)) + return 1; + if (get_class_info() != &geometrycollection_class) + wkt->qs_append(')'); + return 0; +} + + static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo) { double res; @@ -248,12 +291,37 @@ Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer, } +int Geometry::create_from_opresult(Geometry_buffer *g_buf, + String *res, Gcalc_result_receiver &rr) +{ + uint32 geom_type= rr.get_result_typeid(); + Geometry *obj= create_by_typeid(g_buf, geom_type); + + if (!obj || res->reserve(WKB_HEADER_SIZE, 512)) + return 1; + + res->q_append((char) wkb_ndr); + res->q_append(geom_type); + return obj->init_from_opresult(res, rr.result(), rr.length()); +} + + bool Geometry::envelope(String *result) const { MBR mbr; const char *end; - if (get_mbr(&mbr, &end) || result->reserve(1+4*3+SIZEOF_STORED_DOUBLE*10)) + if (get_mbr(&mbr, &end)) + { + /* Empty geometry */ + if (result->reserve(1 + 4*2)) + return 1; + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_geometrycollection); + result->q_append((uint32) 0); + return 0; + } + if (result->reserve(1 + 4 * 3 + SIZEOF_STORED_DOUBLE * 10)) return 1; result->q_append((char) wkb_ndr); @@ -290,13 +358,13 @@ bool Geometry::envelope(String *result) const bool Geometry::create_point(String *result, const char *data) const { - if (no_data(data, SIZEOF_STORED_DOUBLE * 2) || - result->reserve(1 + 4 + SIZEOF_STORED_DOUBLE * 2)) + if (no_data(data, POINT_DATA_SIZE) || + result->reserve(1 + 4 + POINT_DATA_SIZE)) return 1; result->q_append((char) wkb_ndr); result->q_append((uint32) wkb_point); /* Copy two double in same format */ - result->q_append(data, SIZEOF_STORED_DOUBLE*2); + result->q_append(data, POINT_DATA_SIZE); return 0; } @@ -316,7 +384,7 @@ bool Geometry::create_point(String *result, const char *data) const bool Geometry::create_point(String *result, double x, double y) const { - if (result->reserve(1 + 4 + SIZEOF_STORED_DOUBLE * 2)) + if (result->reserve(1 + 4 + POINT_DATA_SIZE)) return 1; result->q_append((char) wkb_ndr); @@ -348,7 +416,7 @@ const char *Geometry::append_points(String *txt, uint32 n_points, double x,y; data+= offset; get_point(&x, &y, data); - data+= SIZEOF_STORED_DOUBLE * 2; + data+= POINT_DATA_SIZE; txt->qs_append(x); txt->qs_append(' '); txt->qs_append(y); @@ -383,7 +451,7 @@ const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data, points= uint4korr(data); data+= 4; - if (no_data(data, (SIZEOF_STORED_DOUBLE * 2 + offset) * points)) + if (no_data(data, (POINT_DATA_SIZE + offset) * points)) return 0; /* Calculate MBR for points */ @@ -391,7 +459,7 @@ const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data, { data+= offset; mbr->add_xy(data, data + SIZEOF_STORED_DOUBLE); - data+= SIZEOF_STORED_DOUBLE * 2; + data+= POINT_DATA_SIZE; } return data; } @@ -409,7 +477,7 @@ bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb) { double x, y; if (trs->get_next_number(&x) || trs->get_next_number(&y) || - wkb->reserve(SIZEOF_STORED_DOUBLE * 2)) + wkb->reserve(POINT_DATA_SIZE)) return 1; wkb->q_append(x); wkb->q_append(y); @@ -456,6 +524,31 @@ bool Gis_point::get_mbr(MBR *mbr, const char **end) const return 0; } + +int Gis_point::area(double *ar, const char **end) const +{ + *ar= 0; + *end= m_data+ POINT_DATA_SIZE; + return 0; +} + + +int Gis_point::geom_length(double *len, const char **end) const +{ + *len= 0; + *end= m_data+ POINT_DATA_SIZE; + return 0; +} + + +int Gis_point::store_shapes(Gcalc_shape_transporter *trn) const +{ + double x, y; + + return get_xy(&x, &y) || trn->single_point(x, y); +} + + const Geometry::Class_info *Gis_point::get_class_info() const { return &point_class; @@ -507,12 +600,12 @@ uint Gis_line_string::init_from_wkb(const char *wkb, uint len, const char *wkb_end; Gis_point p; - if (len < 4) + if (len < 4 || + (n_points= wkb_get_uint(wkb, bo))<1) return 0; - n_points= wkb_get_uint(wkb, bo); proper_length= 4 + n_points * POINT_DATA_SIZE; - if (!n_points || len < proper_length || res->reserve(proper_length)) + if (len < proper_length || res->reserve(proper_length)) return 0; res->q_append(n_points); @@ -538,7 +631,7 @@ bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const data += 4; if (n_points < 1 || - no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points) || + no_data(data, POINT_DATA_SIZE * n_points) || txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) return 1; @@ -546,7 +639,7 @@ bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const { double x, y; get_point(&x, &y, data); - data+= SIZEOF_STORED_DOUBLE * 2; + data+= POINT_DATA_SIZE; txt->qs_append(x); txt->qs_append(' '); txt->qs_append(y); @@ -564,7 +657,7 @@ bool Gis_line_string::get_mbr(MBR *mbr, const char **end) const } -int Gis_line_string::geom_length(double *len) const +int Gis_line_string::geom_length(double *len, const char **end) const { uint32 n_points; double prev_x, prev_y; @@ -575,21 +668,35 @@ int Gis_line_string::geom_length(double *len) const return 1; n_points= uint4korr(data); data+= 4; - if (n_points < 1 || no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) + if (n_points < 1 || no_data(data, POINT_DATA_SIZE * n_points)) return 1; get_point(&prev_x, &prev_y, data); - data+= SIZEOF_STORED_DOUBLE*2; - + data+= POINT_DATA_SIZE; while (--n_points) { double x, y; get_point(&x, &y, data); - data+= SIZEOF_STORED_DOUBLE * 2; + data+= POINT_DATA_SIZE; *len+= sqrt(pow(prev_x-x,2)+pow(prev_y-y,2)); prev_x= x; prev_y= y; } + *end= data; + return 0; +} + + +int Gis_line_string::area(double *ar, const char **end) const +{ + uint32 n_points; + *ar= 0.0; + + /* read number of points */ + if (no_data(m_data, 4)) + return 1; + n_points= uint4korr(m_data); + *end= m_data + 4 + POINT_DATA_SIZE * n_points; return 0; } @@ -609,14 +716,14 @@ int Gis_line_string::is_closed(int *closed) const return 0; } data+= 4; - if (no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) + if (no_data(data, POINT_DATA_SIZE * n_points)) return 1; /* Get first point */ get_point(&x1, &y1, data); /* get last point */ - data+= SIZEOF_STORED_DOUBLE*2 + (n_points-2)*POINT_DATA_SIZE; + data+= POINT_DATA_SIZE + (n_points-2)*POINT_DATA_SIZE; get_point(&x2, &y2, data); *closed= (x1==x2) && (y1==y2); @@ -660,6 +767,41 @@ int Gis_line_string::point_n(uint32 num, String *result) const return create_point(result, m_data + 4 + (num - 1) * POINT_DATA_SIZE); } + +int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn) const +{ + uint32 n_points; + double x, y; + double UNINIT_VAR(prev_x), UNINIT_VAR(prev_y); + int first_point= 1; + const char *data= m_data; + + if (no_data(m_data, 4)) + return 1; + n_points= uint4korr(data); + data+= 4; + if (n_points < 1 || no_data(data, POINT_DATA_SIZE * n_points)) + return 1; + + trn->start_line(); + + while (n_points--) + { + get_point(&x, &y, data); + data+= POINT_DATA_SIZE; + if (!first_point && x == prev_x && y == prev_y) + continue; + if (trn->add_point(x, y)) + return 1; + first_point= 0; + prev_x= x; + prev_y= y; + } + + return trn->complete_line(); +} + + const Geometry::Class_info *Gis_line_string::get_class_info() const { return &linestring_class; @@ -721,6 +863,52 @@ bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_polygon::init_from_opresult(String *bin, + const char *opres, uint res_len) +{ + const char *opres_orig= opres; + uint32 position= bin->length(); + uint32 poly_shapes= 0; + + if (bin->reserve(4, 512)) + return 0; + bin->q_append(poly_shapes); + + while (opres_orig + res_len > opres) + { + uint32 n_points, proper_length; + const char *op_end, *p1_position; + Gis_point p; + Gcalc_function::shape_type st; + + st= (Gcalc_function::shape_type) uint4korr(opres); + if (poly_shapes && st != Gcalc_function::shape_hole) + break; + poly_shapes++; + n_points= uint4korr(opres + 4) + 1; /* skip shape type id */ + proper_length= 4 + n_points * POINT_DATA_SIZE; + + if (bin->reserve(proper_length, 512)) + return 0; + + bin->q_append(n_points); + op_end= opres + 8 + (n_points-1) * 8 * 2; + p1_position= (opres+= 8); + for (; opres<op_end; opres+= POINT_DATA_SIZE) + { + if (!p.init_from_wkb(opres, POINT_DATA_SIZE, wkb_ndr, bin)) + return 0; + } + if (!p.init_from_wkb(p1_position, POINT_DATA_SIZE, wkb_ndr, bin)) + return 0; + } + + bin->write_at_position(position, poly_shapes); + + return (uint) (opres - opres_orig); +} + + uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res) { @@ -730,9 +918,7 @@ uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, if (len < 4) return 0; - if (!(n_linear_rings= wkb_get_uint(wkb, bo))) - return 0; - + n_linear_rings= wkb_get_uint(wkb, bo); if (res->reserve(4, 512)) return 0; wkb+= 4; @@ -778,7 +964,7 @@ bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const return 1; n_points= uint4korr(data); data+= 4; - if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points) || + if (no_data(data, POINT_DATA_SIZE * n_points) || txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; txt->qs_append('('); @@ -832,16 +1018,16 @@ int Gis_polygon::area(double *ar, const char **end_of_data) const if (no_data(data, 4)) return 1; n_points= uint4korr(data); - if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points)) + if (no_data(data, POINT_DATA_SIZE * n_points)) return 1; get_point(&prev_x, &prev_y, data+4); - data+= (4+SIZEOF_STORED_DOUBLE*2); + data+= (4+POINT_DATA_SIZE); while (--n_points) // One point is already read { double x, y; get_point(&x, &y, data); - data+= (SIZEOF_STORED_DOUBLE*2); + data+= POINT_DATA_SIZE; lr_area+= (prev_x + x)* (prev_y - y); prev_x= x; prev_y= y; @@ -868,7 +1054,7 @@ int Gis_polygon::exterior_ring(String *result) const n_points= uint4korr(data); data+= 4; length= n_points * POINT_DATA_SIZE; - if (no_data(data, length) || result->reserve(1+4+4+ length)) + if (no_data(data, length) || result->reserve(1 + 4 + 4 + length)) return 1; result->q_append((char) wkb_ndr); @@ -914,7 +1100,7 @@ int Gis_polygon::interior_ring_n(uint32 num, String *result) const n_points= uint4korr(data); points_size= n_points * POINT_DATA_SIZE; data+= 4; - if (no_data(data, points_size) || result->reserve(1+4+4+ points_size)) + if (no_data(data, points_size) || result->reserve(1 + 4 + 4 + points_size)) return 1; result->q_append((char) wkb_ndr); @@ -953,16 +1139,16 @@ int Gis_polygon::centroid_xy(double *x, double *y) const return 1; org_n_points= n_points= uint4korr(data); data+= 4; - if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points)) + if (no_data(data, POINT_DATA_SIZE * n_points)) return 1; get_point(&prev_x, &prev_y, data); - data+= (SIZEOF_STORED_DOUBLE*2); + data+= POINT_DATA_SIZE; while (--n_points) // One point is already read { double tmp_x, tmp_y; get_point(&tmp_x, &tmp_y, data); - data+= (SIZEOF_STORED_DOUBLE*2); + data+= POINT_DATA_SIZE; cur_area+= (prev_x + tmp_x) * (prev_y - tmp_y); cur_cx+= tmp_x; cur_cy+= tmp_y; @@ -1002,6 +1188,74 @@ int Gis_polygon::centroid(String *result) const return create_point(result, x, y); } + +int Gis_polygon::store_shapes(Gcalc_shape_transporter *trn) const +{ + uint32 n_linear_rings; + const char *data= m_data; + double first_x, first_y; + double prev_x, prev_y; + int was_equal_first= 0; + + if (trn->start_poly()) + return 1; + + if (no_data(data, 4)) + return 1; + n_linear_rings= uint4korr(data); + data+= 4; + + while (n_linear_rings--) + { + uint32 n_points; + + if (no_data(data, 4)) + return 1; + n_points= uint4korr(data); + data+= 4; + if (!n_points || no_data(data, POINT_DATA_SIZE * n_points)) + return 1; + + trn->start_ring(); + get_point(&first_x, &first_y, data); + data+= POINT_DATA_SIZE; + n_points--; + prev_x= first_x; + prev_y= first_y; + if (trn->add_point(first_x, first_y)) + return 1; + while (--n_points) + { + double x, y; + get_point(&x, &y, data); + data+= POINT_DATA_SIZE; + if (x == prev_x && y == prev_y) + continue; + prev_x= x; + prev_y= y; + if (was_equal_first) + { + if (trn->add_point(first_x, first_y)) + return 1; + was_equal_first= 0; + } + if (x == first_x && y == first_y) + { + was_equal_first= 1; + continue; + } + if (trn->add_point(x, y)) + return 1; + } + data+= POINT_DATA_SIZE; + trn->complete_ring(); + } + + trn->complete_poly(); + return 0; +} + + const Geometry::Class_info *Gis_polygon::get_class_info() const { return &polygon_class; @@ -1030,7 +1284,7 @@ bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb) for (;;) { - if (wkb->reserve(1+4, 512)) + if (wkb->reserve(1 + 4, 512)) return 1; wkb->q_append((char) wkb_ndr); wkb->q_append((uint32) wkb_point); @@ -1045,6 +1299,32 @@ bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_multi_point::init_from_opresult(String *bin, + const char *opres, uint res_len) +{ + uint bin_size, n_points; + Gis_point p; + const char *opres_end; + + n_points= res_len/(4+8*2); + bin_size= n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE) + 4; + + if (bin->reserve(bin_size, 512)) + return 0; + + bin->q_append(n_points); + opres_end= opres + res_len; + for (; opres < opres_end; opres+= (4 + 8*2)) + { + bin->q_append((char)wkb_ndr); + bin->q_append((uint32)wkb_point); + if (!p.init_from_wkb(opres + 4, POINT_DATA_SIZE, wkb_ndr, bin)) + return 0; + } + return res_len; +} + + uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res) { @@ -1083,7 +1363,7 @@ bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const n_points= uint4korr(m_data); if (no_data(m_data+4, - n_points * (SIZEOF_STORED_DOUBLE * 2 + WKB_HEADER_SIZE)) || + n_points * (POINT_DATA_SIZE + WKB_HEADER_SIZE)) || txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; *end= append_points(txt, n_points, m_data+4, WKB_HEADER_SIZE); @@ -1124,6 +1404,35 @@ int Gis_multi_point::geometry_n(uint32 num, String *result) const return 0; } + +int Gis_multi_point::store_shapes(Gcalc_shape_transporter *trn) const +{ + uint32 n_points; + Gis_point pt; + const char *data= m_data; + + if (no_data(data, 4)) + return 1; + n_points= uint4korr(data); + data+= 4; + + if (trn->start_collection(n_points)) + return 1; + + while (n_points--) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + data+= WKB_HEADER_SIZE; + pt.set_data_ptr(data, (uint32) (m_data_end - data)); + if (pt.store_shapes(trn)) + return 1; + data+= pt.get_data_size(); + } + return 0; +} + + const Geometry::Class_info *Gis_multi_point::get_class_info() const { return &multipoint_class; @@ -1166,10 +1475,9 @@ bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) { Gis_line_string ls; - if (wkb->reserve(1+4, 512)) + if (wkb->reserve(1 + 4, 512)) return 1; - wkb->q_append((char) wkb_ndr); - wkb->q_append((uint32) wkb_linestring); + wkb->q_append((char) wkb_ndr); wkb->q_append((uint32) wkb_linestring); if (trs->check_next_symbol('(') || ls.init_from_wkt(trs, wkb) || @@ -1184,15 +1492,48 @@ bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_multi_line_string::init_from_opresult(String *bin, + const char *opres, uint res_len) +{ + const char *opres_orig= opres; + int ns_pos= bin->length(); + uint n_linestring= 0; + + if (bin->reserve(4, 512)) + return 0; + bin->q_append(n_linestring); + + while (res_len) + { + Gis_line_string ls; + int ls_len; + + if (bin->reserve(WKB_HEADER_SIZE, 512)) + return 0; + + bin->q_append((char) wkb_ndr); + bin->q_append((uint32) wkb_linestring); + + if (!(ls_len= ls.init_from_opresult(bin, opres, res_len))) + return 0; + opres+= ls_len; + res_len-= ls_len; + n_linestring++; + } + bin->write_at_position(ns_pos, n_linestring); + return (uint) (opres - opres_orig); +} + + uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res) { uint32 n_line_strings; const char *wkb_orig= wkb; - if (len < 4) + if (len < 4 || + (n_line_strings= wkb_get_uint(wkb, bo))< 1) return 0; - n_line_strings= wkb_get_uint(wkb, bo); if (res->reserve(4, 512)) return 0; @@ -1240,7 +1581,7 @@ bool Gis_multi_line_string::get_data_as_wkt(String *txt, return 1; n_points= uint4korr(data + WKB_HEADER_SIZE); data+= WKB_HEADER_SIZE + 4; - if (no_data(data, n_points * (SIZEOF_STORED_DOUBLE*2)) || + if (no_data(data, n_points * POINT_DATA_SIZE) || txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; txt->qs_append('('); @@ -1311,10 +1652,11 @@ int Gis_multi_line_string::geometry_n(uint32 num, String *result) const } -int Gis_multi_line_string::geom_length(double *len) const +int Gis_multi_line_string::geom_length(double *len, const char **end) const { uint32 n_line_strings; const char *data= m_data; + const char *line_end; if (no_data(data, 4)) return 1; @@ -1328,7 +1670,7 @@ int Gis_multi_line_string::geom_length(double *len) const Gis_line_string ls; data+= WKB_HEADER_SIZE; ls.set_data_ptr(data, (uint32) (m_data_end - data)); - if (ls.geom_length(&ls_len)) + if (ls.geom_length(&ls_len, &line_end)) return 1; *len+= ls_len; /* @@ -1337,6 +1679,7 @@ int Gis_multi_line_string::geom_length(double *len) const */ data+= ls.get_data_size(); } + *end= data; return 0; } @@ -1370,6 +1713,35 @@ int Gis_multi_line_string::is_closed(int *closed) const return 0; } + +int Gis_multi_line_string::store_shapes(Gcalc_shape_transporter *trn) const +{ + uint32 n_lines; + Gis_line_string ls; + const char *data= m_data; + + if (no_data(data, 4)) + return 1; + n_lines= uint4korr(data); + data+= 4; + + if (trn->start_collection(n_lines)) + return 1; + + while (n_lines--) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + data+= WKB_HEADER_SIZE; + ls.set_data_ptr(data, (uint32) (m_data_end - data)); + if (ls.store_shapes(trn)) + return 1; + data+= ls.get_data_size(); + } + return 0; +} + + const Geometry::Class_info *Gis_multi_line_string::get_class_info() const { return &multilinestring_class; @@ -1420,7 +1792,7 @@ bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) for (;;) { - if (wkb->reserve(1+4, 512)) + if (wkb->reserve(1 + 4, 512)) return 1; wkb->q_append((char) wkb_ndr); wkb->q_append((uint32) wkb_polygon); @@ -1475,6 +1847,36 @@ uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len, } +uint Gis_multi_polygon::init_from_opresult(String *bin, + const char *opres, uint res_len) +{ + Gis_polygon p; + const char *opres_orig= opres; + uint p_len; + uint32 n_poly= 0; + uint32 np_pos= bin->length(); + + if (bin->reserve(4, 512)) + return 0; + + bin->q_append(n_poly); + while (res_len) + { + if (bin->reserve(1 + 4, 512)) + return 0; + bin->q_append((char)wkb_ndr); + bin->q_append((uint32)wkb_polygon); + if (!(p_len= p.init_from_opresult(bin, opres, res_len))) + return 0; + opres+= p_len; + res_len-= p_len; + n_poly++; + } + bin->write_at_position(np_pos, n_poly); + return opres - opres_orig; +} + + bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const { uint32 n_polygons; @@ -1501,7 +1903,7 @@ bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const return 1; uint32 n_points= uint4korr(data); data+= 4; - if (no_data(data, (SIZEOF_STORED_DOUBLE * 2) * n_points) || + if (no_data(data, POINT_DATA_SIZE * n_points) || txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points, 512)) return 1; @@ -1654,6 +2056,35 @@ int Gis_multi_polygon::centroid(String *result) const return create_point(result, res_cx, res_cy); } + +int Gis_multi_polygon::store_shapes(Gcalc_shape_transporter *trn) const +{ + uint32 n_polygons; + Gis_polygon p; + const char *data= m_data; + + if (no_data(data, 4)) + return 1; + n_polygons= uint4korr(data); + data+= 4; + + if (trn->start_collection(n_polygons)) + return 1; + + while (n_polygons--) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + data+= WKB_HEADER_SIZE; + p.set_data_ptr(data, (uint32) (m_data_end - data)); + if (p.store_shapes(trn)) + return 1; + data+= p.get_data_size(); + } + return 0; +} + + const Geometry::Class_info *Gis_multi_polygon::get_class_info() const { return &multipolygon_class; @@ -1700,24 +2131,41 @@ bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb) uint32 no_pos= wkb->length(); Geometry_buffer buffer; Geometry *g; + char next_sym; if (wkb->reserve(4, 512)) return 1; wkb->length(wkb->length()+4); // Reserve space for points - for (;;) + if (!(next_sym= trs->next_symbol())) + return 1; + + if (next_sym != ')') { - if (!(g= create_from_wkt(&buffer, trs, wkb))) + LEX_STRING next_word; + if (trs->lookup_next_word(&next_word)) return 1; - if (g->get_class_info()->m_type_id == wkb_geometrycollection) + if (next_word.length != 5 || + (my_strnncoll(&my_charset_latin1, + (const uchar*) "empty", 5, + (const uchar*) next_word.str, 5) != 0)) { - trs->set_error_msg("Unexpected GEOMETRYCOLLECTION"); - return 1; + for (;;) + { + if (!(g= create_from_wkt(&buffer, trs, wkb))) + return 1; + + if (g->get_class_info()->m_type_id == wkb_geometrycollection) + { + trs->set_error_msg("Unexpected GEOMETRYCOLLECTION"); + return 1; + } + n_objects++; + if (trs->skip_char(',')) // Didn't find ',' + break; + } } - n_objects++; - if (trs->skip_char(',')) // Didn't find ',' - break; } wkb->write_at_position(no_pos, n_objects); @@ -1725,6 +2173,50 @@ bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_geometry_collection::init_from_opresult(String *bin, + const char *opres, + uint res_len) +{ + const char *opres_orig= opres; + Geometry_buffer buffer; + Geometry *geom; + int g_len; + uint32 wkb_type; + int no_pos= bin->length(); + uint32 n_objects= 0; + + if (bin->reserve(4, 512)) + return 0; + bin->q_append(n_objects); + + while (res_len) + { + switch ((Gcalc_function::shape_type) uint4korr(opres)) + { + case Gcalc_function::shape_point: wkb_type= wkb_point; break; + case Gcalc_function::shape_line: wkb_type= wkb_linestring; break; + case Gcalc_function::shape_polygon: wkb_type= wkb_polygon; break; + default: wkb_type= 0; DBUG_ASSERT(FALSE); + }; + + if (bin->reserve(WKB_HEADER_SIZE, 512)) + return 0; + + bin->q_append((char) wkb_ndr); + bin->q_append(wkb_type); + + if (!(geom= create_by_typeid(&buffer, wkb_type)) || + !(g_len= geom->init_from_opresult(bin, opres, res_len))) + return 0; + opres+= g_len; + res_len-= g_len; + n_objects++; + } + bin->write_at_position(no_pos, n_objects); + return (uint) (opres - opres_orig); +} + + uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res) { @@ -1780,6 +2272,13 @@ bool Gis_geometry_collection::get_data_as_wkt(String *txt, n_objects= uint4korr(data); data+= 4; + if (n_objects == 0) + { + txt->append(STRING_WITH_LEN(" EMPTY"), 512); + goto exit; + } + + txt->qs_append('('); while (n_objects--) { uint32 wkb_type; @@ -1794,10 +2293,11 @@ bool Gis_geometry_collection::get_data_as_wkt(String *txt, geom->set_data_ptr(data, (uint) (m_data_end - data)); if (geom->as_wkt(txt, &data)) return 1; - if (txt->append(STRING_WITH_LEN(","), 512)) + if (n_objects && txt->append(STRING_WITH_LEN(","), 512)) return 1; } - txt->length(txt->length() - 1); + txt->qs_append(')'); +exit: *end= data; return 0; } @@ -1814,6 +2314,8 @@ bool Gis_geometry_collection::get_mbr(MBR *mbr, const char **end) const return 1; n_objects= uint4korr(data); data+= 4; + if (n_objects == 0) + return 1; while (n_objects--) { @@ -1835,6 +2337,82 @@ bool Gis_geometry_collection::get_mbr(MBR *mbr, const char **end) const } +int Gis_geometry_collection::area(double *ar, const char **end) const +{ + uint32 n_objects; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + double result; + + if (no_data(data, 4)) + return 1; + n_objects= uint4korr(data); + data+= 4; + if (n_objects == 0) + return 1; + + result= 0.0; + while (n_objects--) + { + uint32 wkb_type; + + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; + + if (!(geom= create_by_typeid(&buffer, wkb_type))) + return 1; + geom->set_data_ptr(data, (uint32) (m_data_end - data)); + if (geom->area(ar, &data)) + return 1; + result+= *ar; + } + *end= data; + *ar= result; + return 0; +} + + +int Gis_geometry_collection::geom_length(double *len, const char **end) const +{ + uint32 n_objects; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + double result; + + if (no_data(data, 4)) + return 1; + n_objects= uint4korr(data); + data+= 4; + if (n_objects == 0) + return 1; + + result= 0.0; + while (n_objects--) + { + uint32 wkb_type; + + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; + + if (!(geom= create_by_typeid(&buffer, wkb_type))) + return 1; + geom->set_data_ptr(data, (uint32) (m_data_end - data)); + if (geom->geom_length(len, &data)) + return 1; + result+= *len; + } + *end= data; + *len= result; + return 0; +} + + int Gis_geometry_collection::num_geometries(uint32 *num) const { if (no_data(m_data, 4)) @@ -1874,7 +2452,7 @@ int Gis_geometry_collection::geometry_n(uint32 num, String *result) const } while (--num); /* Copy found object to result */ - if (result->reserve(1+4+length)) + if (result->reserve(1 + 4 + length)) return 1; result->q_append((char) wkb_ndr); result->q_append((uint32) wkb_type); @@ -1935,6 +2513,48 @@ bool Gis_geometry_collection::dimension(uint32 *res_dim, const char **end) const return 0; } + +int Gis_geometry_collection::store_shapes(Gcalc_shape_transporter *trn) const +{ + uint32 n_objects; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + + if (no_data(data, 4)) + return 1; + n_objects= uint4korr(data); + data+= 4; + + if (!n_objects) + { + trn->empty_shape(); + return 0; + } + + if (trn->start_collection(n_objects)) + return 1; + + while (n_objects--) + { + uint32 wkb_type; + + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; + if (!(geom= create_by_typeid(&buffer, wkb_type))) + return 1; + geom->set_data_ptr(data, (uint32) (m_data_end - data)); + if (geom->store_shapes(trn)) + return 1; + + data+= geom->get_data_size(); + } + return 0; +} + + const Geometry::Class_info *Gis_geometry_collection::get_class_info() const { return &geometrycollection_class; diff --git a/sql/spatial.h b/sql/spatial.h index e93540db862..bea9ade9598 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -23,6 +23,8 @@ class Gis_read_stream; +#include "gcalc_tools.h" + const uint SRID_SIZE= 4; const uint SIZEOF_STORED_DOUBLE= 8; const uint POINT_DATA_SIZE= SIZEOF_STORED_DOUBLE*2; @@ -247,16 +249,19 @@ public: virtual const Class_info *get_class_info() const=0; virtual uint32 get_data_size() const=0; virtual bool init_from_wkt(Gis_read_stream *trs, String *wkb)=0; - /* returns the length of the wkb that was read */ virtual uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res)=0; + virtual uint init_from_opresult(String *bin, + const char *opres, uint res_len) + { return init_from_wkb(opres + 4, UINT_MAX32, wkb_ndr, bin) + 4; } + virtual bool get_data_as_wkt(String *txt, const char **end) const=0; virtual bool get_mbr(MBR *mbr, const char **end) const=0; virtual bool dimension(uint32 *dim, const char **end) const=0; virtual int get_x(double *x) const { return -1; } virtual int get_y(double *y) const { return -1; } - virtual int geom_length(double *len) const { return -1; } + virtual int geom_length(double *len, const char **end) const { return -1; } virtual int area(double *ar, const char **end) const { return -1;} virtual int is_closed(int *closed) const { return -1; } virtual int num_interior_ring(uint32 *n_int_rings) const { return -1; } @@ -269,6 +274,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_shape_transporter *trn) const=0; public: static Geometry *create_by_typeid(Geometry_buffer *buffer, int type_id); @@ -278,20 +284,11 @@ public: static Geometry *create_from_wkt(Geometry_buffer *buffer, Gis_read_stream *trs, String *wkt, bool init_stream=1); - static Geometry *create_from_wkb(Geometry_buffer *buffer, const char *wkb, - uint32 len, String *res); - int as_wkt(String *wkt, const char **end) - { - uint32 len= (uint) get_class_info()->m_name.length; - if (wkt->reserve(len + 2, 512)) - return 1; - wkt->qs_append(get_class_info()->m_name.str, len); - wkt->qs_append('('); - if (get_data_as_wkt(wkt, end)) - return 1; - wkt->qs_append(')'); - return 0; - } + static Geometry *create_from_wkb(Geometry_buffer *buffer, + const char *wkb, uint32 len, String *res); + static int create_from_opresult(Geometry_buffer *g_buf, + String *res, Gcalc_result_receiver &rr); + int as_wkt(String *wkt, const char **end); inline void set_data_ptr(const char *data, uint32 data_len) { @@ -369,12 +366,15 @@ public: return 0; } + int geom_length(double *len, const char **end) const; + int area(double *ar, const char **end) const; bool dimension(uint32 *dim, const char **end) const { *dim= 0; *end= 0; /* No default end */ return 0; } + int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; }; @@ -391,7 +391,8 @@ public: uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; - int geom_length(double *len) const; + int geom_length(double *len, const char **end) const; + int area(double *ar, const char **end) const; int is_closed(int *closed) const; int num_points(uint32 *n_points) const; int start_point(String *point) const; @@ -403,6 +404,7 @@ public: *end= 0; /* No default end */ return 0; } + int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; }; @@ -417,6 +419,7 @@ public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); + uint init_from_opresult(String *bin, const char *opres, uint res_len); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int area(double *ar, const char **end) const; @@ -431,6 +434,7 @@ public: *end= 0; /* No default end */ return 0; } + int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; }; @@ -445,6 +449,7 @@ public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); + uint init_from_opresult(String *bin, const char *opres, uint res_len); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; @@ -455,6 +460,7 @@ public: *end= 0; /* No default end */ return 0; } + int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; }; @@ -469,11 +475,12 @@ public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); + uint init_from_opresult(String *bin, const char *opres, uint res_len); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; int geometry_n(uint32 num, String *result) const; - int geom_length(double *len) const; + int geom_length(double *len, const char **end) const; int is_closed(int *closed) const; bool dimension(uint32 *dim, const char **end) const { @@ -481,6 +488,7 @@ public: *end= 0; /* No default end */ return 0; } + int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; }; @@ -507,7 +515,9 @@ public: *end= 0; /* No default end */ return 0; } + int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; + uint init_from_opresult(String *bin, const char *opres, uint res_len); }; @@ -521,11 +531,15 @@ public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); + uint init_from_opresult(String *bin, const char *opres, uint res_len); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; + int area(double *ar, const char **end) const; + int geom_length(double *len, const char **end) const; 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_shape_transporter *trn) const; const Class_info *get_class_info() const; }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2411c9c33ea..22c32085785 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8133,6 +8133,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, table_list->table->map= table_list->map_exec; table_list->table->maybe_null= table_list->maybe_null_exec; table_list->table->pos_in_table_list= table_list; + if (table_list->process_index_hints(table_list->table)) + DBUG_RETURN(1); } select_lex->leaf_tables.push_back(table_list); } diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 30c72d603f4..412686a49c9 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -192,17 +192,14 @@ #define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 26) #define OPTIMIZER_SWITCH_LAST (1ULL << 26) -/* -TODO: Materialization is off by default to mimic 5.1/5.2 behavior. -Once cost based choice between materialization and in-to-exists should be -enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION -*/ #define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \ OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT | \ + OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN | \ OPTIMIZER_SWITCH_TABLE_ELIMINATION | \ OPTIMIZER_SWITCH_IN_TO_EXISTS | \ + OPTIMIZER_SWITCH_MATERIALIZATION | \ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4ce278c98ef..07daaa2bd5c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -843,7 +843,7 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where) double rows; double read_time; - DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION)); + //DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION)); subq_pred->optimize(&rows, &read_time); subq_pred->jtbm_read_time= read_time; @@ -15041,9 +15041,15 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) { /* HAVING will be checked after processing aggregate functions, - But WHERE should checkd here (we alredy have read tables) + But WHERE should checkd here (we alredy have read tables). + If there is join->exec_const_cond, and all tables are constant, then it + is equivalent to join->conds. exec_const_cond is already checked in the + beginning of JOIN::exec. If it is false, JOIN::exec returns zero + result already there, therefore execution reaches this point only if + exec_const_cond is TRUE. Since it is equvalent to join->conds, then + join->conds is also TRUE. */ - if (!join->conds || join->conds->val_int()) + if (!join->conds || join->exec_const_cond || join->conds->val_int()) { error= (*end_select)(join, 0, 0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_QUERY_LIMIT) |