diff options
author | Alexey Botchkov <holyfoot@askmonty.org> | 2014-12-03 14:07:43 +0400 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2014-12-04 16:09:34 +0100 |
commit | c4cb15e87ba8cf4f1e7fd8e7cda5c88098ecd515 (patch) | |
tree | 042b437f82a3368aa8c9d5b3c894182f75ec93bf | |
parent | c4655cf862b24fe040280e38cb71d3b5d25f049b (diff) | |
download | mariadb-git-c4cb15e87ba8cf4f1e7fd8e7cda5c88098ecd515.tar.gz |
MDEV-60 Support for Spatial Reference systems for the GIS data.
The GEOMETRY field metadata is stored in the FRM file.
SRID for a spatial column now can be stored, it was added to the CREATE TABLE syntax,
so the AddGeometryData() stored procedure is now possible. Script adding the required Add/DropGeometryColumn sp-s added.
-rw-r--r-- | mysql-test/r/gis.result | 16 | ||||
-rw-r--r-- | mysql-test/t/gis.test | 5 | ||||
-rw-r--r-- | scripts/maria_add_gis_sp.sql | 46 | ||||
-rw-r--r-- | sql/field.cc | 99 | ||||
-rw-r--r-- | sql/field.h | 22 | ||||
-rw-r--r-- | sql/lex.h | 1 | ||||
-rw-r--r-- | sql/sp_head.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 24 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 15 | ||||
-rw-r--r-- | sql/table.cc | 21 | ||||
-rw-r--r-- | sql/unireg.cc | 19 | ||||
-rw-r--r-- | sql/unireg.h | 1 |
13 files changed, 243 insertions, 30 deletions
diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index 433a67746e3..f28d9e7b659 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -1716,8 +1716,16 @@ create table t1(g GEOMETRY, pt POINT); create table t2(g LINESTRING, pl POLYGON); select * from information_schema.geometry_columns; F_TABLE_CATALOG F_TABLE_SCHEMA F_TABLE_NAME F_GEOMETRY_COLUMN G_TABLE_CATALOG G_TABLE_SCHEMA G_TABLE_NAME G_GEOMETRY_COLUMN STORAGE_TYPE GEOMETRY_TYPE COORD_DIMENSION MAX_PPR SRID -def test t1 def test t1 g 1 0 2 0 0 0 -def test t1 def test t1 pt 1 1 2 0 0 0 -def test t2 def test t2 g 1 2 2 0 0 0 -def test t2 def test t2 pl 1 3 2 0 0 0 +def test t1 def test t1 g 1 0 2 0 0 +def test t1 def test t1 pt 1 1 2 0 0 +def test t2 def test t2 g 1 2 2 0 0 +def test t2 def test t2 pl 1 3 2 0 0 drop table t1, t2; +10.1 tests +create table t1(g GEOMETRY(9,4) REF_SYSTEM_ID=101, pt POINT(8,2), pg GEOMETRY REF_SYSTEM_ID=102); +SELECT SRID from information_schema.geometry_columns WHERE G_TABLE_NAME='t1'; +SRID +101 +0 +102 +drop table t1; diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index 2cc9f0c930b..2679874ddff 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -1453,3 +1453,8 @@ create table t2(g LINESTRING, pl POLYGON); select * from information_schema.geometry_columns; drop table t1, t2; +--echo 10.1 tests +create table t1(g GEOMETRY(9,4) REF_SYSTEM_ID=101, pt POINT(8,2), pg GEOMETRY REF_SYSTEM_ID=102); +SELECT SRID from information_schema.geometry_columns WHERE G_TABLE_NAME='t1'; +drop table t1; + diff --git a/scripts/maria_add_gis_sp.sql b/scripts/maria_add_gis_sp.sql new file mode 100644 index 00000000000..fbaef70032f --- /dev/null +++ b/scripts/maria_add_gis_sp.sql @@ -0,0 +1,46 @@ +-- Copyright (C) 2014 MariaDB Ab. +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; version 2 of the License. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This part created stored procedures required by the OpenGIS standards. + +# Proc privilege is needed to run it. + +SET sql_mode=''; + +DROP PROCEDURE IF EXISTS AddGeometryColumn; +DROP PROCEDURE IF EXISTS DropGeometryColumn; + +delimiter | + +CREATE PROCEDURE AddGeometryColumn(catalog varchar(64), t_schema varchar(64), + t_name varchar(64), geometry_column varchar(64), t_srid int) +begin + set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' ADD ', geometry_column,' GEOMETRY REF_SYSTEM_ID=', t_srid); + PREPARE ls from @qwe; + execute ls; + deallocate prepare ls; +end| + +CREATE PROCEDURE DropGeometryColumn(catalog varchar(64), t_schema varchar(64), + t_name varchar(64), geometry_column varchar(64)) +begin + set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' DROP ', geometry_column); + PREPARE ls from @qwe; + execute ls; + deallocate prepare ls; +end| + +delimiter ; + diff --git a/sql/field.cc b/sql/field.cc index a9bd1fe2dc0..f18b571a0d3 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7819,6 +7819,101 @@ uint Field_blob::is_equal(Create_field *new_field) #ifdef HAVE_SPATIAL +/* Values 1-40 reserved for 1-byte options, + 41-80 for 2-byte options, + 81-120 for 4-byte options, + 121-160 for 8-byte options, + other - varied length in next 1-3 bytes. +*/ +enum extra2_gis_field_options { + FIELDGEOM_END=0, + FIELDGEOM_STORAGE_MODEL=1, + FIELDGEOM_PRECISION=2, + FIELDGEOM_SCALE=3, + FIELDGEOM_SRID=81, +}; + + +uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields) +{ + uint image_size= 0; + List_iterator<Create_field> it(create_fields); + Create_field *field; + while ((field= it++)) + { + if (field->sql_type != MYSQL_TYPE_GEOMETRY) + continue; + if (buff) + { + uchar *cbuf= buff + image_size; + + cbuf[0]= FIELDGEOM_STORAGE_MODEL; + cbuf[1]= (uchar) Field_geom::GEOM_STORAGE_WKB; + + cbuf[2]= FIELDGEOM_PRECISION; + cbuf[3]= (uchar) field->length; + + cbuf[4]= FIELDGEOM_SCALE; + cbuf[5]= (uchar) field->decimals; + + cbuf[6]= FIELDGEOM_SRID; + int4store(cbuf + 7, ((uint32) field->srid)); + + cbuf[11]= FIELDGEOM_END; + } + image_size+= 12; + } + + return image_size; +} + + +uint gis_field_options_read(const uchar *buf, uint buf_len, + Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid) +{ + const uchar *buf_end= buf + buf_len; + const uchar *cbuf= buf; + int option_id; + + *precision= *scale= *srid= 0; + *st_type= Field_geom::GEOM_STORAGE_WKB; + + while (cbuf < buf_end) + { + switch ((option_id= *(cbuf++))) + { + case FIELDGEOM_STORAGE_MODEL: + *st_type= (Field_geom::storage_type) cbuf[0]; + break; + case FIELDGEOM_PRECISION: + *precision= cbuf[0]; + break; + case FIELDGEOM_SCALE: + *scale= cbuf[0]; + break; + case FIELDGEOM_SRID: + *srid= uint4korr(cbuf); + break; + case FIELDGEOM_END: + goto end_of_record; + } + if (option_id > 0 and option_id <= 40) + cbuf+= 1; + else if (option_id > 40 and option_id <= 80) + cbuf+= 2; + else if (option_id > 80 and option_id <= 120) + cbuf+= 4; + else if (option_id > 120 and option_id <= 160) + cbuf+= 8; + else /* > 160 and <=255 */ + cbuf+= cbuf[0] ? 1 + cbuf[0] : 3 + uint2korr(cbuf+1); + } + +end_of_record: + return cbuf - buf; +} + + void Field_geom::sql_type(String &res) const { @@ -9595,7 +9690,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uint pack_flag, enum_field_types field_type, CHARSET_INFO *field_charset, - Field::geometry_type geom_type, + Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, const char *field_name) @@ -9661,7 +9756,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, status_var_increment(current_thd->status_var.feature_gis); return new Field_geom(ptr,null_pos,null_bit, unireg_check, field_name, share, - pack_length, geom_type); + pack_length, geom_type, srid); } #endif if (f_is_blob(pack_flag)) diff --git a/sql/field.h b/sql/field.h index 135e326b6a0..a2360c24804 100644 --- a/sql/field.h +++ b/sql/field.h @@ -2542,18 +2542,22 @@ private: class Field_geom :public Field_blob { public: enum geometry_type geom_type; + uint srid; + uint precision; + enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1}; + enum storage_type storage; Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, TABLE_SHARE *share, uint blob_pack_length, - enum geometry_type geom_type_arg) + enum geometry_type geom_type_arg, uint field_srid) :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, share, blob_pack_length, &my_charset_bin) - { geom_type= geom_type_arg; } + { geom_type= geom_type_arg; srid= field_srid; } Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, TABLE_SHARE *share, enum geometry_type geom_type_arg) :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin) - { geom_type= geom_type_arg; } + { geom_type= geom_type_arg; srid= 0; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; } enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; } bool match_collation_to_optimize_range() const { return false; } @@ -2571,8 +2575,13 @@ public: int reset(void) { return Field_blob::reset() || !maybe_null(); } geometry_type get_geometry_type() { return geom_type; }; - uint get_srid() { return 0; } + uint get_srid() { return srid; } }; + +uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields); +uint gis_field_options_read(const uchar *buf, uint buf_len, + Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid); + #endif /*HAVE_SPATIAL*/ @@ -2853,6 +2862,7 @@ public: // Used only for UCS2 intervals List<String> interval_list; CHARSET_INFO *charset; + uint32 srid; Field::geometry_type geom_type; Field *field; // For alter table engine_option_value *option_list; @@ -2877,7 +2887,7 @@ public: bool stored_in_db; Create_field() :after(0), pack_length(0), key_length(0), interval(0), - field(0), option_list(NULL), option_struct(NULL), + srid(0), field(0), option_list(NULL), option_struct(NULL), create_if_not_exists(false), stored_in_db(true) { interval_list.empty(); @@ -2986,7 +2996,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, enum_field_types field_type, CHARSET_INFO *cs, - Field::geometry_type geom_type, + Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, const char *field_name); uint pack_length_to_packflag(uint type); diff --git a/sql/lex.h b/sql/lex.h index 16eacec770f..e0f4ea58306 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -547,6 +547,7 @@ static SYMBOL symbols[] = { { "SOURCE", SYM(SOURCE_SYM)}, { "SPATIAL", SYM(SPATIAL_SYM)}, { "SPECIFIC", SYM(SPECIFIC_SYM)}, + { "REF_SYSTEM_ID", SYM(REF_SYSTEM_ID_SYM)}, { "SQL", SYM(SQL_SYM)}, { "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM)}, { "SQLSTATE", SYM(SQLSTATE_SYM)}, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 9f42e83d7f2..100cc3b0450 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -877,7 +877,7 @@ sp_head::create_result_field(uint field_max_length, const char *field_name, m_return_field_def.pack_flag, m_return_field_def.sql_type, m_return_field_def.charset, - m_return_field_def.geom_type, + m_return_field_def.geom_type, m_return_field_def.srid, Field::NONE, /* unreg check */ m_return_field_def.interval, field_name ? field_name : (const char *) m_name.str); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d81202ea18d..4474a42b5e6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16673,7 +16673,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list) (uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0), f_maybe_null(cdef->pack_flag) ? 1 : 0, cdef->pack_flag, cdef->sql_type, cdef->charset, - cdef->geom_type, cdef->unireg_check, + cdef->geom_type, cdef->srid, cdef->unireg_check, cdef->interval, cdef->field_name); if (!*field) goto error; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 09be25c327d..b2fa01a4fae 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -396,23 +396,23 @@ static int get_geometry_column_record(THD *thd, TABLE_LIST *tables, /*F_TABLE_NAME*/ table->field[2]->store(table_name->str, table_name->length, cs); /*G_TABLE_CATALOG*/ - table->field[3]->store(STRING_WITH_LEN("def"), cs); + table->field[4]->store(STRING_WITH_LEN("def"), cs); /*G_TABLE_SCHEMA*/ - table->field[4]->store(db_name->str, db_name->length, cs); + table->field[5]->store(db_name->str, db_name->length, cs); /*G_TABLE_NAME*/ - table->field[5]->store(table_name->str, table_name->length, cs); + table->field[6]->store(table_name->str, table_name->length, cs); /*G_GEOMETRY_COLUMN*/ - table->field[6]->store(field->field_name, strlen(field->field_name), cs); + table->field[7]->store(field->field_name, strlen(field->field_name), cs); /*STORAGE_TYPE*/ - table->field[7]->store(1LL, TRUE); /*Always 1 (binary implementation)*/ + table->field[8]->store(1LL, TRUE); /*Always 1 (binary implementation)*/ /*GEOMETRY_TYPE*/ - table->field[8]->store((longlong) (fg->get_geometry_type()), TRUE); + table->field[9]->store((longlong) (fg->get_geometry_type()), TRUE); /*COORD_DIMENSION*/ - table->field[9]->store(2LL, TRUE); + table->field[10]->store(2LL, TRUE); /*MAX_PPR*/ - table->field[10]->set_null(); + table->field[11]->set_null(); /*SRID*/ - table->field[11]->store((longlong) (fg->get_srid()), TRUE); + table->field[12]->store((longlong) (fg->get_srid()), TRUE); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); @@ -5550,7 +5550,7 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, field= make_field(&share, (uchar*) 0, field_def->length, (uchar*) "", 0, field_def->pack_flag, field_def->sql_type, field_def->charset, - field_def->geom_type, Field::NONE, + field_def->geom_type, field_def->srid, Field::NONE, field_def->interval, ""); field->table= &tbl; @@ -5603,7 +5603,7 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, field= make_field(&share, (uchar*) 0, field_def->length, (uchar*) "", 0, field_def->pack_flag, field_def->sql_type, field_def->charset, - field_def->geom_type, Field::NONE, + field_def->geom_type, field_def->srid, Field::NONE, field_def->interval, spvar->name.str); field->table= &tbl; @@ -5702,7 +5702,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, field= make_field(&share, (uchar*) 0, field_def->length, (uchar*) "", 0, field_def->pack_flag, field_def->sql_type, field_def->charset, - field_def->geom_type, Field::NONE, + field_def->geom_type, field_def->srid, Field::NONE, field_def->interval, ""); field->table= &tbl; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ea2a3492e43..a4cd8701e09 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1023,7 +1023,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); Currently there are 164 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 164 +%expect 165 /* Comments for TOKENS. @@ -1568,6 +1568,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SQL_SMALL_RESULT %token SQL_SYM /* SQL-2003-R */ %token SQL_THREAD +%token REF_SYSTEM_ID_SYM %token SSL_SYM %token STARTING %token STARTS_SYM @@ -6352,7 +6353,7 @@ field_type: Lex->charset=&my_charset_bin; $$=MYSQL_TYPE_BLOB; } - | spatial_type + | spatial_type float_options srid_option { #ifdef HAVE_SPATIAL Lex->charset=&my_charset_bin; @@ -6467,6 +6468,16 @@ real_type: { $$=MYSQL_TYPE_DOUBLE; } ; +srid_option: + /* empty */ + { Lex->last_field->srid= 0; } + | + REF_SYSTEM_ID_SYM EQ NUM + { + Lex->last_field->srid=atoi($3.str); + } + ; + float_options: /* empty */ { Lex->dec=Lex->length= (char*)0; } diff --git a/sql/table.cc b/sql/table.cc index 50d331c49b3..90b880417cc 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -910,6 +910,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint vcol_screen_length, UNINIT_VAR(options_len); char *vcol_screen_pos; const uchar *options= 0; + uint UNINIT_VAR(gis_options_len); + const uchar *gis_options= 0; KEY first_keyinfo; uint len; uint ext_key_parts= 0; @@ -988,6 +990,16 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #endif break; + case EXTRA2_GIS: +#ifdef HAVE_SPATIAL + { + if (gis_options) + goto err; + gis_options= extra2; + gis_options_len= length; + } +#endif /*HAVE_SPATIAL*/ + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -1433,6 +1445,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, LEX_STRING comment; Virtual_column_info *vcol_info= 0; bool fld_stored_in_db= TRUE; + uint gis_length, gis_decimals, srid; if (new_frm_ver >= 3) { @@ -1449,8 +1462,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (field_type == MYSQL_TYPE_GEOMETRY) { #ifdef HAVE_SPATIAL + uint gis_opt_read; + Field_geom::storage_type st_type; geom_type= (Field::geometry_type) strpos[14]; charset= &my_charset_bin; + gis_opt_read= gis_field_options_read(gis_options, gis_options_len, + &st_type, &gis_length, &gis_decimals, &srid); + gis_options+= gis_opt_read; + gis_options_len-= gis_opt_read; #else goto err; #endif @@ -1611,7 +1630,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, pack_flag, field_type, charset, - geom_type, + geom_type, srid, (Field::utype) MTYP_TYPENR(unireg_type), (interval_nr ? share->intervals+interval_nr-1 : diff --git a/sql/unireg.cc b/sql/unireg.cc index 4087511bc49..7e4a64189d9 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -108,6 +108,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, ulong key_buff_length; ulong filepos, data_offset; uint options_len; + uint gis_extra2_len= 0; uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE]; const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0); int error; @@ -153,6 +154,9 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, options_len= engine_table_options_frm_length(create_info->option_list, create_fields, keys, key_info); +#ifdef HAVE_SPATIAL + gis_extra2_len= gis_field_options_image(NULL, create_fields); +#endif /*HAVE_SPATIAL*/ DBUG_PRINT("info", ("Options length: %u", options_len)); if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN, @@ -197,6 +201,10 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (part_info) extra2_size+= 1 + 1 + hton_name(part_info->default_engine_type)->length; + if (gis_extra2_len) + extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len; + + key_buff_length= uint4korr(fileinfo+47); frm.length= FRM_HEADER_SIZE; // fileinfo; @@ -241,6 +249,15 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, create_fields, keys, key_info); } +#ifdef HAVE_SPATIAL + if (gis_extra2_len) + { + *pos= EXTRA2_GIS; + pos= extra2_write_len(pos+1, gis_extra2_len); + pos+= gis_field_options_image(pos, create_fields); + } +#endif /*HAVE_SPATIAL*/ + int4store(pos, filepos); // end of the extra2 segment pos+= 4; @@ -913,7 +930,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, field->pack_flag, field->sql_type, field->charset, - field->geom_type, + field->geom_type, field->srid, field->unireg_check, field->save_interval ? field->save_interval : field->interval, diff --git a/sql/unireg.h b/sql/unireg.h index 2d51aa39fd4..25335d11e4a 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -185,6 +185,7 @@ enum extra2_frm_value_type { EXTRA2_TABLEDEF_VERSION=0, EXTRA2_DEFAULT_PART_ENGINE=1, + EXTRA2_GIS=2, #define EXTRA2_ENGINE_IMPORTANT 128 |