summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt1
-rw-r--r--sql/field.cc2
-rw-r--r--sql/gcalc_slicescan.cc1966
-rw-r--r--sql/gcalc_slicescan.h595
-rw-r--r--sql/gcalc_tools.cc1429
-rw-r--r--sql/gcalc_tools.h348
-rw-r--r--sql/gstream.cc23
-rw-r--r--sql/gstream.h9
-rw-r--r--sql/item_create.cc355
-rw-r--r--sql/item_geofunc.cc1092
-rw-r--r--sql/item_geofunc.h225
-rw-r--r--sql/opt_range.cc6
-rw-r--r--sql/opt_subselect.cc6
-rw-r--r--sql/opt_subselect.h2
-rw-r--r--sql/plistsort.c166
-rw-r--r--sql/spatial.cc738
-rw-r--r--sql/spatial.h50
-rw-r--r--sql/sql_base.cc2
-rw-r--r--sql/sql_priv.h7
-rw-r--r--sql/sql_select.cc12
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)