diff options
author | unknown <bar@gw.udmsearch.izhnet.ru> | 2002-02-22 15:24:42 +0400 |
---|---|---|
committer | unknown <bar@gw.udmsearch.izhnet.ru> | 2002-02-22 15:24:42 +0400 |
commit | 32a08516c974045bfc16fb30fcac18bb7ad8fbb8 (patch) | |
tree | a1157e118f45978188d5caf492b320d6302b1f93 | |
parent | 3d5dc65dfd72083ed159220d03bea942094e8662 (diff) | |
download | mariadb-git-32a08516c974045bfc16fb30fcac18bb7ad8fbb8.tar.gz |
Added GIS extension
BitKeeper/etc/ignore:
Added myisam/rt_test myisam/sp_test to the ignore list
-rw-r--r-- | .bzrignore | 2 | ||||
-rw-r--r-- | libmysqld/Makefile.am | 3 | ||||
-rw-r--r-- | sql/Makefile.am | 3 | ||||
-rw-r--r-- | sql/field.cc | 57 | ||||
-rw-r--r-- | sql/field.h | 32 | ||||
-rw-r--r-- | sql/gstream.cc | 137 | ||||
-rw-r--r-- | sql/gstream.h | 61 | ||||
-rw-r--r-- | sql/ha_myisam.cc | 8 | ||||
-rw-r--r-- | sql/item.h | 1 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 81 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 79 | ||||
-rw-r--r-- | sql/item_create.cc | 155 | ||||
-rw-r--r-- | sql/item_create.h | 38 | ||||
-rw-r--r-- | sql/item_func.cc | 129 | ||||
-rw-r--r-- | sql/item_func.h | 87 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 330 | ||||
-rw-r--r-- | sql/item_strfunc.h | 193 | ||||
-rw-r--r-- | sql/lex.h | 43 | ||||
-rw-r--r-- | sql/opt_range.cc | 120 | ||||
-rw-r--r-- | sql/opt_range.h | 9 | ||||
-rw-r--r-- | sql/opt_sum.cc | 2 | ||||
-rw-r--r-- | sql/spatial.cc | 1435 | ||||
-rw-r--r-- | sql/spatial.h | 476 | ||||
-rw-r--r-- | sql/sql_class.h | 7 | ||||
-rw-r--r-- | sql/sql_show.cc | 6 | ||||
-rw-r--r-- | sql/sql_string.cc | 40 | ||||
-rw-r--r-- | sql/sql_string.h | 45 | ||||
-rw-r--r-- | sql/sql_table.cc | 57 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 61 | ||||
-rw-r--r-- | sql/structs.h | 1 | ||||
-rw-r--r-- | sql/table.cc | 24 |
31 files changed, 3674 insertions, 48 deletions
diff --git a/.bzrignore b/.bzrignore index 6781375218d..af66d715b02 100644 --- a/.bzrignore +++ b/.bzrignore @@ -460,3 +460,5 @@ vio/test-sslserver vio/viotest-ssl Docs/mysql.xml mysql-test/r/rpl000001.eval +myisam/rt_test +myisam/sp_test diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index edee13c98bb..6e5c0b183bb 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -53,7 +53,8 @@ sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \ sql_rename.cc sql_repl.cc sql_select.cc sql_do.cc sql_show.cc \ sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \ sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \ - unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc + unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ + spatial.cc gstream.cc EXTRA_DIST = lib_vio.c diff --git a/sql/Makefile.am b/sql/Makefile.am index e1ed9ad8915..c83b5388ca0 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -81,7 +81,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ slave.cc sql_repl.cc sql_union.cc \ mini_client.cc mini_client_errors.c \ - stacktrace.c repl_failsafe.h repl_failsafe.cc + stacktrace.c repl_failsafe.h repl_failsafe.cc \ + gstream.cc spatial.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) diff --git a/sql/field.cc b/sql/field.cc index 2a0d0160d00..5d398d0ae5f 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3712,7 +3712,7 @@ Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, :Field_str(ptr_arg, (1L << min(blob_pack_length,3)*8)-1L, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg), - packlength(blob_pack_length),binary_flag(binary_arg) + packlength(blob_pack_length),binary_flag(binary_arg), geom_flag(true) { flags|= BLOB_FLAG; if (binary_arg) @@ -3954,8 +3954,30 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, /* The following is used only when comparing a key */ -void Field_blob::get_key_image(char *buff,uint length) +void Field_blob::get_key_image(char *buff,uint length, imagetype type) { + if(type == itMBR) + { + length-=HA_KEY_BLOB_LENGTH; + ulong blob_length=get_length(ptr); + char *blob; + get_ptr(&blob); + if(!blob_length) + { + return; + } + + MBR mbr; + Geometry gobj; + gobj.create_from_wkb(blob,blob_length); + gobj.get_mbr(&mbr); + float8store(buff, mbr.xmin); + float8store(buff+8, mbr.xmax); + float8store(buff+16, mbr.ymin); + float8store(buff+24, mbr.ymax); + return; + } + length-=HA_KEY_BLOB_LENGTH; uint32 blob_length=get_length(ptr); char *blob; @@ -3977,6 +3999,31 @@ void Field_blob::set_key_image(char *buff,uint length) Field_blob::store(buff+2,length); } +void Field_geom::get_key_image(char *buff,uint length, imagetype type) +{ + length-=HA_KEY_BLOB_LENGTH; + ulong blob_length=get_length(ptr); + char *blob; + get_ptr(&blob); + memcpy(buff+2,blob,length); + + MBR mbr; + Geometry gobj; + gobj.create_from_wkb(blob,blob_length); + gobj.get_mbr(&mbr); + float8store(buff, mbr.xmin); + float8store(buff+8, mbr.xmax); + float8store(buff+16, mbr.ymin); + float8store(buff+24, mbr.ymax); + return; +} + +void Field_geom::set_key_image(char *buff,uint length) +{ +} + + + int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length) { char *blob1; @@ -4606,6 +4653,7 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) case FIELD_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; case FIELD_TYPE_SET: case FIELD_TYPE_ENUM: abort(); return 0; // This shouldn't happen + default: return 0; } return 0; // This shouldn't happen } @@ -4652,6 +4700,11 @@ Field *make_field(char *ptr, uint32 field_length, return new Field_blob(ptr,null_pos,null_bit, unireg_check, field_name, table, pack_length,f_is_binary(pack_flag) != 0); + if (f_is_geom(pack_flag)) + return new Field_geom(ptr,null_pos,null_bit, + unireg_check, field_name, table, + pack_length,f_is_binary(pack_flag) != 0); + if (interval) { if (f_is_enum(pack_flag)) diff --git a/sql/field.h b/sql/field.h index a9b257f0c3a..422e91768e6 100644 --- a/sql/field.h +++ b/sql/field.h @@ -47,6 +47,9 @@ public: enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL, CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD, BIT_FIELD, TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD}; + + enum imagetype { itRAW, itMBR}; + utype unireg_check; uint32 field_length; // Length of field uint16 flags; @@ -137,7 +140,7 @@ public: { memcpy(buff,ptr,length); } inline void set_image(char *buff,uint length) { memcpy(ptr,buff,length); } - virtual void get_key_image(char *buff,uint length) + virtual void get_key_image(char *buff,uint length, imagetype type) { get_image(buff,length); } virtual void set_key_image(char *buff,uint length) { set_image(buff,length); } @@ -825,6 +828,7 @@ class Field_blob :public Field_str { uint packlength; String value; // For temporaries bool binary_flag; + bool geom_flag; public: Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, @@ -834,7 +838,7 @@ public: struct st_table *table_arg, bool binary_arg) :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, table_arg), - packlength(3),binary_flag(binary_arg) + packlength(3),binary_flag(binary_arg), geom_flag(true) { flags|= BLOB_FLAG; if (binary_arg) @@ -881,7 +885,7 @@ public: store_length(length); memcpy_fixed(ptr+packlength,&data,sizeof(char*)); } - void get_key_image(char *buff,uint length); + void get_key_image(char *buff,uint length, imagetype type); void set_key_image(char *buff,uint length); void sql_type(String &str) const; inline bool copy() @@ -910,6 +914,25 @@ public: }; +class Field_geom :public Field_blob { +public: + Field_geom(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + struct st_table *table_arg,uint blob_pack_length, + bool binary_arg) + :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, table_arg, blob_pack_length,binary_arg) {} + Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg, bool binary_arg) + :Field_blob(len_arg, maybe_null_arg, field_name_arg, + table_arg, binary_arg) {} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY; } + + void get_key_image(char *buff,uint length, imagetype type); + void set_key_image(char *buff,uint length); +}; + + class Field_enum :public Field_str { protected: uint packlength; @@ -1059,6 +1082,8 @@ bool test_if_int(const char *str,int length); #define FIELDFLAG_INTERVAL 256 #define FIELDFLAG_BITFIELD 512 // mangled with dec! #define FIELDFLAG_BLOB 1024 // mangled with dec! +#define FIELDFLAG_GEOM 2048 + #define FIELDFLAG_LEFT_FULLSCREEN 8192 #define FIELDFLAG_RIGHT_FULLSCREEN 16384 #define FIELDFLAG_FORMAT_NUMBER 16384 // predit: ###,,## in output @@ -1085,6 +1110,7 @@ bool test_if_int(const char *str,int length); #define f_is_enum(x) ((x) & FIELDFLAG_INTERVAL) #define f_is_bitfield(x) ((x) & FIELDFLAG_BITFIELD) #define f_is_blob(x) (((x) & (FIELDFLAG_BLOB | FIELDFLAG_NUMBER)) == FIELDFLAG_BLOB) +#define f_is_geom(x) ((x) & FIELDFLAG_GEOM) #define f_is_equ(x) ((x) & (1+2+FIELDFLAG_PACK+31*256)) #define f_settype(x) (((int) x) << FIELDFLAG_PACK_SHIFT) #define f_maybe_null(x) (x & FIELDFLAG_MAYBE_NULL) diff --git a/sql/gstream.cc b/sql/gstream.cc new file mode 100644 index 00000000000..5a58fef6744 --- /dev/null +++ b/sql/gstream.cc @@ -0,0 +1,137 @@ +#include "mysql_priv.h" + +int GTextReadStream::get_next_toc_type() const +{ + const char *cur = m_cur; + while((*cur)&&(strchr(" \t\r\n",*cur))) + { + cur++; + } + if(!(*cur)) + { + return eostream; + } + + if(((*cur>='a') && (*cur<='z')) || ((*cur>='A') && (*cur<='Z')) || (*cur=='_')) + { + return word; + } + + if(((*cur>='0') && (*cur<='9')) || (*cur=='-') || (*cur=='+') || (*cur=='.')) + { + return numeric; + } + + if(*cur == '(') + { + return l_bra; + } + + if(*cur == ')') + { + return r_bra; + } + + if(*cur == ',') + { + return comma; + } + + return unknown; +} + +const char *GTextReadStream::get_next_word(int *word_len) +{ + const char *cur = m_cur; + while((*cur)&&(strchr(" \t\r\n",*cur))) + { + cur++; + } + m_last_text_position = cur; + + if(!(*cur)) + { + return 0; + } + + const char *wd_start = cur; + + if(((*cur<'a') || (*cur>'z')) && ((*cur<'A') || (*cur>'Z')) && (*cur!='_')) + { + return NULL; + } + + ++cur; + + while(((*cur>='a') && (*cur<='z')) || ((*cur>='A') && (*cur<='Z')) || (*cur=='_') || + ((*cur>='0') && (*cur<='9'))) + { + ++cur; + } + + *word_len = cur - wd_start; + + m_cur = cur; + + return wd_start; +} + +int GTextReadStream::get_next_number(double *d) +{ + const char *cur = m_cur; + while((*cur)&&(strchr(" \t\r\n",*cur))) + { + cur++; + } + + m_last_text_position = cur; + if(!(*cur)) + { + set_error_msg("Numeric constant expected"); + return 1; + } + + if(((*cur<'0') || (*cur>'9')) && (*cur!='-') && (*cur!='+') && (*cur!='.')) + { + set_error_msg("Numeric constant expected"); + return 1; + } + + char *endptr; + + *d = strtod(cur, &endptr); + + if(endptr) + { + m_cur = endptr; + } + + return 0; +} + +char GTextReadStream::get_next_symbol() +{ + const char *cur = m_cur; + while((*cur)&&(strchr(" \t\r\n",*cur))) + { + cur++; + } + if(!(*cur)) + { + return 0; + } + + m_cur = cur + 1; + m_last_text_position = cur; + + return *cur; +} + +void GTextReadStream::set_error_msg(const char *msg) +{ + size_t len = strlen(msg); + m_err_msg = (char *)my_realloc(m_err_msg, len + 1, MYF(MY_ALLOW_ZERO_PTR)); + memcpy(m_err_msg, msg, len + 1); +} + + diff --git a/sql/gstream.h b/sql/gstream.h new file mode 100644 index 00000000000..f8df6e337b0 --- /dev/null +++ b/sql/gstream.h @@ -0,0 +1,61 @@ +#ifndef GSTREAM_H +#define GSTREAM_H + +#ifdef WITHOUT_MYSQL + #include ".\rtree\myisamdef.h" +#else + #include "mysql_priv.h" +#endif + +class GTextReadStream +{ +public: + enum TokTypes + { + unknown, + eostream, + word, + numeric, + l_bra, + r_bra, + comma, + }; + GTextReadStream(const char *buffer, int size) : + m_cur(buffer), m_limit(buffer + size), m_last_text_position(buffer), m_err_msg(NULL) {} + GTextReadStream() : m_cur(NULL), m_limit(NULL), m_err_msg(NULL) {} + + ~GTextReadStream() + { + my_free(m_err_msg, MYF(MY_ALLOW_ZERO_PTR)); + } + + int get_next_toc_type() const; + const char *get_next_word(int *word_len); + int get_next_number(double *d); + char get_next_symbol(); + + const char *get_last_text_position() const + { + return m_last_text_position; + } + + void set_error_msg(const char *msg); + +// caller should free this pointer + char *get_error_msg() + { + char *err_msg = m_err_msg; + m_err_msg = NULL; + return err_msg; + } +protected: + const char *m_cur; + const char *m_limit; + const char *m_last_text_position; + char *m_err_msg; +}; + +#endif + + + diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 78ac9f3b309..05bc8901c5b 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -28,6 +28,7 @@ #include "../srclib/myisam/myisamdef.h" #else #include "../myisam/myisamdef.h" +#include "../myisam/rt_index.h" #endif ulong myisam_sort_buffer_size; @@ -763,7 +764,7 @@ int ha_myisam::index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { statistic_increment(ha_read_key_count,&LOCK_status); - int error=mi_rkey(file,buf,active_index, key, key_len, find_flag); + int error=mi_rkey(file,buf,active_index, key, key_len, (enum ha_rkey_function)find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return error; } @@ -772,7 +773,7 @@ int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag) { statistic_increment(ha_read_key_count,&LOCK_status); - int error=mi_rkey(file,buf,index, key, key_len, find_flag); + int error=mi_rkey(file,buf,index, key, key_len, (enum ha_rkey_function)find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return error; } @@ -1008,7 +1009,8 @@ int ha_myisam::create(const char *name, register TABLE *table, pos=table->key_info; for (i=0; i < table->keys ; i++, pos++) { - keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT)); + keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL)); + keydef[i].key_alg=pos->key_alg; // +BAR keydef[i].seg=keyseg; keydef[i].keysegs=pos->key_parts; for (j=0 ; j < pos->key_parts ; j++) diff --git a/sql/item.h b/sql/item.h index 5028f25c6b8..247b3d7081d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -357,6 +357,7 @@ public: }; +#include "spatial.h" #include "item_sum.h" #include "item_func.h" #include "item_cmpfunc.h" diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 0c83698e60a..1de398177b5 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -24,6 +24,7 @@ #include "mysql_priv.h" #include <m_ctype.h> + /* ** Test functions ** These returns 0LL if false and 1LL if true and null if some arg is null @@ -1348,3 +1349,83 @@ Item_func_regex::~Item_func_regex() } #endif /* USE_REGEX */ + + +/**************************************************************** + Classes and functions for spatial relations +*****************************************************************/ + +longlong Item_func_spatial_rel::val_int() +{ + String *res1=args[0]->val_str(&tmp_value1); + String *res2=args[1]->val_str(&tmp_value2); + Geometry g1, g2; + MBR mbr1,mbr2; + + if ((null_value=(args[0]->null_value || + args[1]->null_value || + g1.create_from_wkb(res1->ptr(),res1->length()) || + g2.create_from_wkb(res2->ptr(),res2->length()) || + g1.get_mbr(&mbr1) || + g2.get_mbr(&mbr2)))) + return 0; + + switch (spatial_rel) + { + case SP_CONTAINS_FUNC: + return mbr1.contains(&mbr2); + case SP_WITHIN_FUNC: + return mbr1.within(&mbr2); + case SP_EQUALS_FUNC: + return mbr1.equals(&mbr2); + case SP_DISJOINT_FUNC: + return mbr1.disjoint(&mbr2); + case SP_INTERSECTS_FUNC: + return mbr1.intersects(&mbr2); + case SP_TOUCHES_FUNC: + return mbr1.touches(&mbr2); + case SP_OVERLAPS_FUNC: + return mbr1.overlaps(&mbr2); + case SP_CROSSES_FUNC: + return 0; + default: + break; + } + + null_value=1; + return 0; +} + +longlong Item_func_isempty::val_int() +{ + String tmp; + null_value=0; + return args[0]->null_value ? 1 : 0; +} + +longlong Item_func_issimple::val_int() +{ + String tmp; + String *wkb=args[0]->val_str(&tmp); + + if ((null_value= (!wkb || args[0]->null_value ))) + return 0; + /* TODO: Ramil or Holyfoot, add real IsSimple calculation */ + return 0; +} + +longlong Item_func_isclosed::val_int() +{ + String tmp; + String *wkb=args[0]->val_str(&tmp); + Geometry geom; + int isclosed; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,is_closed) || + geom.is_closed(&isclosed)); + + return (longlong) isclosed; +} diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c9c7d5654d6..b8f21f21008 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -570,3 +570,82 @@ inline Item *and_conds(Item *a,Item *b) cond->update_used_tables(); return cond; } + + +/************************************************************** +Spatial relations +***************************************************************/ + +class Item_func_spatial_rel :public Item_bool_func2 +{ + enum Functype spatial_rel; +public: + Item_func_spatial_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; + } + } + 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: + return "sp_unknown"; + } + } +}; + + +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"; } +}; + +class Item_func_issimple :public Item_bool_func +{ +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"; } +}; + +class Item_func_isclosed :public Item_bool_func +{ +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"; } +}; diff --git a/sql/item_create.cc b/sql/item_create.cc index 6f64e9517ba..79d7fcfe230 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -413,3 +413,158 @@ Item *create_func_cast(Item *a, Item_cast cast_type) } return res; } + +Item *create_func_geometry_from_text(Item* a) +{ + return new Item_func_geometry_from_text(a); +} + +Item *create_func_as_text(Item* a) +{ + return new Item_func_as_text(a); +} + +Item *create_func_startpoint(Item* a) +{ + return new Item_func_spatial_decomp(a, Item_func::SP_STARTPOINT); +} + +Item *create_func_endpoint(Item* a) +{ + return new Item_func_spatial_decomp(a, Item_func::SP_ENDPOINT); +} + +Item *create_func_exteriorring(Item* a) +{ + return new Item_func_spatial_decomp(a, Item_func::SP_EXTERIORRING); +} + +Item *create_func_pointn(Item* a, Item* b) +{ + return new Item_func_spatial_decomp_n(a,b,Item_func::SP_POINTN); +} + +Item *create_func_interiorringn(Item* a, Item* b) +{ + return new Item_func_spatial_decomp_n(a,b,Item_func::SP_INTERIORRINGN); +} + +Item *create_func_geometryn(Item* a, Item* b) +{ + return new Item_func_spatial_decomp_n(a,b,Item_func::SP_GEOMETRYN); +} + +Item *create_func_centroid(Item* a) +{ + return new Item_func_centroid(a); +} + +Item *create_func_envelope(Item* a) +{ + return new Item_func_envelope(a); +} + +Item *create_func_equals(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_EQUALS_FUNC); +} + +Item *create_func_disjoint(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_DISJOINT_FUNC); +} + +Item *create_func_intersects(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_INTERSECTS_FUNC); +} + +Item *create_func_touches(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_TOUCHES_FUNC); +} + +Item *create_func_crosses(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_CROSSES_FUNC); +} + +Item *create_func_within(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_WITHIN_FUNC); +} + +Item *create_func_contains(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_CONTAINS_FUNC); +} + +Item *create_func_overlaps(Item* a, Item* b) +{ + return new Item_func_spatial_rel(a, b, Item_func::SP_OVERLAPS_FUNC); +} + +Item *create_func_isempty(Item* a) +{ + return new Item_func_isempty(a); +} + +Item *create_func_issimple(Item* a) +{ + return new Item_func_issimple(a); +} + +Item *create_func_isclosed(Item* a) +{ + return new Item_func_isclosed(a); +} + +Item *create_func_geometry_type(Item* a) +{ + return new Item_func_geometry_type(a); +} + +Item *create_func_dimension(Item* a) +{ + return new Item_func_dimension(a); +} + +Item *create_func_x(Item* a) +{ + return new Item_func_x(a); +} + +Item *create_func_y(Item* a) +{ + return new Item_func_y(a); +} + +Item *create_func_numpoints(Item* a) +{ + return new Item_func_numpoints(a); +} + +Item *create_func_numinteriorring(Item* a) +{ + return new Item_func_numinteriorring(a); +} + +Item *create_func_numgeometries(Item* a) +{ + return new Item_func_numgeometries(a); +} + +Item *create_func_area(Item* a) +{ + return new Item_func_area(a); +} + +Item *create_func_glength(Item* a) +{ + return new Item_func_glength(a); +} + +Item *create_func_point(Item* a, Item* b) +{ + return new Item_func_point(a,b); +} diff --git a/sql/item_create.h b/sql/item_create.h index 580596505da..6f7e8ebf89a 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -88,3 +88,41 @@ Item *create_func_version(void); Item *create_func_weekday(Item* a); Item *create_load_file(Item* a); Item *create_wait_for_master_pos(Item* a, Item* b); + +Item *create_func_geometry_from_text(Item* a); +Item *create_func_as_text(Item* a); +Item *create_func_startpoint(Item* a); +Item *create_func_endpoint(Item* a); +Item *create_func_exteriorring(Item* a); +Item *create_func_centroid(Item* a); +Item *create_func_envelope(Item* a); +Item *create_func_pointn(Item* a, Item* b); +Item *create_func_interiorringn(Item* a, Item* b); +Item *create_func_geometryn(Item* a, Item* b); + +Item *create_func_equals(Item* a, Item* b); +Item *create_func_disjoint(Item* a, Item* b); +Item *create_func_intersects(Item* a, Item* b); +Item *create_func_touches(Item* a, Item* b); +Item *create_func_crosses(Item* a, Item* b); +Item *create_func_within(Item* a, Item* b); +Item *create_func_contains(Item* a, Item* b); +Item *create_func_overlaps(Item* a, Item* b); + +Item *create_func_isempty(Item* a); +Item *create_func_issimple(Item* a); +Item *create_func_isclosed(Item* a); + +Item *create_func_geometry_type(Item* a); +Item *create_func_dimension(Item* a); +Item *create_func_x(Item* a); +Item *create_func_y(Item* a); +Item *create_func_area(Item* a); +Item *create_func_glength(Item* a); + +Item *create_func_numpoints(Item* a); +Item *create_func_numinteriorring(Item* a); +Item *create_func_numgeometries(Item* a); + +Item *create_func_point(Item* a,Item* b); + diff --git a/sql/item_func.cc b/sql/item_func.cc index 1818a755a43..65d4413b484 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -27,6 +27,8 @@ #include <time.h> #include <ft_global.h> #include "slave.h" // for wait_for_master_pos +#include "gstream.h" + /* return TRUE if item is a constant */ @@ -2252,3 +2254,130 @@ Item *get_system_var(LEX_STRING name) my_error(ER_UNKNOWN_SYSTEM_VARIABLE,MYF(0),name); return 0; } + + +/************************************************************************** +Spatial functions +***************************************************************************/ + +longlong Item_func_dimension::val_int() +{ + uint32 dim; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + geom.dimension(&dim)); + + return (longlong) dim; +} + +longlong Item_func_numinteriorring::val_int() +{ + uint32 num; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,num_interior_ring) || + geom.num_interior_ring(&num)); + + return (longlong) num; +} + +longlong Item_func_numgeometries::val_int() +{ + uint32 num; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,num_geometries) || + geom.num_geometries(&num)); + + return (longlong) num; +} + +longlong Item_func_numpoints::val_int() +{ + uint32 num; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,num_points) || + geom.num_points(&num)); + + return (longlong) num; +} + + +double Item_func_x::val() +{ + double res; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,get_x) || + geom.get_x(&res)); + + return res; +} + + +double Item_func_y::val() +{ + double res; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,get_y) || + geom.get_y(&res)); + + return res; +} + + +double Item_func_area::val() +{ + double res; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,area) || + geom.area(&res)); + + return res; +} + + +double Item_func_glength::val() +{ + double res; + String *wkb=args[0]->val_str(&value); + Geometry geom; + + null_value= (!wkb || + args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,length) || + geom.length(&res)); + return res; +} diff --git a/sql/item_func.h b/sql/item_func.h index 823bfb44a96..41df0a63c1c 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -39,7 +39,12 @@ public: enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, GE_FUNC,GT_FUNC,FT_FUNC, LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC, - COND_AND_FUNC,COND_OR_FUNC,BETWEEN,IN_FUNC,INTERVAL_FUNC}; + COND_AND_FUNC,COND_OR_FUNC,BETWEEN,IN_FUNC,INTERVAL_FUNC, + SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC, + SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC, + SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC, + SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING, + SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN}; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL }; enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } @@ -944,6 +949,86 @@ public: void init_search(bool no_order); }; + +class Item_func_dimension :public Item_int_func +{ + String value; +public: + Item_func_dimension(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "dimension"; } + void fix_length_and_dec() { max_length=10; } +}; + + +class Item_func_x :public Item_real_func +{ + String value; +public: + Item_func_x(Item *a) :Item_real_func(a) {} + double val(); + const char *func_name() const { return "x"; } +}; + + +class Item_func_y :public Item_real_func +{ + String value; +public: + Item_func_y(Item *a) :Item_real_func(a) {} + double val(); + const char *func_name() const { return "y"; } +}; + + +class Item_func_numgeometries :public Item_int_func +{ + String value; +public: + Item_func_numgeometries(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "numgeometries"; } + void fix_length_and_dec() { max_length=10; } +}; + +class Item_func_numinteriorring :public Item_int_func +{ + String value; +public: + Item_func_numinteriorring(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "numinteriorring"; } + void fix_length_and_dec() { max_length=10; } +}; + +class Item_func_numpoints :public Item_int_func +{ + String value; +public: + Item_func_numpoints(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "numpoints"; } + void fix_length_and_dec() { max_length=10; } +}; + +class Item_func_area :public Item_real_func +{ + String value; +public: + Item_func_area(Item *a) :Item_real_func(a) {} + double val(); + const char *func_name() const { return "area"; } +}; + +class Item_func_glength :public Item_real_func +{ + String value; +public: + Item_func_glength(Item *a) :Item_real_func(a) {} + double val(); + const char *func_name() const { return "glength"; } +}; + class Item_func_match_nl :public Item_func_match { public: diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index c64fdc7a049..cdf909e8c4d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1953,3 +1953,333 @@ String* Item_func_inet_ntoa::val_str(String* str) str->length(str->length()-1); // Remove last '.'; return str; } + +/******************************************************* +General functions for spatial objects +********************************************************/ + +#include "gstream.h" + +String *Item_func_geometry_from_text::val_str(String *str) +{ + Geometry geom; + String *wkt = args[0]->val_str(str); + GTextReadStream trs(wkt->ptr(), wkt->length()); + + str->length(0); + if ((null_value=(args[0]->null_value || geom.create_from_wkt(&trs, str, 0)))) + return 0; + return str; +} + + +void Item_func_geometry_from_text::fix_length_and_dec() +{ + max_length=MAX_BLOB_WIDTH; +} + + +String *Item_func_as_text::val_str(String *str) +{ + String *wkt = args[0]->val_str(str); + Geometry geom; + + str->length(0); + if ((null_value=(args[0]->null_value || + geom.create_from_wkb(wkt->ptr(),wkt->length()) || + geom.as_wkt(str)))) + return 0; + return str; +} + +void Item_func_as_text::fix_length_and_dec() +{ + max_length=MAX_BLOB_WIDTH; +} + +String *Item_func_geometry_type::val_str(String *str) +{ + String *wkt = args[0]->val_str(str); + Geometry geom; + + if ((null_value=(args[0]->null_value || + geom.create_from_wkb(wkt->ptr(),wkt->length())))) + return 0; + str->copy(geom.get_class_info()->m_name); + return str; +} + + +String *Item_func_envelope::val_str(String *str) +{ + String *wkb = args[0]->val_str(str); + Geometry geom; + + null_value = args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + geom.envelope(str); + + return null_value ? 0 : str; +} + + +String *Item_func_centroid::val_str(String *str) +{ + String *wkb = args[0]->val_str(str); + Geometry geom; + + null_value = args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) || + !GEOM_METHOD_PRESENT(geom,centroid) || + geom.centroid(str); + + return null_value ? 0: str; +} + + +/*********************************************** + Spatial decomposition functions +***********************************************/ + +String *Item_func_spatial_decomp::val_str(String *str) +{ + String *wkb = args[0]->val_str(str); + Geometry geom; + + if ((null_value = (args[0]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length())))) + return 0; + + null_value=1; + switch(decomp_func) + { + case SP_STARTPOINT: + if (!GEOM_METHOD_PRESENT(geom,start_point) || geom.start_point(str)) + goto ret; + break; + + case SP_ENDPOINT: + if (!GEOM_METHOD_PRESENT(geom,end_point) || geom.end_point(str)) + goto ret; + break; + + case SP_EXTERIORRING: + if (!GEOM_METHOD_PRESENT(geom,exterior_ring) || geom.exterior_ring(str)) + goto ret; + break; + + default: + goto ret; + } + null_value=0; + +ret: + return null_value ? 0 : str; +} + + +String *Item_func_spatial_decomp_n::val_str(String *str) +{ + String *wkb = args[0]->val_str(str); + long n = (long) args[1]->val_int(); + Geometry geom; + + if ((null_value = (args[0]->null_value || + args[1]->null_value || + geom.create_from_wkb(wkb->ptr(),wkb->length()) ))) + return 0; + + null_value=1; + + switch(decomp_func_n) + { + case SP_POINTN: + if (!GEOM_METHOD_PRESENT(geom,point_n) || + geom.point_n(n,str)) + goto ret; + break; + + case SP_GEOMETRYN: + if (!GEOM_METHOD_PRESENT(geom,geometry_n) || + geom.geometry_n(n,str)) + goto ret; + break; + + case SP_INTERIORRINGN: + if (!GEOM_METHOD_PRESENT(geom,interior_ring_n) || + geom.interior_ring_n(n,str)) + goto ret; + break; + + default: + goto ret; + } + null_value=0; + +ret: + return null_value ? 0 : str; +} + + + +/*********************************************** +Functions to concatinate various spatial objects +************************************************/ + + +/* +* Concatinate doubles into Point +*/ + + +String *Item_func_point::val_str(String *str) +{ + if ( (null_value = (args[0]->null_value || + args[1]->null_value || + str->realloc(1+4+8+8)))) + return 0; + + str->length(0); + str->q_append((char)Geometry::wkbNDR); + str->q_append((uint32)Geometry::wkbPoint); + str->q_append((double)args[0]->val()); + str->q_append((double)args[1]->val()); + return str; +} + + +/* + Concatinates various items into various collections + with checkings for valid wkb type of items. + For example, MultiPoint can be a collection of Points only. + coll_type contains wkb type of target collection. + item_type contains a valid wkb type of items. + In the case when coll_type is wkbGeometryCollection, + we do not check wkb type of items, any is valid. +*/ + +String *Item_func_spatial_collection::val_str(String *str) +{ + uint i; + + null_value=1; + + str->length(0); + if(str->reserve(9,512)) + return 0; + + str->q_append((char)Geometry::wkbNDR); + str->q_append((uint32)coll_type); + str->q_append((uint32)arg_count); + + for (i = 0; i < arg_count; ++i) + { + if (args[i]->null_value) + goto ret; + + String *res = args[i]->val_str(str); + + if ( coll_type == Geometry::wkbGeometryCollection ) + { + /* + In the case of GeometryCollection we don't need + any checkings for item types, so just copy them + into target collection + */ + if ((null_value=(str->reserve(res->length(),512)))) + goto ret; + + str->q_append(res->ptr(),res->length()); + } + else + { + uint32 wkb_type, len=res->length(); + const char *data=res->ptr()+1; + + /* + In the case of named collection we must to + check that items are of specific type, let's + do this checking now + */ + + if (len<5) + goto ret; + wkb_type=uint4korr(data); + data+=4; + len-=5; + if ( wkb_type != item_type ) + goto ret; + + switch(coll_type) + { + case Geometry::wkbMultiPoint: + case Geometry::wkbMultiLineString: + case Geometry::wkbMultiPolygon: + if (len<WKB_HEADER_SIZE) + goto ret; + + data+=WKB_HEADER_SIZE; + len-=WKB_HEADER_SIZE; + if (str->reserve(len,512)) + goto ret; + str->q_append(data,len); + break; + + case Geometry::wkbLineString: + if (str->reserve(POINT_DATA_SIZE,512)) + goto ret; + str->q_append(data,POINT_DATA_SIZE); + break; + + case Geometry::wkbPolygon: + { + uint32 n_points; + double x1, y1, x2, y2; + + if (len < WKB_HEADER_SIZE + 4 + 8 + 8) + goto ret; + data+=WKB_HEADER_SIZE; + len-=WKB_HEADER_SIZE; + + uint32 llen=len; + const char *ldata=data; + + n_points=uint4korr(data); + data+=4; + float8get(x1,data); + data+=8; + float8get(y1,data); + data+=8; + + len-= 4 + 8 + 8; + + if (len < n_points * POINT_DATA_SIZE) + goto ret; + data+=(n_points-2) * POINT_DATA_SIZE; + + float8get(x2,data); + float8get(y2,data+8); + + if ((x1 != x2) || (y1 != y2)) + goto ret; + + if (str->reserve(llen,512)) + goto ret; + str->q_append(ldata, llen); + } + break; + + default: + goto ret; + } + } + } + + if (str->length() > max_allowed_packet) + goto ret; + + null_value = 0; + +ret: + return null_value ? 0 : str; +} diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 1279a5099d5..a8768e62f98 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -475,3 +475,196 @@ public: const char *func_name() const { return "inet_ntoa"; } void fix_length_and_dec() { decimals = 0; max_length=3*8+7; } }; + + +/******************************************************* +Spatial functions +********************************************************/ + +class Item_func_geometry_from_text :public Item_str_func +{ +public: + Item_func_geometry_from_text(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "geometryfromtext"; } + String *val_str(String *); + void fix_length_and_dec(); +}; + +class Item_func_as_text :public Item_str_func +{ +public: + Item_func_as_text(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "astext"; } + String *val_str(String *); + void fix_length_and_dec(); +}; + +class Item_func_geometry_type :public Item_str_func +{ +public: + Item_func_geometry_type(Item *a) :Item_str_func(a) {} + String *val_str(String *); + const char *func_name() const { return "geometrytype"; } + void fix_length_and_dec() + { + max_length=20; // "GeometryCollection" is the most long + }; +}; + +class Item_func_centroid :public Item_str_func +{ +public: + Item_func_centroid(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "centroid"; } + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} +}; + +class Item_func_envelope :public Item_str_func +{ +public: + Item_func_envelope(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "envelope"; } + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} +}; + +class Item_func_point :public Item_str_func +{ +public: + Item_func_point(Item *a,Item *b) :Item_str_func(a,b) {} + const char *func_name() const { return "point"; } + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} +}; + +class Item_func_spatial_decomp :public Item_str_func +{ + enum Functype decomp_func; +public: + Item_func_spatial_decomp(Item *a, Item_func::Functype ft) : + Item_str_func(a) { decomp_func = ft; } + const char *func_name() const + { + switch (decomp_func) + { + case SP_STARTPOINT: + return "startpoint"; + case SP_ENDPOINT: + return "endpoint"; + case SP_EXTERIORRING: + return "exteriorring"; + default: + return "spatial_decomp_unknown"; + } + } + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} +}; + +class Item_func_spatial_decomp_n :public Item_str_func +{ + enum Functype decomp_func_n; +public: + Item_func_spatial_decomp_n(Item *a, Item *b, Item_func::Functype ft) : + Item_str_func(a, b) { decomp_func_n = ft; } + const char *func_name() const + { + switch (decomp_func_n) + { + case SP_POINTN: + return "pointn"; + case SP_GEOMETRYN: + return "geometryn"; + case SP_INTERIORRINGN: + return "interiorringn"; + default: + return "spatial_decomp_n_unknown"; + } + } + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} +}; + + +class Item_func_spatial_collection :public Item_str_func +{ + String tmp_value; + enum Geometry::wkbType coll_type; + enum Geometry::wkbType item_type; +public: + Item_func_spatial_collection( + List<Item> &list, enum Geometry::wkbType ct, enum Geometry::wkbType it) : + Item_str_func(list) + { + coll_type=ct; + item_type=it; + } + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "multipoint"; } +}; + + +/* +class Item_func_multipoint :public Item_str_func +{ + String tmp_value; +public: + Item_func_multipoint(List<Item> &list) :Item_str_func(list) {} + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "multipoint"; } +}; + +class Item_func_linestring :public Item_str_func +{ + String tmp_value; +public: + Item_func_linestring(List<Item> &list) :Item_str_func(list) {} + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "linestring"; } +}; + +class Item_func_multilinestring :public Item_str_func +{ + String tmp_value; +public: + Item_func_multilinestring(List<Item> &list) :Item_str_func(list) {} + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "multilinestring"; } +}; + +class Item_func_polygon :public Item_str_func +{ + String tmp_value; +public: + Item_func_polygon(List<Item> &list) :Item_str_func(list) {} + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "polygon"; } +}; + +class Item_func_multipolygon :public Item_str_func +{ + String tmp_value; +public: + Item_func_multipolygon(List<Item> &list) :Item_str_func(list) {} + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "multipolygon"; } +}; + +class Item_func_geometrycollection :public Item_str_func +{ + String tmp_value; +public: + Item_func_geometrycollection(List<Item> &list) :Item_str_func(list) {} + String *val_str(String *); + void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} + const char *func_name() const { return "geometrycollection"; } +}; + +*/ diff --git a/sql/lex.h b/sql/lex.h index 6bee4152e48..04edaf217b9 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -75,6 +75,7 @@ static SYMBOL symbols[] = { { "BOOL", SYM(BOOL_SYM),0,0}, { "BOOLEAN", SYM(BOOLEAN_SYM),0,0}, { "BOTH", SYM(BOTH),0,0}, + { "BTREE", SYM(BTREE_SYM),0,0}, { "BY", SYM(BY),0,0}, { "CACHE", SYM(CACHE_SYM),0,0}, { "CASCADE", SYM(CASCADE),0,0}, @@ -158,12 +159,14 @@ static SYMBOL symbols[] = { { "FULL", SYM(FULL),0,0}, { "FULLTEXT", SYM(FULLTEXT_SYM),0,0}, { "FUNCTION", SYM(UDF_SYM),0,0}, + { "GEOM", SYM(GEOM_SYM),0,0}, { "GLOBAL", SYM(GLOBAL_SYM),0,0}, { "GRANT", SYM(GRANT),0,0}, { "GRANTS", SYM(GRANTS),0,0}, { "GROUP", SYM(GROUP),0,0}, { "HAVING", SYM(HAVING),0,0}, { "HANDLER", SYM(HANDLER_SYM),0,0}, + { "HASH", SYM(HASH_SYM),0,0}, { "HEAP", SYM(HEAP_SYM),0,0}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0}, { "HOUR", SYM(HOUR_SYM),0,0}, @@ -296,6 +299,7 @@ static SYMBOL symbols[] = { { "ROLLBACK", SYM(ROLLBACK_SYM),0,0}, { "ROW", SYM(ROW_SYM),0,0}, { "ROWS", SYM(ROWS_SYM),0,0}, + { "RTREE", SYM(RTREE_SYM),0,0}, { "SECOND", SYM(SECOND_SYM),0,0}, { "SELECT", SYM(SELECT_SYM),0,0}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0}, @@ -308,6 +312,7 @@ static SYMBOL symbols[] = { { "SLAVE", SYM(SLAVE),0,0}, { "SMALLINT", SYM(SMALLINT),0,0}, { "SONAME", SYM(UDF_SONAME_SYM),0,0}, + { "SPATIAL", SYM(SPATIAL_SYM),0,0}, { "SQL_AUTO_IS_NULL", SYM(SQL_AUTO_IS_NULL),0,0}, { "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT),0,0}, { "SQL_BIG_SELECTS", SYM(SQL_BIG_SELECTS),0,0}, @@ -384,8 +389,10 @@ static SYMBOL sql_functions[] = { { "ABS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_abs)}, { "ACOS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_acos)}, { "ADDDATE", SYM(DATE_ADD_INTERVAL),0,0}, + { "AREA", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_area)}, { "ASCII", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ascii)}, { "ASIN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_asin)}, + { "ASTEXT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_as_text)}, { "ATAN", SYM(ATAN),0,0}, { "ATAN2", SYM(ATAN),0,0}, { "BENCHMARK", SYM(BENCHMARK_SYM),0,0}, @@ -396,17 +403,20 @@ static SYMBOL sql_functions[] = { { "CAST", SYM(CAST_SYM),0,0}, { "CEILING", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, { "BIT_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_length)}, + { "CENTROID", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_centroid)}, { "CHAR_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "CHARACTER_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "COALESCE", SYM(COALESCE),0,0}, { "CONCAT", SYM(CONCAT),0,0}, { "CONCAT_WS", SYM(CONCAT_WS),0,0}, { "CONNECTION_ID", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, + { "CONTAINS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_contains)}, { "CONV", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)}, { "CONVERT", SYM(CONVERT_SYM),0,0}, { "COUNT", SYM(COUNT_SYM),0,0}, { "COS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, + { "CROSSES", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_crosses)}, { "CURDATE", SYM(CURDATE),0,0}, { "CURTIME", SYM(CURTIME),0,0}, { "DATE_ADD", SYM(DATE_ADD_INTERVAL),0,0}, @@ -420,9 +430,15 @@ static SYMBOL sql_functions[] = { { "DEGREES", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_degrees)}, { "DES_ENCRYPT", SYM(DES_ENCRYPT_SYM),0,0}, { "DES_DECRYPT", SYM(DES_DECRYPT_SYM),0,0}, + { "DIMENSION", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dimension)}, + { "DISJOINT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_disjoint)}, { "ELT", SYM(ELT_FUNC),0,0}, { "ENCODE", SYM(ENCODE_SYM),0,0}, { "ENCRYPT", SYM(ENCRYPT),0,0}, + { "ENDPOINT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_endpoint)}, + { "ENVELOPE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_envelope)}, + { "EQUALS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_equals)}, + { "EXTERIORRING", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_exteriorring)}, { "EXTRACT", SYM(EXTRACT_SYM),0,0}, { "EXP", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_exp)}, { "EXPORT_SET", SYM(EXPORT_SET),0,0}, @@ -434,6 +450,11 @@ static SYMBOL sql_functions[] = { { "FROM_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_from_days)}, { "FROM_UNIXTIME", SYM(FROM_UNIXTIME),0,0}, { "GET_LOCK", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_get_lock)}, + { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION),0,0}, + { "GEOMETRYN", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_geometryn)}, + { "GEOMETRYTYPE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_geometry_type)}, + { "GEOMFROMTEXT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_geometry_from_text)}, + { "GLENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_glength)}, { "GREATEST", SYM(GREATEST_SYM),0,0}, { "GROUP_UNIQUE_USERS", SYM(GROUP_UNIQUE_USERS),0,0}, { "HEX", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_hex)}, @@ -441,10 +462,16 @@ static SYMBOL sql_functions[] = { { "INET_ATON", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_aton)}, { "INET_NTOA", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_ntoa)}, { "INSTR", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_instr)}, + { "INTERIORRINGN", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_interiorringn)}, + { "INTERSECTS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_intersects)}, + { "ISCLOSED", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_isclosed)}, + { "ISEMPTY", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_isempty)}, { "ISNULL", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_isnull)}, + { "ISSIMPLE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_issimple)}, { "LCASE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)}, { "LEAST", SYM(LEAST_SYM),0,0}, { "LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, + { "LINESTRING", SYM(LINESTRING),0,0}, { "LOAD_FILE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_load_file)}, { "LOCATE", SYM(LOCATE),0,0}, { "LOG", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log)}, @@ -460,15 +487,25 @@ static SYMBOL sql_functions[] = { { "MID", SYM(SUBSTRING),0,0}, /* unireg function */ { "MIN", SYM(MIN_SYM),0,0}, { "MOD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_mod)}, + { "MULTILINESTRING", SYM(MULTILINESTRING),0,0}, + { "MULTIPOINT", SYM(MULTIPOINT),0,0}, + { "MULTIPOLYGON", SYM(MULTIPOLYGON),0,0}, { "MONTHNAME", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_monthname)}, { "NOW", SYM(NOW_SYM),0,0}, { "NULLIF", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_nullif)}, + { "NUMGEOMETRIES", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numgeometries)}, + { "NUMINTERIORRING", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numinteriorring)}, + { "NUMPOINTS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numpoints)}, { "OCTET_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, { "OCT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)}, { "ORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)}, + { "OVERLAPS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_overlaps)}, { "PERIOD_ADD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)}, { "PERIOD_DIFF", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_diff)}, { "PI", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_pi)}, + { "POINT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_point)}, + { "POINTN", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pointn)}, + { "POLYGON", SYM(POLYGON),0,0}, { "POSITION", SYM(POSITION_SYM),0,0}, { "POW", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)}, { "POWER", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)}, @@ -489,6 +526,7 @@ static SYMBOL sql_functions[] = { { "SOUNDEX", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_soundex)}, { "SPACE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_space)}, { "SQRT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sqrt)}, + { "STARTPOINT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_startpoint)}, { "STD", SYM(STD_SYM),0,0}, { "STDDEV", SYM(STD_SYM),0,0}, { "STRCMP", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)}, @@ -501,6 +539,7 @@ static SYMBOL sql_functions[] = { { "TIME_FORMAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)}, { "TIME_TO_SEC", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_time_to_sec)}, { "TO_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_to_days)}, + { "TOUCHES", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_touches)}, { "TRIM", SYM(TRIM),0,0}, { "UCASE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, { "UNIQUE_USERS", SYM(UNIQUE_USERS),0,0}, @@ -510,5 +549,9 @@ static SYMBOL sql_functions[] = { { "VERSION", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)}, { "WEEK", SYM(WEEK_SYM),0,0}, { "WEEKDAY", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)}, + { "WITHIN", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_within)}, + { "X", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_x)}, + { "Y", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_y)}, { "YEARWEEK", SYM(YEARWEEK),0,0} + }; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c3f4c91b718..34ee34ecc79 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -172,8 +172,9 @@ public: void store(uint length,char **min_key,uint min_key_flag, char **max_key, uint max_key_flag) { - if (!(min_flag & NO_MIN_RANGE) && - !(min_key_flag & (NO_MIN_RANGE | NEAR_MIN))) + if ((min_flag & GEOM_FLAG) || + (!(min_flag & NO_MIN_RANGE) && + !(min_key_flag & (NO_MIN_RANGE | NEAR_MIN)))) { if (maybe_null && *min_value) { @@ -659,6 +660,8 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, key_parts->null_bit= key_info->key_part[part].null_bit; if (key_parts->field->type() == FIELD_TYPE_BLOB) key_parts->part_length+=HA_KEY_BLOB_LENGTH; + key_parts->image_type = + (key_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW; } param.real_keynr[param.keys++]=idx; } @@ -676,6 +679,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, { SEL_ARG **key,**end,**best_key=0; + for (idx=0,key=tree->keys, end=key+param.keys ; key != end ; key++,idx++) @@ -1042,7 +1046,7 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, DBUG_RETURN(0); if (maybe_null) *str=0; // Not NULL - field->get_key_image(str+maybe_null,key_part->part_length); + field->get_key_image(str+maybe_null,key_part->part_length, key_part->image_type); if (!(tree=new SEL_ARG(field,str,str))) DBUG_RETURN(0); @@ -1067,6 +1071,41 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, case Item_func::GE_FUNC: tree->max_flag=NO_MAX_RANGE; break; + case Item_func::SP_EQUALS_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_EQUAL;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + case Item_func::SP_DISJOINT_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_DISJOINT;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + case Item_func::SP_INTERSECTS_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + case Item_func::SP_TOUCHES_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + + case Item_func::SP_CROSSES_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + case Item_func::SP_WITHIN_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_WITHIN;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + + case Item_func::SP_CONTAINS_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_CONTAIN;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + case Item_func::SP_OVERLAPS_FUNC: + tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512; + tree->max_flag=NO_MAX_RANGE; + break; + default: break; } @@ -2187,18 +2226,30 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, !memcmp(param->min_key,param->max_key,min_key_length)) tmp=1; // Max one record else + { + if(tmp_min_flag & GEOM_FLAG) + { + tmp=param->table->file-> + records_in_range((int) keynr,(byte*)(param->min_key + 1), + min_key_length, (ha_rkey_function)(tmp_min_flag ^ GEOM_FLAG), + (byte *)NullS,0,HA_READ_KEY_EXACT); + } + else + { tmp=param->table->file-> records_in_range((int) keynr, (byte*) (!min_key_length ? NullS : param->min_key), min_key_length, - (tmp_min_flag & NEAR_MIN ? - HA_READ_AFTER_KEY : HA_READ_KEY_EXACT), + tmp_min_flag & NEAR_MIN ? + HA_READ_AFTER_KEY : HA_READ_KEY_EXACT, (byte*) (!max_key_length ? NullS : param->max_key), max_key_length, (tmp_max_flag & NEAR_MAX ? HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY)); + } + } end: if (tmp == HA_POS_ERROR) // Impossible range return tmp; @@ -2294,19 +2345,24 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key, } } else - flag=key_tree->min_flag | key_tree->max_flag; + { + flag = (key_tree->min_flag & GEOM_FLAG) ? + key_tree->min_flag : key_tree->min_flag | key_tree->max_flag; + } /* Ensure that some part of min_key and max_key are used. If not, regard this as no lower/upper range */ - if (tmp_min_key != param->min_key) - flag&= ~NO_MIN_RANGE; - else - flag|= NO_MIN_RANGE; - if (tmp_max_key != param->max_key) - flag&= ~NO_MAX_RANGE; - else - flag|= NO_MAX_RANGE; - + if((flag & GEOM_FLAG) == 0) + { + if (tmp_min_key != param->min_key) + flag&= ~NO_MIN_RANGE; + else + flag|= NO_MIN_RANGE; + if (tmp_max_key != param->max_key) + flag&= ~NO_MAX_RANGE; + else + flag|= NO_MAX_RANGE; + } if (flag == 0) { uint length= (uint) (tmp_min_key - param->min_key); @@ -2439,13 +2495,19 @@ int QUICK_SELECT::get_next() int result; if (range) { // Already read through key - result=((range->flag & EQ_RANGE) ? +/* result=((range->flag & EQ_RANGE) ? file->index_next_same(record, (byte*) range->min_key, range->min_length) : file->index_next(record)); +*/ + result=((range->flag & (EQ_RANGE | GEOM_FLAG) ) ? + file->index_next_same(record, (byte*) range->min_key, + range->min_length) : + file->index_next(record)); + if (!result) { - if (!cmp_next(*it.ref())) + if ((range->flag & GEOM_FLAG) || !cmp_next(*it.ref())) DBUG_RETURN(0); } else if (result != HA_ERR_END_OF_FILE) @@ -2454,6 +2516,23 @@ int QUICK_SELECT::get_next() if (!(range=it++)) DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + + if(range->flag & GEOM_FLAG) + { + if ((result = file->index_read(record, + (byte*) (range->min_key + ((range->flag & GEOM_FLAG) > 0)), + range->min_length, + (ha_rkey_function)(range->flag ^ GEOM_FLAG)))) + + { + if (result != HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(result); + range=0; // Not found, to next range + continue; + } + DBUG_RETURN(0); + } + if (range->flag & NO_MIN_RANGE) // Read first record { int error; @@ -2464,13 +2543,14 @@ int QUICK_SELECT::get_next() range=0; // No matching records; go to next range continue; } - if ((result = file->index_read(record,(byte*) range->min_key, + if ((result = file->index_read(record, + (byte*) (range->min_key + ((range->flag & GEOM_FLAG) > 0)), range->min_length, - ((range->flag & NEAR_MIN) ? + (range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY: (range->flag & EQ_RANGE) ? HA_READ_KEY_EXACT : - HA_READ_KEY_OR_NEXT)))) + HA_READ_KEY_OR_NEXT))) { if (result != HA_ERR_KEY_NOT_FOUND) diff --git a/sql/opt_range.h b/sql/opt_range.h index 83eb10235ea..86df6121042 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -31,11 +31,14 @@ #define UNIQUE_RANGE 16 #define EQ_RANGE 32 #define NULL_RANGE 64 +#define GEOM_FLAG 128 + typedef struct st_key_part { - uint16 key,part,part_length; - uint8 null_bit; - Field *field; + uint16 key,part,part_length; + uint8 null_bit; + Field *field; + Field::imagetype image_type; } KEY_PART; class QUICK_RANGE :public Sql_alloc { diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 78878c40b37..b7b2552ef52 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -343,7 +343,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) // Save found constant if (part->null_bit) *key_ptr++= (byte) test(part->field->is_null()); - part->field->get_key_image((char*) key_ptr,part->length); + part->field->get_key_image((char*) key_ptr,part->length, Field::itRAW); key_ptr+=part->store_length - test(part->null_bit); left_length-=part->store_length; } diff --git a/sql/spatial.cc b/sql/spatial.cc new file mode 100644 index 00000000000..32942a70dc2 --- /dev/null +++ b/sql/spatial.cc @@ -0,0 +1,1435 @@ +#include "mysql_priv.h" + + +#define MAX_DIGITS_IN_DOUBLE 16 + +/***************************** GClassInfo *******************************/ + +#define IMPLEMENT_GEOM(class_name, type_id, name) \ +{ \ + (GF_InitFromText) &class_name::init_from_text, \ + (GF_GetDataAsText) &class_name::get_data_as_text, \ + (GF_GetDataSize) &class_name::get_data_size, \ + (GF_GetMBR) &class_name::get_mbr, \ + (GF_GetD) &class_name::get_x, \ + (GF_GetD) &class_name::get_y, \ + (GF_GetD) &class_name::length, \ + (GF_GetD) &class_name::area, \ + (GF_GetI) &class_name::is_closed, \ + (GF_GetUI) &class_name::num_interior_ring, \ + (GF_GetUI) &class_name::num_points, \ + (GF_GetUI) &class_name::num_geometries, \ + (GF_GetUI) &class_name::dimension, \ + (GF_GetWS) &class_name::start_point, \ + (GF_GetWS) &class_name::end_point, \ + (GF_GetWS) &class_name::exterior_ring, \ + (GF_GetWS) &class_name::centroid, \ + (GF_GetUIWS) &class_name::point_n, \ + (GF_GetUIWS) &class_name::interior_ring_n, \ + (GF_GetUIWS) &class_name::geometry_n, \ + class_name::type_id, \ + name, \ + NULL \ +}, + + +static Geometry::GClassInfo ci_collection[] = +{ + IMPLEMENT_GEOM(GPoint, wkbPoint, "POINT") + IMPLEMENT_GEOM(GLineString, wkbLineString, "LINESTRING") + IMPLEMENT_GEOM(GPolygon, wkbPolygon, "POLYGON") + IMPLEMENT_GEOM(GMultiPoint, wkbMultiPoint, "MULTIPOINT") + IMPLEMENT_GEOM(GMultiLineString, wkbMultiLineString, "MULTILINESTRING") + IMPLEMENT_GEOM(GMultiPolygon, wkbMultiPolygon, "MULTIPOLYGON") + IMPLEMENT_GEOM(GGeometryCollection, wkbGeometryCollection, "GEOMETRYCOLLECTION") +}; + +static Geometry::GClassInfo *ci_collection_end = ci_collection + sizeof(ci_collection); + +/***************************** Geometry *******************************/ + +Geometry::GClassInfo *Geometry::find_class(int type_id) +{ + for (GClassInfo *cur_rt = ci_collection; cur_rt < ci_collection_end; ++cur_rt) + { + if (cur_rt->m_type_id == type_id) + { + return cur_rt; + } + } + return NULL; +} + +Geometry::GClassInfo *Geometry::find_class(const char *name, size_t len) +{ + for (GClassInfo *cur_rt = ci_collection; + cur_rt < ci_collection_end; ++cur_rt) + { + if ((cur_rt->m_name[len] == 0) && + (strncmp(cur_rt->m_name, name, len) == 0)) + { + return cur_rt; + } + } + return NULL; +} + +int Geometry::create_from_wkb(const char *data, uint32 data_len) +{ + uint32 geom_type; + + if (data_len < 1+4) + return 1; + data += sizeof(char); + +//FIXME: check byte ordering + geom_type = uint4korr(data); + data += 4; + m_vmt = find_class(geom_type); + if (!m_vmt) return -1; + m_data = data; + m_data_end = data + data_len; + return 0; +} + +int Geometry::create_from_wkt(GTextReadStream *trs, String *wkt, int init_stream) +{ + int name_len; + const char *name = trs->get_next_word(&name_len); + if (!name) + { + trs->set_error_msg("Geometry name expected"); + return -1; + } + if (!(m_vmt = find_class(name, name_len))) + return -1; + if (wkt->reserve(1 + 4, 512)) + return 1; + wkt->q_append((char)wkbNDR); + wkt->q_append((uint32)get_class_info()->m_type_id); + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return -1; + } + if (init_from_text(trs, wkt)) return 1; + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return -1; + } + if (init_stream) + { + init_from_wkb(wkt->ptr(), wkt->length()); + shift_wkb_header(); + } + return 0; +} + +int Geometry::envelope(String *result) const +{ + MBR mbr; + + get_mbr(&mbr); + + if (result->reserve(1+4*3+sizeof(double)*10)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPolygon); + result->q_append((uint32)1); + result->q_append((uint32)5); + result->q_append(mbr.xmin); + result->q_append(mbr.ymin); + result->q_append(mbr.xmax); + result->q_append(mbr.ymin); + result->q_append(mbr.xmax); + result->q_append(mbr.ymax); + result->q_append(mbr.xmin); + result->q_append(mbr.ymax); + result->q_append(mbr.xmin); + result->q_append(mbr.ymin); + + return 0; +} + +/***************************** Point *******************************/ + +size_t GPoint::get_data_size() const +{ + return POINT_DATA_SIZE; +} + +int GPoint::init_from_text(GTextReadStream *trs, String *wkb) +{ + double x, y; + if (wkb->reserve(sizeof(double)*2)) + return 1; + if (trs->get_next_number(&x)) + return 1; + if (trs->get_next_number(&y)) + return 1; + wkb->q_append(x); + wkb->q_append(y); + + return 0; +} + +int GPoint::get_data_as_text(String *txt) const +{ + double x, y; + if (get_xy(&x, &y)) + return 1; + if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 1)) + return 1; + txt->qs_append(x); + txt->qs_append(' '); + txt->qs_append(y); + return 0; +} + +int GPoint::get_mbr(MBR *mbr) const +{ + double x, y; + if (get_xy(&x, &y)) + return 1; + mbr->add_xy(x, y); + return 0; +} + +/***************************** LineString *******************************/ + +size_t GLineString::get_data_size() const +{ + uint32 n_points = uint4korr(m_data); + + return 4 + n_points*POINT_DATA_SIZE; +} + +int GLineString::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_points = 0; + int np_pos = wkb->length(); + GPoint p; + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_points); + + for (;;) + { + if (p.init_from_text(trs, wkb)) + return 1; + ++n_points; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else break; + } + + if (n_points<2) + { + trs->set_error_msg("Too few points in LINESTRING"); + return 1; + } + + wkb->WriteAtPosition(np_pos, n_points); + + return 0; +} + +int GLineString::get_data_as_text(String *txt) const +{ + uint32 n_points; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + + n_points = uint4korr(data); + data += 4; + + if (no_data(data, sizeof(double) * 2 * n_points)) + return 1; + + if (txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + for (; n_points>0; --n_points) + { + double x, y; + float8get(x, data); + data += sizeof(double); + float8get(y, data); + data += sizeof(double); + txt->qs_append(x); + txt->qs_append(' '); + txt->qs_append(y); + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GLineString::get_mbr(MBR *mbr) const +{ + uint32 n_points; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + + n_points = uint4korr(data); + data += 4; + + if (no_data(data, sizeof(double) * 2 * n_points)) + return 1; + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + + return 0; +} + +int GLineString::length(double *len) const +{ + uint32 n_points; + double prev_x, prev_y; + const char *data = m_data; + + *len=0; + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + + if (no_data(data, sizeof(double) * 2 * n_points)) + return 1; + + --n_points; + float8get(prev_x, data); + data += 8; + float8get(prev_y, data); + data += 8; + + for (; n_points>0; --n_points) + { + double x, y; + float8get(x, data); + data += 8; + float8get(y, data); + data += 8; + *len+=sqrt(pow(prev_x-x,2)+pow(prev_y-y,2)); + prev_x=x; + prev_y=y; + } + return 0; +} + +int GLineString::is_closed(int *closed) const + +{ + uint32 n_points; + double x1, y1, x2, y2; + + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + float8get(x1, data); + data += 8; + float8get(y1, data); + data += 8 + (n_points-2)*POINT_DATA_SIZE; + float8get(x2, data); + data += 8; + float8get(y2, data); + + *closed=(x1==x2)&&(y1==y2); + + return 0; +} + +int GLineString::num_points(uint32 *n_points) const +{ + *n_points = uint4korr(m_data); + return 0; +} + +int GLineString::start_point(String *result) const +{ + const char *data = m_data + 4; + if (no_data(data, 8+8)) + return 1; + + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append((double *)data); + result->q_append((double *)(data + 8)); + + return 0; +} + +int GLineString::end_point(String *result) const +{ + const char *data = m_data; + uint32 n_points; + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + + data += 4 + (n_points-1)*POINT_DATA_SIZE; + + if (no_data(data, 8+8)) + return 1; + + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append((double *)data); + result->q_append((double *)(data + 8)); + + return 0; +} + + +int GLineString::point_n(uint32 num, String *result) const +{ + const char *data = m_data; + uint32 n_points; + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + + if ((uint32)(num-1) >= n_points) // really means (num > n_points || num < 1) + return 1; + + data += 4 + (num - 1)*POINT_DATA_SIZE; + + if (no_data(data, 8+8)) + return 1; + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append((double *)data); + result->q_append((double *)(data + 8)); + + return 0; +} + +/***************************** Polygon *******************************/ + +size_t GPolygon::get_data_size() const +{ + uint32 n_linear_rings = 0; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + + n_linear_rings = uint4korr(data); + data += 4; + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + data += 4 + uint4korr(data)*POINT_DATA_SIZE; + } + return data - m_data; +} + +int GPolygon::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_linear_rings = 0; + int lr_pos = wkb->length(); + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_linear_rings); + + for (;;) + { + GLineString ls; + size_t ls_pos=wkb->length(); + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return 1; + } + if (ls.init_from_text(trs, wkb)) + return 1; + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return 1; + } + ls.init_from_wkb(wkb->ptr()+ls_pos, wkb->length()-ls_pos); + int closed; + ls.is_closed(&closed); + if (!closed) + { + trs->set_error_msg("POLYGON's linear ring isn't closed"); + return 1; + } + ++n_linear_rings; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(lr_pos, n_linear_rings); + return 0; +} + +int GPolygon::get_data_as_text(String *txt) const +{ + uint32 n_linear_rings; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + + n_linear_rings = uint4korr(data); + data += 4; + + for (; n_linear_rings>0; --n_linear_rings) + { + if(no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + + if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + txt->qs_append('('); + for (; n_points>0; --n_points) + { + txt->qs_append((double *)data); + txt->qs_append(' '); + txt->qs_append((double *)(data + 8)); + txt->qs_append(','); + + data += 8+8; + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GPolygon::get_mbr(MBR *mbr) const +{ + uint32 n_linear_rings; + + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_linear_rings = uint4korr(data); + data += 4; + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + } + return 0; +} + +int GPolygon::area(double *ar) const +{ + uint32 n_linear_rings; + double result = -1.0; + + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_linear_rings = uint4korr(data); + data += 4; + for (; n_linear_rings>0; --n_linear_rings) + { + double prev_x, prev_y; + double lr_area=0; + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + if (no_data(data, (8+8) * n_points)) + return 1; + float8get(prev_x, data+4); + float8get(prev_y, data+(4+8)); + data += (4+8+8); + + --n_points; + for (; n_points>0; --n_points) + { + double x, y; + float8get(x, data); + float8get(y, data + 8); + lr_area+=(prev_x+x)*(prev_y-y); + prev_x=x; + prev_y=y; + data += (8+8); + } + lr_area=fabs(lr_area)/2; + if(result==-1) result=lr_area; + else result-=lr_area; + } + *ar=fabs(result); + return 0; +} + + +int GPolygon::exterior_ring(String *result) const +{ + uint32 n_points; + const char *data = m_data + 4; // skip n_linerings + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + if (no_data(data, n_points * POINT_DATA_SIZE)) + return 1; + + if (result->reserve(1+4+4+ n_points * POINT_DATA_SIZE)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbLineString); + result->q_append(n_points); + result->q_append(data, n_points * POINT_DATA_SIZE); + + return 0; +} + +int GPolygon::num_interior_ring(uint32 *n_int_rings) const +{ + const char *data = m_data; + if (no_data(data, 4)) + return 1; + *n_int_rings = uint4korr(data); + --(*n_int_rings); + + return 0; +} + +int GPolygon::interior_ring_n(uint32 num, String *result) const +{ + const char *data = m_data; + uint32 n_linear_rings; + uint32 n_points; + + if (no_data(data, 4)) + return 1; + + n_linear_rings = uint4korr(data); + data += 4; + if ((num >= n_linear_rings) || (num < 1)) + return -1; + + for (; num > 0; --num) + { + if (no_data(data, 4)) + return 1; + data += 4 + uint4korr(data) * POINT_DATA_SIZE; + } + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + int points_size = n_points * POINT_DATA_SIZE; + data += 4; + if (no_data(data, points_size)) + return 1; + + if (result->reserve(1+4+4+ points_size)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbLineString); + result->q_append(n_points); + result->q_append(data, points_size); + + return 0; +} + +int GPolygon::centroid_xy(double *x, double *y) const +{ + uint32 n_linear_rings; + uint32 i; + double res_area, res_cx, res_cy; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + n_linear_rings = uint4korr(data); + data += 4; + + for(i = 0; i < n_linear_rings; ++i) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + double prev_x, prev_y; + double cur_area = 0; + double cur_cx = 0; + double cur_cy = 0; + + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + float8get(prev_x, data); + float8get(prev_y, data+8); + data += (8+8); + + uint32 n = n_points - 1; + for (; n > 0; --n) + { + double x, y; + float8get(x, data); + float8get(y, data + 8); + + cur_area += (prev_x + x) * (prev_y - y); + cur_cx += x; + cur_cy += y; + prev_x = x; + prev_y = y; + data += (8+8); + } + cur_area = fabs(cur_area) / 2; + cur_cx = cur_cx / (n_points - 1); + cur_cy = cur_cy / (n_points - 1); + if(i) + { + double d_area = res_area - cur_area; + if (d_area <= 0) + return 1; + res_cx = (res_area * res_cx - cur_area * cur_cx) / d_area; + res_cy = (res_area * res_cy - cur_area * cur_cy) / d_area; + } + else + { + res_area = cur_area; + res_cx = cur_cx; + res_cy = cur_cy; + } + } + + *x = res_cx; + *y = res_cy; + + return 0; +} + +int GPolygon::centroid(String *result) const +{ + double x, y; + + this->centroid_xy(&x, &y); + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append(x); + result->q_append(y); + + return 0; +} + + +/***************************** MultiPoint *******************************/ + +size_t GMultiPoint::get_data_size() const +{ + return 4 + uint4korr(m_data)*(POINT_DATA_SIZE + WKB_HEADER_SIZE); +} + +int GMultiPoint::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_points = 0; + int np_pos = wkb->length(); + GPoint p; + + if (wkb->reserve(4, 512)) + return 1; + wkb->q_append((uint32)n_points); + + for (;;) + { + if (wkb->reserve(1+4, 512)) + return 1; + wkb->q_append((char)wkbNDR); + wkb->q_append((uint32)wkbPoint); + if (p.init_from_text(trs, wkb)) + return 1; + ++n_points; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(np_pos, n_points); + + return 0; +} + +int GMultiPoint::get_data_as_text(String *txt) const +{ + uint32 n_points; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + + n_points = uint4korr(data); + data += 4; + if (no_data(data, n_points * (8+8+WKB_HEADER_SIZE))) + return 1; + + if (txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + + for (; n_points>0; --n_points) + { + txt->qs_append((double *)(data + WKB_HEADER_SIZE)); + txt->qs_append(' '); + txt->qs_append((double *)(data + (8 + WKB_HEADER_SIZE))); + txt->qs_append(','); + data += 8+8+WKB_HEADER_SIZE; + } + txt->length(txt->length()-1); + return 0; +} + +int GMultiPoint::get_mbr(MBR *mbr) const +{ + uint32 n_points; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + if (no_data(data, n_points * (8+8+WKB_HEADER_SIZE))) + return 1; + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)(data + WKB_HEADER_SIZE), + (double *)(data + 8 + WKB_HEADER_SIZE)); + data += (8+8+WKB_HEADER_SIZE); + } + return 0; +} + +/***************************** MultiLineString *******************************/ + +size_t GMultiLineString::get_data_size() const +{ + uint32 n_line_strings = 0; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + + for (; n_line_strings>0; --n_line_strings) + { + if (no_data(data, WKB_HEADER_SIZE + 4)) + return 1; + data += WKB_HEADER_SIZE + 4 + uint4korr(data + WKB_HEADER_SIZE) * POINT_DATA_SIZE; + } + return data - m_data; +} + +int GMultiLineString::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_line_strings = 0; + int ls_pos = wkb->length(); + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_line_strings); + + for (;;) + { + GLineString ls; + + if (wkb->reserve(1+4, 512)) + return 1; + wkb->q_append((char)wkbNDR); + wkb->q_append((uint32)wkbLineString); + + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return 1; + } + if (ls.init_from_text(trs, wkb)) + return 1; + + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return 1; + } + ++n_line_strings; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(ls_pos, n_line_strings); + + return 0; +} + +int GMultiLineString::get_data_as_text(String *txt) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + for (; n_line_strings>0; --n_line_strings) + { + if (no_data(data, (WKB_HEADER_SIZE + 4))) + return 1; + uint32 n_points = uint4korr(data + WKB_HEADER_SIZE); + data += WKB_HEADER_SIZE + 4; + if (no_data(data, n_points * (8+8))) + return 1; + + if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + txt->qs_append('('); + for (; n_points>0; --n_points) + { + txt->qs_append((double *)data); + txt->qs_append(' '); + txt->qs_append((double *)(data + 8)); + txt->qs_append(','); + data += 8+8; + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GMultiLineString::get_mbr(MBR *mbr) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + + for (; n_line_strings>0; --n_line_strings) + { + if (no_data(data, WKB_HEADER_SIZE + 4)) + return 1; + uint32 n_points = uint4korr(data + WKB_HEADER_SIZE); + data += 4+WKB_HEADER_SIZE; + if (no_data(data, (8+8)*n_points)) + return 1; + + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + } + return 0; +} + +int GMultiLineString::length(double *len) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + *len=0; + for (; n_line_strings>0; --n_line_strings) + { + double ls_len; + GLineString ls; + data += WKB_HEADER_SIZE; + ls.init_from_wkb(data, m_data_end - data); + if (ls.length(&ls_len)) + return 1; + *len+=ls_len; + data += ls.get_data_size(); + } + return 0; +} + +int GMultiLineString::is_closed(int *closed) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 1)) + return 1; + n_line_strings = uint4korr(data); + data += 4 + WKB_HEADER_SIZE; + for (; n_line_strings>0; --n_line_strings) + { + GLineString ls; + ls.init_from_wkb(data, m_data_end - data); + if (ls.is_closed(closed)) + return 1; + if (!*closed) + return 0; + data += ls.get_data_size() + WKB_HEADER_SIZE; + } + return 0; +} + +/***************************** MultiPolygon *******************************/ + +size_t GMultiPolygon::get_data_size() const +{ + uint32 n_polygons; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + if (no_data(data, 4 + WKB_HEADER_SIZE)) + return 1; + uint32 n_linear_rings = uint4korr(data + WKB_HEADER_SIZE); + data += 4 + WKB_HEADER_SIZE; + + for (; n_linear_rings > 0; --n_linear_rings) + { + data += 4 + uint4korr(data) * POINT_DATA_SIZE; + } + } + return data - m_data; +} + +int GMultiPolygon::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_polygons = 0; + int np_pos = wkb->length(); + GPolygon p; + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_polygons); + + for (;;) + { + if (wkb->reserve(1+4, 512)) + return 1; + wkb->q_append((char)wkbNDR); + wkb->q_append((uint32)wkbPolygon); + + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return 1; + } + if (p.init_from_text(trs, wkb)) + return 1; + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return 1; + } + ++n_polygons; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(np_pos, n_polygons); + return 0; +} + +int GMultiPolygon::get_data_as_text(String *txt) const +{ + uint32 n_polygons; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + if (no_data(data, 4 + WKB_HEADER_SIZE)) + return 1; + data += WKB_HEADER_SIZE; + uint32 n_linear_rings = uint4korr(data); + data += 4; + + if (txt->reserve(1, 512)) + return 1; + txt->q_append('('); + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8)*n_points)) return 1; + + if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points, + 512)) return 1; + txt->qs_append('('); + for (; n_points>0; --n_points) + { + txt->qs_append((double *)data); + txt->qs_append(' '); + txt->qs_append((double *)(data + 8)); + txt->qs_append(','); + data += 8+8; + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GMultiPolygon::get_mbr(MBR *mbr) const +{ + uint32 n_polygons; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + if (no_data(data, 4+WKB_HEADER_SIZE)) + return 1; + uint32 n_linear_rings = uint4korr(data + WKB_HEADER_SIZE); + data += WKB_HEADER_SIZE + 4; + + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8)*n_points)) + return 1; + + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + } + } + return 0; +} + + +int GMultiPolygon::area(double *ar) const +{ + uint32 n_polygons; + const char *data = m_data; + double result = 0; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + double p_area; + + GPolygon p; + data += WKB_HEADER_SIZE; + p.init_from_wkb(data, m_data_end - data); + if (p.area(&p_area)) + return 1; + result += p_area; + data += p.get_data_size(); + } + *ar = result; + return 0; +} + +int GMultiPolygon::centroid(String *result) const +{ + uint32 n_polygons; + uint i; + GPolygon p; + double res_area, res_cx, res_cy; + double cur_area, cur_cx, cur_cy; + + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (i = 0; i < n_polygons; ++i) + { + data += WKB_HEADER_SIZE; + p.init_from_wkb(data, m_data_end - data); + if (p.area(&cur_area)) + return 1; + + if (p.centroid_xy(&cur_cx, &cur_cy)) + return 1; + + if(i) + { + double sum_area = res_area + cur_area; + res_cx = (res_area * res_cx + cur_area * cur_cx) / sum_area; + res_cy = (res_area * res_cy + cur_area * cur_cy) / sum_area; + } + else + { + res_area = cur_area; + res_cx = cur_cx; + res_cy = cur_cy; + } + + data += p.get_data_size(); + } + + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append(res_cx); + result->q_append(res_cy); + + return 0; +} + +/***************************** GeometryCollection *******************************/ + +size_t GGeometryCollection::get_data_size() const +{ + uint32 n_objects; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + for (; n_objects>0; --n_objects) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + Geometry geom; + + if (geom.init(wkb_type)) + return 0; + + geom.init_from_wkb(data, m_data_end - data); + size_t object_size=geom.get_data_size(); + data += object_size; + } + return data - m_data; +} + +int GGeometryCollection::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_objects = 0; + int no_pos = wkb->length(); + Geometry g; + + if (wkb->reserve(4, 512)) + return 1; + wkb->q_append((uint32)n_objects); + + for (;;) + { + if (g.create_from_wkt(trs, wkb)) + return 1; + + if (g.get_class_info()->m_type_id==wkbGeometryCollection) + { + trs->set_error_msg("Unexpected GEOMETRYCOLLECTION"); + return 1; + } + ++n_objects; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else break; + } + wkb->WriteAtPosition(no_pos, n_objects); + + return 0; +} + +int GGeometryCollection::get_data_as_text(String *txt) const +{ + uint32 n_objects; + const char *data = m_data; + Geometry geom; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + for (; n_objects>0; --n_objects) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + if (geom.as_wkt(txt)) + return 1; + data += geom.get_data_size(); + txt->reserve(1, 512); + txt->q_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GGeometryCollection::get_mbr(MBR *mbr) const +{ + uint32 n_objects; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + for (; n_objects>0; --n_objects) + { + if(no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + Geometry geom; + + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + geom.get_mbr(mbr); + data += geom.get_data_size(); + } + return 0; +} + +int GGeometryCollection::num_geometries(uint32 *num) const +{ + *num = uint4korr(m_data); + return 0; +} + +int GGeometryCollection::geometry_n(uint32 num, String *result) const +{ + const char *data = m_data; + uint32 n_objects; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + if ((num > n_objects) || (num < 1)) + { + return -1; + } + for (; num > 0; --num) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + Geometry geom; + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + if (num == 1) + { + if (result->reserve(1+4+geom.get_data_size())) + return 1; + result->q_append((char)wkbNDR); + result->q_append((uint32)wkb_type); + result->q_append(data, geom.get_data_size()); + break; + } + else + { + data += geom.get_data_size(); + } + } + return 0; +} + +int GGeometryCollection::dimension(uint32 *dim) const +{ + uint32 n_objects; + *dim = 0; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + for (; n_objects > 0; --n_objects) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + uint32 d; + + Geometry geom; + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + if (geom.dimension(&d)) + return 1; + + if (d > *dim) + *dim = d; + data += geom.get_data_size(); + } + return 0; +} + +/***************************** /objects *******************************/ diff --git a/sql/spatial.h b/sql/spatial.h new file mode 100644 index 00000000000..09e8722a85e --- /dev/null +++ b/sql/spatial.h @@ -0,0 +1,476 @@ +#ifndef _spatial_h +#define _spatial_h + +#include "gstream.h" + +const int POINT_DATA_SIZE = 8+8; +const int WKB_HEADER_SIZE = 1+4; + +struct stPoint2D +{ + double x; + double y; +}; + +struct stLinearRing +{ + size_t n_points; + stPoint2D points; +}; + +/***************************** MBR *******************************/ + +struct MBR +{ + MBR() + { + xmin=DBL_MAX; + ymin=DBL_MAX; + xmax=-DBL_MAX; + ymax=-DBL_MAX; + } + + MBR(const double &_xmin, const double &_ymin, const double &_xmax, const double &_ymax) + { + xmin=_xmin; + ymin=_ymin; + xmax=_xmax; + ymax=_ymax; + } + + MBR(const stPoint2D &min, const stPoint2D &max) + { + xmin=min.x; + ymin=min.y; + xmax=max.x; + ymax=max.y; + } + + double xmin; + double ymin; + double xmax; + double ymax; + + void add_xy(double x, double y) + { /* Not using "else" for proper one point MBR calculation */ + if (x<xmin) + { + xmin=x; + } + if (x>xmax) + { + xmax=x; + } + if (y<ymin) + { + ymin=y; + } + if (y>ymax) + { + ymax=y; + } + } + + void add_xy(double *px, double *py) + { /* Not using "else" for proper one point MBR calculation */ + double x, y; + float8get(x, px); + float8get(y, py); + if (x<xmin) + { + xmin=x; + } + if (x>xmax) + { + xmax=x; + } + if (y<ymin) + { + ymin=y; + } + if (y>ymax) + { + ymax=y; + } + } + + void add_mbr(const MBR *mbr) + { + if (mbr->xmin<xmin) + { + xmin=mbr->xmin; + } + if (mbr->xmax>xmax) + { + xmax=mbr->xmax; + } + if (mbr->ymin<ymin) + { + ymin=mbr->ymin; + } + if (mbr->ymax>ymax) + { + ymax=mbr->ymax; + } + } + + int equals(const MBR *mbr) + { + return (mbr->xmin==xmin)&&(mbr->ymin==ymin)&&(mbr->xmax==xmax)&&(mbr->ymax==ymax); + } + + int disjoint(const MBR *mbr) + { + return (mbr->xmin>xmax)||(mbr->ymin>ymax)||(mbr->xmax<xmin)||(mbr->ymax<ymin); + } + + int intersects(const MBR *mbr) + { + return !disjoint(mbr); + } + + int touches(const MBR *mbr) + { + return (((mbr->xmin==xmax) || (mbr->xmax==xmin)) && + ((mbr->ymin>=ymin) && (mbr->ymin<=ymax) || + (mbr->ymax>=ymin) && (mbr->ymax<=ymax))) || + (((mbr->ymin==ymax) || (mbr->ymax==ymin)) && + ((mbr->xmin>=xmin) && (mbr->xmin<=xmax) || + (mbr->xmax>=xmin)&&(mbr->xmax<=xmax))); + } + + int within(const MBR *mbr) + { + return (mbr->xmin<=xmin) && (mbr->ymin<=ymin) && + (mbr->xmax>=xmax) && (mbr->ymax>=ymax); + } + + int contains(const MBR *mbr) + { + return (mbr->xmin>=xmin) && (mbr->ymin>=ymin) && + (mbr->xmax<=xmax) && (mbr->ymax<=ymax); + } + + bool inner_point(double x, double y) const + { + return (xmin<x) && (xmax>x) && (ymin<y) && (ymax>x); + } + + int overlaps(const MBR *mbr) + { + int lb = mbr->inner_point(xmin, ymin); + int rb = mbr->inner_point(xmax, ymin); + int rt = mbr->inner_point(xmax, ymax); + int lt = mbr->inner_point(xmin, ymax); + + int a = lb+rb+rt+lt; + return (a>0) && (a<4) && (!within(mbr)); + } +}; + + +/***************************** Geometry *******************************/ + +class Geometry; + +typedef int (Geometry::*GF_InitFromText)(GTextReadStream *, String *); +typedef int (Geometry::*GF_GetDataAsText)(String *) const; +typedef size_t (Geometry::*GF_GetDataSize)() const; +typedef int (Geometry::*GF_GetMBR)(MBR *) const; + +typedef int (Geometry::*GF_GetD)(double *) const; +typedef int (Geometry::*GF_GetI)(int *) const; +typedef int (Geometry::*GF_GetUI)(uint32 *) const; +typedef int (Geometry::*GF_GetWS)(String *) const; +typedef int (Geometry::*GF_GetUIWS)(uint32, String *) const; + +#define GEOM_METHOD_PRESENT(geom_obj, method)\ + (geom_obj.m_vmt->method != &Geometry::method) + +class Geometry +{ +public: + enum wkbType + { + wkbPoint = 1, + wkbLineString = 2, + wkbPolygon = 3, + wkbMultiPoint = 4, + wkbMultiLineString = 5, + wkbMultiPolygon = 6, + wkbGeometryCollection = 7 + }; + enum wkbByteOrder + { + wkbXDR = 0, /* Big Endian */ + wkbNDR = 1 /* Little Endian */ + }; + + + class GClassInfo + { + public: + GF_InitFromText init_from_text; + GF_GetDataAsText get_data_as_text; + GF_GetDataSize get_data_size; + GF_GetMBR get_mbr; + GF_GetD get_x; + GF_GetD get_y; + GF_GetD length; + GF_GetD area; + + GF_GetI is_closed; + + GF_GetUI num_interior_ring; + GF_GetUI num_points; + GF_GetUI num_geometries; + GF_GetUI dimension; + + GF_GetWS start_point; + GF_GetWS end_point; + GF_GetWS exterior_ring; + GF_GetWS centroid; + + GF_GetUIWS point_n; + GF_GetUIWS interior_ring_n; + GF_GetUIWS geometry_n; + + int m_type_id; + const char *m_name; + GClassInfo *m_next_rt; + }; + GClassInfo *m_vmt; + + const GClassInfo *get_class_info() const { return m_vmt; } + size_t get_data_size() const { return (this->*m_vmt->get_data_size)(); } + + int init_from_text(GTextReadStream *trs, String *wkb) + { return (this->*m_vmt->init_from_text)(trs, wkb); } + + int get_data_as_text(String *txt) const + { return (this->*m_vmt->get_data_as_text)(txt); } + + int get_mbr(MBR *mbr) const { return (this->*m_vmt->get_mbr)(mbr); } + int dimension(uint32 *dim) const + { return (this->*m_vmt->dimension)(dim); } + + int get_x(double *x) const { return (this->*m_vmt->get_x)(x); } + int get_y(double *y) const { return (this->*m_vmt->get_y)(y); } + int length(double *len) const { return (this->*m_vmt->length)(len); } + int area(double *ar) const { return (this->*m_vmt->area)(ar); } + + int is_closed(int *closed) const { return (this->*m_vmt->is_closed)(closed); } + + int num_interior_ring(uint32 *n_int_rings) const + { return (this->*m_vmt->num_interior_ring)(n_int_rings); } + int num_points(uint32 *n_points) const + { return (this->*m_vmt->num_points)(n_points); } + + int num_geometries(uint32 *num) const + { return (this->*m_vmt->num_geometries)(num); } + + int start_point(String *point) const + { return (this->*m_vmt->start_point)(point); } + int end_point(String *point) const + { return (this->*m_vmt->end_point)(point); } + int exterior_ring(String *ring) const + { return (this->*m_vmt->exterior_ring)(ring); } + int centroid(String *point) const + { return (this->*m_vmt->centroid)(point); } + + int point_n(uint32 num, String *result) const + { return (this->*m_vmt->point_n)(num, result); } + int interior_ring_n(uint32 num, String *result) const + { return (this->*m_vmt->interior_ring_n)(num, result); } + int geometry_n(uint32 num, String *result) const + { return (this->*m_vmt->geometry_n)(num, result); } + +public: + int create_from_wkb(const char *data, uint32 data_len); + int create_from_wkt(GTextReadStream *trs, String *wkt, int init_stream=1); + int init(int type_id) + { + m_vmt = find_class(type_id); + return !m_vmt; + } + int new_geometry(const char *name, size_t len) + { + m_vmt = find_class(name, len); + return !m_vmt; + } + + int as_wkt(String *wkt) const + { + if(wkt->reserve(strlen(get_class_info()->m_name) + 2, 512)) + return 1; + wkt->qs_append(get_class_info()->m_name); + wkt->qs_append('('); + if(get_data_as_text(wkt)) + return 1; + wkt->qs_append(')'); + return 0; + } + + void init_from_wkb(const char *data, uint32 data_len) + { + m_data = data; + m_data_end = data + data_len; + } + + void shift_wkb_header() + { + m_data += WKB_HEADER_SIZE; + } + + int envelope(String *result) const; + +protected: + static GClassInfo *find_class(int type_id); + static GClassInfo *find_class(const char *name, size_t len); + + bool no_data(const char *cur_data, uint32 data_amount) const + { + return cur_data + data_amount > m_data_end; + } + + const char *m_data; + const char *m_data_end; +}; + +/***************************** Point *******************************/ + +class GPoint: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + + int get_xy(double *x, double *y) const + { + const char *data = m_data; + if(no_data(data, sizeof(double)) * 2) return 1; + float8get(*x, data); + float8get(*y, data + sizeof(double)); + return 0; + } + + int get_x(double *x) const + { + if(no_data(m_data, sizeof(double))) return 1; + float8get(*x, m_data); + return 0; + } + + int get_y(double *y) const + { + const char *data = m_data; + if(no_data(data, sizeof(double)) * 2) return 1; + float8get(*y, data + sizeof(double)); + return 0; + } + + int dimension(uint32 *dim) const { *dim = 0; return 0; } +}; + +/***************************** LineString *******************************/ + +class GLineString: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + + int length(double *len) const; + int is_closed(int *closed) const; + int num_points(uint32 *n_points) const; + int start_point(String *point) const; + int end_point(String *point) const; + int point_n(uint32 n, String *result) const; + int dimension(uint32 *dim) const { *dim = 1; return 0; } +// IsRing +}; + +/***************************** Polygon *******************************/ + +class GPolygon: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + + int area(double *ar) const; + int exterior_ring(String *result) const; + int num_interior_ring(uint32 *n_int_rings) const; + int interior_ring_n(uint32 num, String *result) const; + int centroid_xy(double *x, double *y) const; + int centroid(String *result) const; + int dimension(uint32 *dim) const { *dim = 2; return 0; } +// PointOnSurface +}; + +/***************************** MultiPoint *******************************/ + +class GMultiPoint: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + int dimension(uint32 *dim) const { *dim = 0; return 0; } +}; + +/***************************** MultiLineString *******************************/ + +class GMultiLineString: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + + int length(double *len) const; + int is_closed(int *closed) const; + int dimension(uint32 *dim) const { *dim = 1; return 0; } +}; + +/***************************** MultiPolygon *******************************/ + +class GMultiPolygon: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + + int area(double *ar) const; + int centroid(String *result) const; + int dimension(uint32 *dim) const { *dim = 2; return 0; } +// PointOnSurface +}; + +/***************************** GeometryCollection *******************************/ + +class GGeometryCollection: public Geometry +{ +public: + size_t get_data_size() const; + int init_from_text(GTextReadStream *trs, String *wkb); + int get_data_as_text(String *txt) const; + int get_mbr(MBR *mbr) const; + + int num_geometries(uint32 *num) const; + int geometry_n(uint32 num, String *result) const; + + int dimension(uint32 *dim) const; +}; + +#endif diff --git a/sql/sql_class.h b/sql/sql_class.h index a1423cfcdf1..355987c259e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -198,13 +198,18 @@ public: class Key :public Sql_alloc { public: - enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT }; + enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL }; enum Keytype type; + enum ha_key_alg alg; // +BAR List<key_part_spec> columns; const char *Name; Key(enum Keytype type_par,const char *name_arg,List<key_part_spec> &cols) :type(type_par), columns(cols),Name(name_arg) {} + + Key(enum Keytype type_par, enum ha_key_alg alg_par, const char *name_arg,List<key_part_spec> &cols) + :type(type_par),alg(alg_par), columns(cols),Name(name_arg){} //+BAR + ~Key() {} const char *name() { return Name; } }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 131266a11d6..52541ffe50f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -880,11 +880,17 @@ store_create_info(THD *thd, TABLE *table, String *packet) packet->append("UNIQUE ", 7); else if (key_info->flags & HA_FULLTEXT) packet->append("FULLTEXT ", 9); + else if (key_info->flags & HA_SPATIAL) + packet->append("SPATIAL ", 8); packet->append("KEY ", 4); if (!found_primary) append_identifier(thd,packet,key_info->name); + // +BAR: send USING only in non-default case: non-spatial rtree + if((key_info->key_alg == HA_KEY_ALG_RTREE) && !(key_info->flags & HA_SPATIAL)) + packet->append(" USING RTREE",12); + packet->append(" (", 2); for (uint j=0 ; j < key_info->key_parts ; j++,key_part++) diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 8fe84947ac2..f0cfd590072 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -459,6 +459,44 @@ bool String::replace(uint32 offset,uint32 arg_length,const String &to) return FALSE; } +// added by Holyfoot for "geometry" needs +int String::reserve(uint32 space_needed, uint32 grow_by) +{ + if (Alloced_length < str_length + space_needed) + { + if (realloc(Alloced_length + max(space_needed, grow_by) - 1)) + return TRUE; + } + return FALSE; +} + +void String::qs_append(const char *str) +{ + int len = strlen(str); + memcpy(Ptr + str_length, str, len + 1); + str_length += len; +} + +void String::qs_append(double d) +{ + char *buff = Ptr + str_length; + sprintf(buff,"%.14g", d); + str_length += strlen(buff); +} + +void String::qs_append(double *d) +{ + double ld; + float8get(ld, d); + qs_append(ld); +} + +void String::qs_append(const char &c) +{ + Ptr[str_length] = c; + str_length += sizeof(c); +} + int sortcmp(const String *x,const String *y) { @@ -805,3 +843,5 @@ int wild_compare(String &match,String &wild, char escape) DBUG_RETURN(wild_compare(match.ptr(),match.ptr()+match.length(), wild.ptr(), wild.ptr()+wild.length(),escape)); } + + diff --git a/sql/sql_string.h b/sql/sql_string.h index ad7455ecbf1..ac579cd752d 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -188,4 +188,49 @@ public: friend int wild_compare(String &match,String &wild,char escape); uint32 numchars(); int charpos(int i,uint32 offset=0); + +// added by Holyfoot for "geometry" needs + int reserve(uint32 space_needed) + { + return realloc(str_length + space_needed); + } + int reserve(uint32 space_needed, uint32 grow_by); + +// these append operations do NOT check alloced memory +// q_*** methods writes values of parameters itself +// qs_*** methods writes string representation of value + void q_append(const char &c) + { + Ptr[str_length++] = c; + } + void q_append(const uint32 &n) + { + int4store(Ptr + str_length, n); + str_length += 4; + } + void q_append(double d) + { + float8store(Ptr + str_length, d); + str_length += 8; + } + void q_append(double *d) + { + float8store(Ptr + str_length, *d); + str_length += 8; + } + void q_append(const char *data, uint32 data_len) + { + memcpy(Ptr + str_length, data, data_len); + str_length += data_len; + } + + void WriteAtPosition(int position, uint32 value) + { + int4store(Ptr + position,value); + } + + void qs_append(const char *str); + void qs_append(double d); + void qs_append(double *d); + void qs_append(const char &c); }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 0335c6f49cf..fa1879b33ff 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -438,8 +438,21 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, uint key_length=0; key_part_spec *column; - key_info->flags= (key->type == Key::MULTIPLE) ? 0 : - (key->type == Key::FULLTEXT) ? HA_FULLTEXT : HA_NOSAME; + switch(key->type){ + case Key::MULTIPLE: + key_info->flags = 0; + break; + case Key::FULLTEXT: + key_info->flags = HA_FULLTEXT; + break; + case Key::SPATIAL: + key_info->flags = HA_SPATIAL; + break; + default: + key_info->flags = HA_NOSAME; + } + + key_info->key_alg = key->alg; key_info->key_parts=(uint8) key->columns.elements; key_info->key_part=key_part_info; key_info->usable_key_parts= key_number; @@ -452,7 +465,31 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } } - + /* + Make SPATIAL to be RTREE by default + SPATIAL only on BLOB or at least BINARY, this + actually should be replaced by special GEOM type + in near future when new frm file is ready + checking for proper key parts number: + */ + + if(key_info->flags == HA_SPATIAL){ + if(key_info->key_parts!=1){ + my_printf_error(ER_WRONG_ARGUMENTS, + ER(ER_WRONG_ARGUMENTS),MYF(0),"SPATIAL INDEX"); + DBUG_RETURN(-1); + } + }else + { + if(key_info->key_alg == HA_KEY_ALG_RTREE){ + if((key_info->key_parts&1)==1){ + my_printf_error(ER_WRONG_ARGUMENTS, + ER(ER_WRONG_ARGUMENTS),MYF(0),"RTREE INDEX"); + DBUG_RETURN(-1); + } + } + } + List_iterator<key_part_spec> cols(key->columns); for (uint column_nr=0 ; (column=cols++) ; column_nr++) { @@ -480,6 +517,14 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { if (key->type == Key::FULLTEXT) column->length=1; /* ft-code ignores it anyway :-) */ + else if (key->type == Key::SPATIAL) + { + /* + BAR: 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case + Lately we'll extend this code to support more dimensions + */ + column->length=4*sizeof(double); + } else { my_printf_error(ER_BLOB_KEY_WITHOUT_LENGTH, @@ -1471,11 +1516,13 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, key_part_length)); } if (key_parts.elements) - key_list.push_back(new Key(key_info->flags & HA_NOSAME ? + key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL : + (key_info->flags & HA_NOSAME ? (!my_strcasecmp(key_name, "PRIMARY") ? Key::PRIMARY : Key::UNIQUE) : (key_info->flags & HA_FULLTEXT ? - Key::FULLTEXT : Key::MULTIPLE), + Key::FULLTEXT : Key::MULTIPLE)), + key_info->key_alg, key_name,key_parts)); } key_it.rewind(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 742c39ce901..62b1e1841ff 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -54,6 +54,7 @@ inline Item *or_or_concat(Item* A, Item* B) List<Item> *item_list; List<String> *string_list; Key::Keytype key_type; + enum ha_key_alg key_alg; enum db_type db_type; enum row_type row_type; enum ha_rkey_function ha_rkey_mode; @@ -154,6 +155,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token BOOL_SYM %token BOOLEAN_SYM %token BOTH +%token BTREE_SYM %token BY %token CACHE_SYM %token CASCADE @@ -200,6 +202,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token GREATEST_SYM %token GROUP %token HAVING +%token HASH_SYM %token HEAP_SYM %token HEX_NUM %token HIGH_PRIORITY @@ -291,10 +294,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token ROWS_SYM %token ROW_FORMAT_SYM %token ROW_SYM +%token RTREE_SYM %token SET %token SERIALIZABLE_SYM %token SESSION_SYM %token SHUTDOWN +%token SPATIAL_SYM %token SQL_CACHE_SYM %token SQL_NO_CACHE_SYM %token SSL_SYM @@ -344,6 +349,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token ENUM %token FAST_SYM %token FLOAT_SYM +%token GEOM_SYM %token INT_SYM %token LIMIT %token LONGBLOB @@ -401,6 +407,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token FORMAT_SYM %token FOR_SYM %token FROM_UNIXTIME +%token GEOMETRYCOLLECTION %token GROUP_UNIQUE_USERS %token HOUR_MINUTE_SYM %token HOUR_SECOND_SYM @@ -412,6 +419,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token INTERVAL_SYM %token LAST_INSERT_ID %token LEFT +%token LINESTRING %token LOCATE %token MAKE_SET_SYM %token MINUTE_SECOND_SYM @@ -419,8 +427,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MODE_SYM %token MODIFY_SYM %token MONTH_SYM +%token MULTILINESTRING +%token MULTIPOINT +%token MULTIPOLYGON %token NOW_SYM %token PASSWORD +%token POLYGON %token POSITION_SYM %token PROCEDURE %token RAND @@ -527,6 +539,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <key_type> key_type opt_unique_or_fulltext +%type <key_alg> + key_alg opt_btree_or_rtree + %type <string_list> key_usage_list @@ -713,21 +728,22 @@ create: } create2 - | CREATE opt_unique_or_fulltext INDEX ident ON table_ident + | CREATE opt_unique_or_fulltext INDEX ident key_alg ON table_ident { LEX *lex=Lex; lex->sql_command= SQLCOM_CREATE_INDEX; - if (!add_table_to_list($6,NULL,1)) + if (!add_table_to_list($7,NULL,1)) YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); lex->change=NullS; } - '(' key_list ')' + '(' key_list ')' { LEX *lex=Lex; - lex->key_list.push_back(new Key($2,$4.str,lex->col_list)); + + lex->key_list.push_back(new Key($2,$5,$4.str,lex->col_list)); lex->col_list.empty(); } | CREATE DATABASE opt_if_not_exists ident @@ -874,10 +890,10 @@ field_list_item: { Lex->col_list.empty(); /* Alloced by sql_alloc */ } - | key_type opt_ident '(' key_list ')' + | key_type opt_ident key_alg '(' key_list ')' { LEX *lex=Lex; - lex->key_list.push_back(new Key($1,$2,lex->col_list)); + lex->key_list.push_back(new Key($1,$3,$2,lex->col_list)); lex->col_list.empty(); /* Alloced by sql_alloc */ } | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references @@ -942,6 +958,8 @@ type: $$=FIELD_TYPE_TINY_BLOB; } | BLOB_SYM { Lex->type|=BINARY_FLAG; $$=FIELD_TYPE_BLOB; } + | GEOM_SYM { Lex->type|=BINARY_FLAG; + $$=FIELD_TYPE_GEOMETRY; } | MEDIUMBLOB { Lex->type|=BINARY_FLAG; $$=FIELD_TYPE_MEDIUM_BLOB; } | LONGBLOB { Lex->type|=BINARY_FLAG; @@ -1083,6 +1101,8 @@ key_type: | key_or_index { $$= Key::MULTIPLE; } | FULLTEXT_SYM { $$= Key::FULLTEXT; } | FULLTEXT_SYM key_or_index { $$= Key::FULLTEXT; } + | SPATIAL_SYM { $$= Key::SPATIAL; } + | SPATIAL_SYM key_or_index { $$= Key::SPATIAL; } | opt_constraint UNIQUE_SYM { $$= Key::UNIQUE; } | opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; } @@ -1099,6 +1119,16 @@ opt_unique_or_fulltext: /* empty */ { $$= Key::MULTIPLE; } | UNIQUE_SYM { $$= Key::UNIQUE; } | FULLTEXT_SYM { $$= Key::FULLTEXT; } + | SPATIAL_SYM { $$= Key::SPATIAL; } + +key_alg: + /* empty */ { $$= HA_KEY_ALG_BTREE; } + | USING opt_btree_or_rtree { $$= $2 } + +opt_btree_or_rtree: + BTREE_SYM { $$= HA_KEY_ALG_BTREE; } + | RTREE_SYM { $$= HA_KEY_ALG_RTREE; } + | HASH_SYM { $$= HA_KEY_ALG_HASH; } key_list: key_list ',' key_part order_dir { Lex->col_list.push_back($3); } @@ -1684,6 +1714,10 @@ simple_expr: } | FIELD_FUNC '(' expr ',' expr_list ')' { $$= new Item_func_field($3, *$5); } + | GEOMETRYCOLLECTION '(' expr_list ')' + { $$= new Item_func_spatial_collection(* $3, + Geometry::wkbGeometryCollection, + Geometry::wkbPoint); } | HOUR_SYM '(' expr ')' { $$= new Item_func_hour($3); } | IF '(' expr ',' expr ',' expr ')' @@ -1708,6 +1742,9 @@ simple_expr: } | LEFT '(' expr ',' expr ')' { $$= new Item_func_left($3,$5); } + | LINESTRING '(' expr_list ')' + { $$= new Item_func_spatial_collection(* $3, + Geometry::wkbLineString, Geometry::wkbPoint); } | LOCATE '(' expr ',' expr ')' { $$= new Item_func_locate($5,$3); } | LOCATE '(' expr ',' expr ',' expr ')' @@ -1720,6 +1757,15 @@ simple_expr: { $$= new Item_func_minute($3); } | MONTH_SYM '(' expr ')' { $$= new Item_func_month($3); } + | MULTILINESTRING '(' expr_list ')' + { $$= new Item_func_spatial_collection(* $3, + Geometry::wkbMultiLineString, Geometry::wkbLineString); } + | MULTIPOINT '(' expr_list ')' + { $$= new Item_func_spatial_collection(* $3, + Geometry::wkbMultiPoint, Geometry::wkbPoint); } + | MULTIPOLYGON '(' expr_list ')' + { $$= new Item_func_spatial_collection(* $3, + Geometry::wkbMultiPolygon, Geometry::wkbPolygon ); } | NOW_SYM optional_braces { $$= new Item_func_now(); current_thd->safe_to_cache_query=0;} | NOW_SYM '(' expr ')' @@ -1728,6 +1774,9 @@ simple_expr: { $$= new Item_func_password($3); } + | POLYGON '(' expr_list ')' + { $$= new Item_func_spatial_collection(* $3, + Geometry::wkbPolygon, Geometry::wkbLineString); } | POSITION_SYM '(' no_in_expr IN_SYM expr ')' { $$ = new Item_func_locate($5,$3); } | RAND '(' expr ')' diff --git a/sql/structs.h b/sql/structs.h index 9e577128c8d..3ee4680f4a4 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -64,6 +64,7 @@ typedef struct st_key_part_info { /* Info about a key part */ typedef struct st_key { uint key_length; /* Tot length of key */ uint flags; /* dupp key and pack flags */ + enum ha_key_alg key_alg; /* +BAR Algorithm BTREE or RTREE */ uint key_parts; /* How many key_parts */ uint extra_length; uint usable_key_parts; /* Should normally be = key_parts */ diff --git a/sql/table.cc b/sql/table.cc index 9aae9e17e5a..9e6283d6ad4 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -153,7 +153,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, { keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME; keyinfo->key_length= (uint) uint2korr(strpos+1); - keyinfo->key_parts= (uint) strpos[3]; strpos+=4; + keyinfo->key_parts= (uint) strpos[3]; + strpos+=4; + keyinfo->key_part= key_part; keyinfo->rec_per_key= rec_per_key; for (j=keyinfo->key_parts ; j-- ; key_part++) @@ -395,6 +397,26 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, } } + keyinfo->key_alg=HA_KEY_ALG_BTREE; // BAR : btree by default + +#define BAR_DIRTY_HACK +#ifdef BAR_DIRTY_HACK + // BAR FIXME: Dirty hack while waiting for new .frm format + switch(keyinfo->name[0]){ + case 'R': + keyinfo->key_alg=HA_KEY_ALG_RTREE; + break; + case 'S': + keyinfo->key_alg = HA_KEY_ALG_RTREE; + keyinfo->flags |= HA_SPATIAL; + break; + case 'B': + default: + keyinfo->key_alg=HA_KEY_ALG_BTREE; + break; + } +#endif + for (i=0 ; i < keyinfo->key_parts ; key_part++,i++) { if (new_field_pack_flag <= 1) |