diff --git a/configure.in b/configure.in index 94bc85557df..cf1b4b0cd32 100644 --- a/configure.in +++ b/configure.in @@ -2516,9 +2516,9 @@ linked_client_targets="linked_libmysql_sources" if test "$THREAD_SAFE_CLIENT" = "no" then - sql_client_dirs="strings regex mysys extra libmysql client" + sql_client_dirs="strings regex mysys dbug extra libmysql client" else - sql_client_dirs="strings regex mysys extra libmysql libmysql_r client" + sql_client_dirs="strings regex mysys dbug extra libmysql libmysql_r client" linked_client_targets="$linked_client_targets linked_libmysql_r_sources" AC_CONFIG_FILES(libmysql_r/Makefile) AC_DEFINE([THREAD_SAFE_CLIENT], [1], [Should be client be thread safe]) diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index 37614cc1e1f..d45d6f8b16f 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -592,8 +592,8 @@ static uint copy_decode_table(uint16 *to_pos, uint offset, uint16 *decode_table) { uint prev_offset; - prev_offset= offset; DBUG_ENTER("copy_decode_table"); + prev_offset= offset; /* Descent on the left side. */ if (!(*decode_table & IS_CHAR)) diff --git a/mysql-test/r/ndb_lock.result b/mysql-test/r/ndb_lock.result index 2c212b9cfef..d5875cb4d47 100644 --- a/mysql-test/r/ndb_lock.result +++ b/mysql-test/r/ndb_lock.result @@ -87,11 +87,27 @@ x y z rollback; commit; begin; +select * from t1 where y = 'one' or y = 'three' for update; +x y z +# # # +# # # +begin; +select * from t1 where x = 2 for update; +x y z +2 two 2 +select * from t1 where x = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; select * from t1 where y = 'one' or y = 'three' order by x for update; x y z 1 one 1 3 three 3 begin; +select * from t1 where x = 2 for update; +x y z +2 two 2 select * from t1 where x = 1 for update; ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; @@ -124,6 +140,22 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; +select * from t1 where y = 'one' or y = 'three' lock in share mode; +x y z +# # # +# # # +begin; +select * from t1 where y = 'one' lock in share mode; +x y z +1 one 1 +select * from t1 where x = 2 for update; +x y z +2 two 2 +select * from t1 where x = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; x y z 1 one 1 @@ -132,6 +164,9 @@ begin; select * from t1 where y = 'one' lock in share mode; x y z 1 one 1 +select * from t1 where x = 2 for update; +x y z +2 two 2 select * from t1 where x = 1 for update; ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 000a9317b8d..21d8ba05acc 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1055,6 +1055,65 @@ EXECUTE stmt USING @a; 0 0 DEALLOCATE PREPARE stmt; DROP TABLE t1; +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1 (i INT); +PREPARE st_19182 +FROM "CREATE TABLE t2 (i INT, j INT, KEY (i), KEY(j)) SELECT i FROM t1"; +EXECUTE st_19182; +DESC t2; +Field Type Null Key Default Extra +j int(11) YES MUL NULL +i int(11) YES MUL NULL +DROP TABLE t2; +EXECUTE st_19182; +DESC t2; +Field Type Null Key Default Extra +j int(11) YES MUL NULL +i int(11) YES MUL NULL +DEALLOCATE PREPARE st_19182; +DROP TABLE t2, t1; +drop database if exists mysqltest; +drop table if exists t1, t2; +create database mysqltest character set utf8; +prepare stmt1 from "create table mysqltest.t1 (c char(10))"; +prepare stmt2 from "create table mysqltest.t2 select 'test'"; +execute stmt1; +execute stmt2; +show create table mysqltest.t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` char(10) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8 +show create table mysqltest.t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `test` varchar(4) character set latin1 NOT NULL default '' +) ENGINE=MyISAM DEFAULT CHARSET=utf8 +drop table mysqltest.t1; +drop table mysqltest.t2; +alter database mysqltest character set latin1; +execute stmt1; +execute stmt2; +show create table mysqltest.t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` char(10) character set utf8 default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +show create table mysqltest.t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `test` varchar(4) NOT NULL default '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop database mysqltest; +deallocate prepare stmt1; +deallocate prepare stmt2; +execute stmt; +show create table t1; +drop table t1; +execute stmt; +show create table t1; +drop table t1; +deallocate prepare stmt; End of 4.1 tests. create table t1 (a varchar(20)); insert into t1 values ('foo'); @@ -1536,4 +1595,54 @@ a 2 DEALLOCATE PREPARE stmt; DROP TABLE t1,t2; +drop table if exists t1; +create table t1 (s1 char(20)); +prepare stmt from "alter table t1 modify s1 int"; +execute stmt; +execute stmt; +drop table t1; +deallocate prepare stmt; +drop table if exists t1; +create table t1 (a int, b int); +prepare s_6895 from "alter table t1 drop column b"; +execute s_6895; +show columns from t1; +Field Type Null Key Default Extra +a int(11) YES NULL +drop table t1; +create table t1 (a int, b int); +execute s_6895; +show columns from t1; +Field Type Null Key Default Extra +a int(11) YES NULL +drop table t1; +create table t1 (a int, b int); +execute s_6895; +show columns from t1; +Field Type Null Key Default Extra +a int(11) YES NULL +deallocate prepare s_6895; +drop table t1; +create table t1 (i int primary key auto_increment) comment='comment for table t1'; +create table t2 (i int, j int, k int); +prepare stmt from "alter table t1 auto_increment=100"; +execute stmt; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `i` int(11) NOT NULL auto_increment, + PRIMARY KEY (`i`) +) ENGINE=MyISAM AUTO_INCREMENT=100 DEFAULT CHARSET=latin1 COMMENT='comment for table t1' +flush tables; +select * from t2; +i j k +execute stmt; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `i` int(11) NOT NULL auto_increment, + PRIMARY KEY (`i`) +) ENGINE=MyISAM AUTO_INCREMENT=100 DEFAULT CHARSET=latin1 COMMENT='comment for table t1' +deallocate prepare stmt; +drop table t1, t2; End of 5.0 tests. diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 44063c1e890..a7f6f638966 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3611,3 +3611,9 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 range si,ai si 5 NULL 2 Using where 1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where DROP TABLE t1,t2,t3; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a int); +INSERT into t1 values (1), (2), (3); +SELECT * FROM t1 LIMIT 2, -1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-1' at line 1 +DROP TABLE t1; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 9db4325aea2..69a4ddacf74 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -5626,6 +5626,23 @@ Called B Called B drop procedure proc_21462_a| drop procedure proc_21462_b| +drop table if exists t3| +drop procedure if exists proc_bug19733| +create table t3 (s1 int)| +create procedure proc_bug19733() +begin +declare v int default 0; +while v < 100 do +create index i on t3 (s1); +drop index i on t3; +set v = v + 1; +end while; +end| +call proc_bug19733()| +call proc_bug19733()| +call proc_bug19733()| +drop procedure proc_bug19733| +drop table t3| End of 5.0 tests DROP TABLE IF EXISTS bug23760| DROP TABLE IF EXISTS bug23760_log| diff --git a/mysql-test/t/ndb_lock.test b/mysql-test/t/ndb_lock.test index 48a8b77dcd7..8dc02dc20cb 100644 --- a/mysql-test/t/ndb_lock.test +++ b/mysql-test/t/ndb_lock.test @@ -102,16 +102,36 @@ connection con1; commit; # table scan +# +# Note that there are two distinct execution paths in which we unlock +# non-matching rows inspected during table scan - one that is used in +# case of filesort and one that used in rest of cases. Below we cover +# the latter (Bug #20390 "SELECT FOR UPDATE does not release locks of +# untouched rows in full table scans"). connection con1; begin; -select * from t1 where y = 'one' or y = 'three' order by x for update; +# We can't use "order by x" here as it will cause filesort +--replace_column 1 # 2 # 3 # +select * from t1 where y = 'one' or y = 'three' for update; connection con2; begin; # Have to check with pk access here since scans take locks on # all rows and then release them in chunks -# Bug #20390 SELECT FOR UPDATE does not release locks of untouched rows in full table scans -#select * from t1 where x = 2 for update; +select * from t1 where x = 2 for update; +--error 1205 +select * from t1 where x = 1 for update; +rollback; + +connection con1; +commit; + +# And now the test for case with filesort +begin; +select * from t1 where y = 'one' or y = 'three' order by x for update; +connection con2; +begin; +select * from t1 where x = 2 for update; --error 1205 select * from t1 where x = 1 for update; rollback; @@ -157,15 +177,32 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; +# We can't use "order by x" here as it will cause filesort +--replace_column 1 # 2 # 3 # +select * from t1 where y = 'one' or y = 'three' lock in share mode; connection con2; begin; select * from t1 where y = 'one' lock in share mode; # Have to check with pk access here since scans take locks on # all rows and then release them in chunks -# Bug #20390 SELECT FOR UPDATE does not release locks of untouched rows in full table scans -#select * from t1 where x = 2 for update; +select * from t1 where x = 2 for update; +--error 1205 +select * from t1 where x = 1 for update; +rollback; + +connection con1; +commit; + +# And the same test for case with filesort +connection con1; +begin; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; + +connection con2; +begin; +select * from t1 where y = 'one' lock in share mode; +select * from t1 where x = 2 for update; --error 1205 select * from t1 where x = 1 for update; rollback; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index f6de2a40efa..5f00aa60b11 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1106,11 +1106,95 @@ EXECUTE stmt USING @a; DEALLOCATE PREPARE stmt; DROP TABLE t1; +# +# Bug#19182: CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work +# from stored procedure. +# +# The cause of a bug was that cached LEX::create_list was modified, +# and then together with LEX::key_list was reset. +# +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +CREATE TABLE t1 (i INT); + +PREPARE st_19182 +FROM "CREATE TABLE t2 (i INT, j INT, KEY (i), KEY(j)) SELECT i FROM t1"; + +EXECUTE st_19182; +DESC t2; + +DROP TABLE t2; + +# Check that on second execution we don't loose 'j' column and the keys +# on 'i' and 'j' columns. +EXECUTE st_19182; +DESC t2; + +DEALLOCATE PREPARE st_19182; +DROP TABLE t2, t1; + +# +# Bug #22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" +# +# Code which implemented CREATE/ALTER TABLE and CREATE DATABASE +# statement modified HA_CREATE_INFO structure in LEX, making these +# statements PS/SP-unsafe (their re-execution might have resulted +# in incorrect results). +# +--disable_warnings +drop database if exists mysqltest; +drop table if exists t1, t2; +--enable_warnings +# CREATE TABLE and CREATE TABLE ... SELECT +create database mysqltest character set utf8; +prepare stmt1 from "create table mysqltest.t1 (c char(10))"; +prepare stmt2 from "create table mysqltest.t2 select 'test'"; +execute stmt1; +execute stmt2; +show create table mysqltest.t1; +show create table mysqltest.t2; +drop table mysqltest.t1; +drop table mysqltest.t2; +alter database mysqltest character set latin1; +execute stmt1; +execute stmt2; +show create table mysqltest.t1; +show create table mysqltest.t2; +drop database mysqltest; +deallocate prepare stmt1; +deallocate prepare stmt2; +# +# CREATE TABLE with DATA DIRECTORY option +# +# Protect ourselves from data left in tmp/ by a previos possibly failed +# test +--system rm -f $MYSQLTEST_VARDIR/tmp/t1.* +--disable_query_log +eval prepare stmt from "create table t1 (c char(10)) data directory='$MYSQLTEST_VARDIR/tmp'"; +--enable_query_log +execute stmt; +# +# DATA DIRECTORY option does not always work: if the operating +# system does not support symlinks, have_symlinks option is automatically +# disabled. +# In this case DATA DIRECTORY is silently ignored when +# creating a table, and is not output by SHOW CREATE TABLE. +# +--disable_result_log +show create table t1; +--enable_result_log +drop table t1; +execute stmt; +--disable_result_log +show create table t1; +--enable_result_log +drop table t1; +deallocate prepare stmt; --echo End of 4.1 tests. - - ############################# 5.0 tests start ################################ # # @@ -1585,5 +1669,76 @@ EXECUTE stmt USING @arg; DEALLOCATE PREPARE stmt; DROP TABLE t1,t2; +# +# Bug#4968 "Stored procedure crash if cursor opened on altered table" +# The bug is not repeatable any more after the fix for +# Bug#15217 "Bug #15217 Using a SP cursor on a table created with PREPARE +# fails with weird error", however ALTER TABLE is not re-execution friendly +# and that caused a valgrind warning. Check that the warning is gone. +# +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (s1 char(20)); +prepare stmt from "alter table t1 modify s1 int"; +execute stmt; +execute stmt; +drop table t1; +deallocate prepare stmt; + +# +# Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing" +# +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int, b int); +prepare s_6895 from "alter table t1 drop column b"; +execute s_6895; +show columns from t1; +drop table t1; +create table t1 (a int, b int); +execute s_6895; +show columns from t1; +drop table t1; +create table t1 (a int, b int); +execute s_6895; +show columns from t1; +deallocate prepare s_6895; +drop table t1; + +# +# Bug #22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" +# +# 5.0 part of the test. +# + +# ALTER TABLE +create table t1 (i int primary key auto_increment) comment='comment for table t1'; +create table t2 (i int, j int, k int); +prepare stmt from "alter table t1 auto_increment=100"; +execute stmt; +show create table t1; +# Let us trash table-cache's memory +flush tables; +select * from t2; +execute stmt; +show create table t1; +deallocate prepare stmt; +drop table t1, t2; +# 5.1 part of the test. +# CREATE DATABASE +#set @old_character_set_server= @@character_set_server; +#set @@character_set_server= latin1; +#prepare stmt from "create database mysqltest"; +#execute stmt; +#show create database mysqltest; +#drop database mysqltest; +#set @@character_set_server= utf8; +#execute stmt; +#show create database mysqltest; +#drop database mysqltest; +#deallocate prepare stmt; +#set @@character_set_server= @old_character_set_server; --echo End of 5.0 tests. diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 0c82cef867f..332389220f6 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3092,3 +3092,23 @@ SELECT t3.a FROM t1,t2,t3 t3.c IN ('bb','ee'); DROP TABLE t1,t2,t3; + +# +# Bug#6298: LIMIT #, -1 no longer works to set start with no end limit +# + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a int); +INSERT into t1 values (1), (2), (3); + +# LIMIT N, -1 was accepted by accident in 4.0, but was not intended. +# This test verifies that this illegal construct is now properly detected. + +--error ER_PARSE_ERROR +SELECT * FROM t1 LIMIT 2, -1; + +DROP TABLE t1; + diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 524ee9cf66c..e5d019a215f 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -6587,6 +6587,34 @@ call proc_21462_b(1)| drop procedure proc_21462_a| drop procedure proc_21462_b| + +# +# Bug#19733 "Repeated alter, or repeated create/drop, fails" +# Check that CREATE/DROP INDEX is re-execution friendly. +# +--disable_warnings +drop table if exists t3| +drop procedure if exists proc_bug19733| +--enable_warnings +create table t3 (s1 int)| + +create procedure proc_bug19733() +begin + declare v int default 0; + while v < 100 do + create index i on t3 (s1); + drop index i on t3; + set v = v + 1; + end while; +end| + +call proc_bug19733()| +call proc_bug19733()| +call proc_bug19733()| + +drop procedure proc_bug19733| +drop table t3| + --echo End of 5.0 tests diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 0f580c833a9..f0ae4bae3a2 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -6111,7 +6111,7 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, retry: if(report_error) { - if (file) + if (file && pTrans) { reterr= file->ndb_err(pTrans); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 534d42e1c17..8d0f2a4d6bf 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -773,17 +773,15 @@ int prepare_create_field(create_field *sql_field, uint table_flags); bool mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, - List &fields, List &keys, + Alter_info *alter_info, bool tmp_table, uint select_field_count); bool mysql_alter_table(THD *thd, char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - List &fields, - List &keys, - uint order_num, ORDER *order, bool ignore, - ALTER_INFO *alter_info, bool do_send_ok); -bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok); + Alter_info *alter_info, + uint order_num, ORDER *order, bool ignore); +bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list); bool mysql_create_like_table(THD *thd, TABLE_LIST *table, HA_CREATE_INFO *create_info, Table_ident *src_table); @@ -792,9 +790,6 @@ bool mysql_rename_table(enum db_type base, const char * old_name, const char *new_db, const char * new_name); -bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List &keys); -bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, - ALTER_INFO *alter_info); bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, Item **conds, uint order_num, ORDER *order); int mysql_update(THD *thd,TABLE_LIST *tables,List &fields, @@ -896,7 +891,8 @@ int get_quote_char_for_identifier(THD *thd, const char *name, uint length); void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild); int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); -bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create); +bool mysqld_show_create_db(THD *thd, char *dbname, + const HA_CREATE_INFO *create); void mysqld_list_processes(THD *thd,const char *user,bool verbose); int mysqld_show_status(THD *thd); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b59962a5005..f107569c3cb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5466,6 +5466,11 @@ log and this option does nothing anymore.", 0, 0, 0, 0, 0}, {"use-symbolic-links", 's', "Enable symbolic link support. Deprecated option; use --symbolic-links instead.", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, + /* + The system call realpath() produces warnings under valgrind and + purify. These are not suppressed: instead we disable symlinks + option if compiled with valgrind support. + */ IF_PURIFY(0,1), 0, 0, 0, 0, 0}, {"user", 'u', "Run mysqld daemon as user.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/sql/sql_class.h b/sql/sql_class.h index d74158ec07c..05034ebd573 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1839,21 +1839,22 @@ class select_insert :public select_result_interceptor { class select_create: public select_insert { ORDER *group; TABLE_LIST *create_table; - List *extra_fields; - List *keys; HA_CREATE_INFO *create_info; + Alter_info *alter_info; MYSQL_LOCK *lock; Field **field; public: - select_create (TABLE_LIST *table, - HA_CREATE_INFO *create_info_par, - List &fields_par, - List &keys_par, - List &select_fields,enum_duplicates duplic, bool ignore) - :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), create_table(table), - extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par), + select_create(TABLE_LIST *table, + HA_CREATE_INFO *create_info_arg, + Alter_info *alter_info_arg, + List &select_fields, + enum_duplicates duplic, bool ignore) + :select_insert(NULL, NULL, &select_fields, 0, 0, duplic, ignore), + create_table(table), + create_info(create_info_arg), + alter_info(alter_info_arg), lock(0) - {} + {} int prepare(List &list, SELECT_LEX_UNIT *u); void store_values(List &values); void send_error(uint errcode,const char *err); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index be6707c80a9..c60d3c307d0 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2616,11 +2616,11 @@ bool select_insert::send_eof() temporary table flag) create_table in Pointer to TABLE_LIST object providing database and name for table to be created or to be open - extra_fields in/out Initial list of fields for table to be created - keys in List of keys for table to be created + alter_info in/out Initial list of columns and indexes for the table + to be created items in List of items which should be used to produce rest of fields for the table (corresponding fields will - be added to the end of 'extra_fields' list) + be added to the end of alter_info->create_list) lock out Pointer to the MYSQL_LOCK object for table created (open) will be returned in this parameter. Since this table is not included in THD::lock caller is @@ -2642,8 +2642,8 @@ bool select_insert::send_eof() static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, TABLE_LIST *create_table, - List *extra_fields, - List *keys, List *items, + Alter_info *alter_info, + List *items, MYSQL_LOCK **lock) { TABLE tmp_table; // Used during 'create_field()' @@ -2682,7 +2682,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(0); if (item->maybe_null) cr_field->flags &= ~NOT_NULL_FLAG; - extra_fields->push_back(cr_field); + alter_info->create_list.push_back(cr_field); } /* create and lock table @@ -2703,8 +2703,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, { tmp_disable_binlog(thd); if (!mysql_create_table(thd, create_table->db, create_table->table_name, - create_info, *extra_fields, *keys, 0, - select_field_count)) + create_info, alter_info, 0, select_field_count)) { /* If we are here in prelocked mode we either create temporary table @@ -2760,7 +2759,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) unit= u; table= create_table_from_items(thd, create_info, create_table, - extra_fields, keys, &values, &lock); + alter_info, &values, &lock); if (!table) DBUG_RETURN(-1); // abort() deletes table diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 986cb4760de..f14856e23fa 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1054,6 +1054,17 @@ int MYSQLlex(void *arg, void *yythd) } +Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) + :drop_list(rhs.drop_list, mem_root), + alter_list(rhs.alter_list, mem_root), + key_list(rhs.key_list, mem_root), + create_list(rhs.create_list, mem_root), + flags(rhs.flags), + keys_onoff(rhs.keys_onoff), + tablespace_op(rhs.tablespace_op) +{} + + /* Skip comment in the end of statement. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a7b162c6062..5731e009cb4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -686,18 +686,58 @@ typedef class st_select_lex SELECT_LEX; #define ALTER_CONVERT 1024 #define ALTER_FORCE 2048 -typedef struct st_alter_info +/** + @brief Parsing data for CREATE or ALTER TABLE. + + This structure contains a list of columns or indexes to be created, + altered or dropped. +*/ + +class Alter_info { +public: List drop_list; List alter_list; + List key_list; + List create_list; uint flags; enum enum_enable_or_disable keys_onoff; enum tablespace_op_type tablespace_op; - st_alter_info(){clear();} - void clear(){keys_onoff= LEAVE_AS_IS;tablespace_op= NO_TABLESPACE_OP;} - void reset(){drop_list.empty();alter_list.empty();clear();} -} ALTER_INFO; + Alter_info() : + flags(0), + keys_onoff(LEAVE_AS_IS), + tablespace_op(NO_TABLESPACE_OP) + {} + + void reset() + { + drop_list.empty(); + alter_list.empty(); + key_list.empty(); + create_list.empty(); + flags= 0; + keys_onoff= LEAVE_AS_IS; + tablespace_op= NO_TABLESPACE_OP; + } + /** + Construct a copy of this object to be used for mysql_alter_table + and mysql_create_table. Historically, these two functions modify + their Alter_info arguments. This behaviour breaks re-execution of + prepared statements and stored procedures and is compensated by + always supplying a copy of Alter_info to these functions. + The constructed copy still shares key Key, Alter_drop, create_field + and Alter_column elements of the lists - these structures are not + modified and thus are not copied. + + @note You need to use check thd->is_fatal_error for out + of memory condition after calling this function. + */ + Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root); +private: + Alter_info &operator=(const Alter_info &rhs); // not implemented + Alter_info(const Alter_info &rhs); // not implemented +}; struct st_sp_chistics { @@ -883,8 +923,6 @@ typedef struct st_lex : public Query_tables_list List interval_list; List users_list; List columns; - List key_list; - List create_list; List *insert_list,field_list,value_list,update_list; List many_values; List var_list; @@ -977,7 +1015,7 @@ typedef struct st_lex : public Query_tables_list bool safe_to_cache_query; bool subqueries, ignore; st_parsing_options parsing_options; - ALTER_INFO alter_info; + Alter_info alter_info; /* Prepared statements SQL syntax:*/ LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ /* diff --git a/sql/sql_list.h b/sql/sql_list.h index 1f2d7841f8a..d00a4598af5 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -61,21 +61,24 @@ public: pointer. */ -class list_node :public Sql_alloc + +/** + list_node - a node of a single-linked list. + @note We never call a destructor for instances of this class. +*/ + +struct list_node :public Sql_alloc { -public: list_node *next; void *info; list_node(void *info_par,list_node *next_par) :next(next_par),info(info_par) - {} + {} list_node() /* For end_of_list */ - { - info=0; - next= this; - } - friend class base_list; - friend class base_list_iterator; + { + info= 0; + next= this; + } }; @@ -91,12 +94,57 @@ public: inline void empty() { elements=0; first= &end_of_list; last=&first;} inline base_list() { empty(); } + /** + This is a shallow copy constructor that implicitly passes the ownership + from the source list to the new instance. The old instance is not + updated, so both objects end up sharing the same nodes. If one of + the instances then adds or removes a node, the other becomes out of + sync ('last' pointer), while still operational. Some old code uses and + relies on this behaviour. This logic is quite tricky: please do not use + it in any new code. + */ inline base_list(const base_list &tmp) :Sql_alloc() { elements= tmp.elements; first= tmp.first; last= elements ? tmp.last : &first; } + /** + Construct a deep copy of the argument in memory root mem_root. + The elements themselves are copied by pointer. + */ + inline base_list(const base_list &rhs, MEM_ROOT *mem_root) + { + if (rhs.elements) + { + /* + It's okay to allocate an array of nodes at once: we never + call a destructor for list_node objects anyway. + */ + first= (list_node*) alloc_root(mem_root, + sizeof(list_node) * rhs.elements); + if (first) + { + elements= rhs.elements; + list_node *dst= first; + list_node *src= rhs.first; + for (; dst < first + elements - 1; dst++, src= src->next) + { + dst->info= src->info; + dst->next= dst + 1; + } + /* Copy the last node */ + dst->info= src->info; + dst->next= &end_of_list; + /* Setup 'last' member */ + last= &dst->next; + return; + } + } + elements= 0; + first= &end_of_list; + last= &first; + } inline base_list(bool error) { } inline bool push_back(void *info) { @@ -347,6 +395,8 @@ template class List :public base_list public: inline List() :base_list() {} inline List(const List &tmp) :base_list(tmp) {} + inline List(const List &tmp, MEM_ROOT *mem_root) : + base_list(tmp, mem_root) {} inline bool push_back(T *a) { return base_list::push_back(a); } inline bool push_back(T *a, MEM_ROOT *mem_root) { return base_list::push_back(a, mem_root); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 007c5edd673..b24148c2f59 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2894,17 +2894,35 @@ mysql_execute_command(THD *thd) // Skip first table, which is the table we are creating TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); TABLE_LIST *select_tables= lex->query_tables; + /* + Code below (especially in mysql_create_table() and select_create + methods) may modify HA_CREATE_INFO structure in LEX, so we have to + use a copy of this structure to make execution prepared statement- + safe. A shallow copy is enough as this code won't modify any memory + referenced from this structure. + */ + HA_CREATE_INFO create_info(lex->create_info); + Alter_info alter_info(lex->alter_info, thd->mem_root); + + if (thd->is_fatal_error) + { + /* out of memory when creating a copy of alter_info */ + res= 1; + goto end_with_restore_list; + } if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; + create_info.alias= create_table->alias; + #ifndef HAVE_READLINK - lex->create_info.data_file_name=lex->create_info.index_file_name=0; + create_info.data_file_name= create_info.index_file_name= NULL; #else /* Fix names if symlinked tables */ - if (append_file_to_dir(thd, &lex->create_info.data_file_name, + if (append_file_to_dir(thd, &create_info.data_file_name, create_table->table_name) || - append_file_to_dir(thd, &lex->create_info.index_file_name, + append_file_to_dir(thd, &create_info.index_file_name, create_table->table_name)) goto end_with_restore_list; #endif @@ -2912,14 +2930,14 @@ mysql_execute_command(THD *thd) If we are using SET CHARSET without DEFAULT, add an implicit DEFAULT to not confuse old users. (This may change). */ - if ((lex->create_info.used_fields & + if ((create_info.used_fields & (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) == HA_CREATE_USED_CHARSET) { - lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET; - lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; - lex->create_info.default_table_charset= lex->create_info.table_charset; - lex->create_info.table_charset= 0; + create_info.used_fields&= ~HA_CREATE_USED_CHARSET; + create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; + create_info.default_table_charset= create_info.table_charset; + create_info.table_charset= 0; } /* The create-select command will open and read-lock the select table @@ -2953,7 +2971,7 @@ mysql_execute_command(THD *thd) Is table which we are changing used somewhere in other parts of query */ - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) + if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { TABLE_LIST *duplicate; if ((duplicate= unique_table(thd, create_table, select_tables))) @@ -2964,10 +2982,10 @@ mysql_execute_command(THD *thd) } } /* If we create merge table, we have to test tables in merge, too */ - if (lex->create_info.used_fields & HA_CREATE_USED_UNION) + if (create_info.used_fields & HA_CREATE_USED_UNION) { TABLE_LIST *tab; - for (tab= (TABLE_LIST*) lex->create_info.merge_list.first; + for (tab= (TABLE_LIST*) create_info.merge_list.first; tab; tab= tab->next_local) { @@ -2980,14 +2998,16 @@ mysql_execute_command(THD *thd) } } } - + /* + select_create is currently not re-execution friendly and + needs to be created for every execution of a PS/SP. + */ if ((result= new select_create(create_table, - &lex->create_info, - lex->create_list, - lex->key_list, - select_lex->item_list, - lex->duplicates, - lex->ignore))) + &create_info, + &alter_info, + select_lex->item_list, + lex->duplicates, + lex->ignore))) { /* CREATE from SELECT give its SELECT_LEX for SELECT, @@ -2996,23 +3016,19 @@ mysql_execute_command(THD *thd) res= handle_select(thd, lex, result, 0); delete result; } - /* reset for PS */ - lex->create_list.empty(); - lex->key_list.empty(); } } else { /* regular create */ if (lex->name) - res= mysql_create_like_table(thd, create_table, &lex->create_info, - (Table_ident *)lex->name); + res= mysql_create_like_table(thd, create_table, &create_info, + (Table_ident *)lex->name); else { res= mysql_create_table(thd, create_table->db, - create_table->table_name, &lex->create_info, - lex->create_list, - lex->key_list, 0, 0); + create_table->table_name, &create_info, + &alter_info, 0, 0); } if (!res) send_ok(thd); @@ -3024,16 +3040,49 @@ end_with_restore_list: break; } case SQLCOM_CREATE_INDEX: + /* Fall through */ + case SQLCOM_DROP_INDEX: + /* + CREATE INDEX and DROP INDEX are implemented by calling ALTER + TABLE with proper arguments. This isn't very fast but it + should work for most cases. + + In the future ALTER TABLE will notice that only added + indexes and create these one by one for the existing table + without having to do a full rebuild. + + One should normally create all indexes with CREATE TABLE or + ALTER TABLE. + */ + { + Alter_info alter_info(lex->alter_info, thd->mem_root); + HA_CREATE_INFO create_info; + + if (thd->is_fatal_error) /* out of memory creating a copy of alter_info*/ + goto error; + DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_one_table_access(thd, INDEX_ACL, all_tables)) + if (check_one_table_access(thd, INDEX_ACL, first_table)) goto error; /* purecov: inspected */ - thd->enable_slow_log= opt_log_slow_admin_statements; if (end_active_trans(thd)) goto error; - else - res = mysql_create_index(thd, first_table, lex->key_list); - break; + /* + Currently CREATE INDEX or DROP INDEX cause a full table rebuild + and thus classify as slow administrative statements just like + ALTER TABLE. + */ + thd->enable_slow_log= opt_log_slow_admin_statements; + bzero((char*) &create_info, sizeof(create_info)); + create_info.db_type= DB_TYPE_DEFAULT; + create_info.row_type= ROW_TYPE_NOT_USED; + create_info.default_table_charset= thd->variables.collation_database; + + res= mysql_alter_table(thd, first_table->db, first_table->table_name, + &create_info, first_table, &alter_info, + 0, (ORDER*) 0, 0); + break; + } #ifdef HAVE_REPLICATION case SQLCOM_SLAVE_START: { @@ -3074,6 +3123,17 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); { ulong priv=0; + /* + Code in mysql_alter_table() may modify its HA_CREATE_INFO argument, + so we have to use a copy of this structure to make execution + prepared statement- safe. A shallow copy is enough as no memory + referenced from this structure will be modified. + */ + HA_CREATE_INFO create_info(lex->create_info); + Alter_info alter_info(lex->alter_info, thd->mem_root); + + if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */ + goto error; if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name); @@ -3088,7 +3148,7 @@ end_with_restore_list: is_schema_db(select_lex->db))|| check_merge_table_access(thd, first_table->db, (TABLE_LIST *) - lex->create_info.merge_list.first)) + create_info.merge_list.first)) goto error; /* purecov: inspected */ if (grant_option) { @@ -3107,13 +3167,13 @@ end_with_restore_list: } } /* Don't yet allow changing of symlinks with ALTER TABLE */ - if (lex->create_info.data_file_name) + if (create_info.data_file_name) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, "DATA DIRECTORY option ignored"); - if (lex->create_info.index_file_name) + if (create_info.index_file_name) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, "INDEX DIRECTORY option ignored"); - lex->create_info.data_file_name=lex->create_info.index_file_name=0; + create_info.data_file_name= create_info.index_file_name= NULL; /* ALTER TABLE ends previous transaction */ if (end_active_trans(thd)) goto error; @@ -3127,13 +3187,13 @@ end_with_restore_list: } thd->enable_slow_log= opt_log_slow_admin_statements; - res= mysql_alter_table(thd, select_lex->db, lex->name, - &lex->create_info, - first_table, lex->create_list, - lex->key_list, - select_lex->order_list.elements, + res= mysql_alter_table(thd, select_lex->db, lex->name, + &create_info, + first_table, + &alter_info, + select_lex->order_list.elements, (ORDER *) select_lex->order_list.first, - lex->ignore, &lex->alter_info, 1); + lex->ignore); } break; } @@ -3286,7 +3346,7 @@ end_with_restore_list: goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? - mysql_recreate_table(thd, first_table, 1) : + mysql_recreate_table(thd, first_table) : mysql_optimize_table(thd, first_table, &lex->check_opt); /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) @@ -3602,15 +3662,6 @@ end_with_restore_list: lex->drop_temporary); } break; - case SQLCOM_DROP_INDEX: - DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_one_table_access(thd, INDEX_ACL, all_tables)) - goto error; /* purecov: inspected */ - if (end_active_trans(thd)) - goto error; - else - res = mysql_drop_index(thd, first_table, &lex->alter_info); - break; case SQLCOM_SHOW_PROCESSLIST: if (!thd->security_ctx->priv_user[0] && check_global_access(thd,PROCESS_ACL)) @@ -3739,6 +3790,12 @@ end_with_restore_list: break; case SQLCOM_CREATE_DB: { + /* + As mysql_create_db() may modify HA_CREATE_INFO structure passed to + it, we need to use a copy of LEX::create_info to make execution + prepared statement- safe. + */ + HA_CREATE_INFO create_info(lex->create_info); if (end_active_trans(thd)) { res= -1; @@ -3766,10 +3823,11 @@ end_with_restore_list: break; } #endif + if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name))) break; res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name), - &lex->create_info, 0); + &create_info, 0); break; } case SQLCOM_DROP_DB: @@ -4456,7 +4514,7 @@ create_sp_error: goto error; } - my_bool nsok= thd->net.no_send_ok; + my_bool save_no_send_ok= thd->net.no_send_ok; thd->net.no_send_ok= TRUE; if (sp->m_flags & sp_head::MULTI_RESULTS) { @@ -4467,7 +4525,7 @@ create_sp_error: back */ my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); - thd->net.no_send_ok= nsok; + thd->net.no_send_ok= save_no_send_ok; goto error; } /* @@ -4483,7 +4541,7 @@ create_sp_error: if (check_routine_access(thd, EXECUTE_ACL, sp->m_db.str, sp->m_name.str, TRUE, FALSE)) { - thd->net.no_send_ok= nsok; + thd->net.no_send_ok= save_no_send_ok; goto error; } #endif @@ -4508,7 +4566,7 @@ create_sp_error: thd->variables.select_limit= select_limit; - thd->net.no_send_ok= nsok; + thd->net.no_send_ok= save_no_send_ok; thd->server_status&= ~bits_to_be_cleared; if (!res) @@ -5917,15 +5975,17 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if (type_modifier & PRI_KEY_FLAG) { lex->col_list.push_back(new key_part_spec(field_name,0)); - lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF, - 0, lex->col_list)); + lex->alter_info.key_list.push_back(new Key(Key::PRIMARY, NullS, + HA_KEY_ALG_UNDEF, 0, + lex->col_list)); lex->col_list.empty(); } if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) { lex->col_list.push_back(new key_part_spec(field_name,0)); - lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0, - lex->col_list)); + lex->alter_info.key_list.push_back(new Key(Key::UNIQUE, NullS, + HA_KEY_ALG_UNDEF, 0, + lex->col_list)); lex->col_list.empty(); } @@ -5988,7 +6048,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, interval_list, cs, uint_geom_type)) DBUG_RETURN(1); - lex->create_list.push_back(new_field); + lex->alter_info.create_list.push_back(new_field); lex->last_field=new_field; DBUG_RETURN(0); } @@ -7016,55 +7076,6 @@ Item * all_any_subquery_creator(Item *left_expr, } -/* - CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with - the proper arguments. This isn't very fast but it should work for most - cases. - - In the future ALTER TABLE will notice that only added indexes - and create these one by one for the existing table without having to do - a full rebuild. - - One should normally create all indexes with CREATE TABLE or ALTER TABLE. -*/ - -bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List &keys) -{ - List fields; - ALTER_INFO alter_info; - alter_info.flags= ALTER_ADD_INDEX; - HA_CREATE_INFO create_info; - DBUG_ENTER("mysql_create_index"); - bzero((char*) &create_info,sizeof(create_info)); - create_info.db_type=DB_TYPE_DEFAULT; - create_info.default_table_charset= thd->variables.collation_database; - create_info.row_type= ROW_TYPE_NOT_USED; - DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, - &create_info, table_list, - fields, keys, 0, (ORDER*)0, - 0, &alter_info, 1)); -} - - -bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) -{ - List fields; - List keys; - HA_CREATE_INFO create_info; - DBUG_ENTER("mysql_drop_index"); - bzero((char*) &create_info,sizeof(create_info)); - create_info.db_type=DB_TYPE_DEFAULT; - create_info.default_table_charset= thd->variables.collation_database; - create_info.row_type= ROW_TYPE_NOT_USED; - alter_info->clear(); - alter_info->flags= ALTER_DROP_INDEX; - DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, - &create_info, table_list, - fields, keys, 0, (ORDER*)0, - 0, alter_info, 1)); -} - - /* Multi update query pre-check @@ -7363,7 +7374,6 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? CREATE_TMP_ACL : CREATE_ACL); - lex->create_info.alias= create_table->alias; if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, 0, 0, test(create_table->schema_table)) || diff --git a/sql/sql_select.cc b/sql/sql_select.cc index abb949ae473..d62032c2b44 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10434,6 +10434,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, */ join->examined_rows++; join->thd->row_count++; + join_tab->read_record.file->unlock_row(); } return NESTED_LOOP_OK; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index af9e3d44186..21a5ead90af 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -460,7 +460,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) } bool mysqld_show_create_db(THD *thd, char *dbname, - HA_CREATE_INFO *create_info) + const HA_CREATE_INFO *create_info) { Security_context *sctx= thd->security_ctx; char buff[2048]; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b3bd3182a59..4f5f4c29854 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -645,8 +645,7 @@ int prepare_create_field(create_field *sql_field, mysql_prepare_table() thd Thread object create_info Create information (like MAX_ROWS) - fields List of fields to create - keys List of keys to create + alter_info List of columns and indexes to create DESCRIPTION Prepares the table and key structures for table creation. @@ -660,8 +659,8 @@ int prepare_create_field(create_field *sql_field, */ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, - List *fields, - List *keys, bool tmp_table, + Alter_info *alter_info, + bool tmp_table, uint *db_options, handler *file, KEY **key_info_buffer, uint *key_count, int select_field_count) @@ -675,11 +674,12 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, int timestamps= 0, timestamps_with_niladic= 0; int field_no,dup_no; int select_field_pos,auto_increment=0; - List_iterator it(*fields),it2(*fields); + List_iterator it(alter_info->create_list); + List_iterator it2(alter_info->create_list); uint total_uneven_bit_length= 0; DBUG_ENTER("mysql_prepare_table"); - select_field_pos= fields->elements - select_field_count; + select_field_pos= alter_info->create_list.elements - select_field_count; null_fields=blob_columns=0; create_info->varchar= 0; max_key_length= file->max_key_length(); @@ -1004,7 +1004,8 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, /* Create keys */ - List_iterator key_iterator(*keys), key_iterator2(*keys); + List_iterator key_iterator(alter_info->key_list); + List_iterator key_iterator2(alter_info->key_list); uint key_parts=0, fk_key_count=0; bool primary_key=0,unique_key=0; Key *key, *key2; @@ -1563,14 +1564,13 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) SYNOPSIS mysql_create_table() - thd Thread object - db Database - table_name Table name - create_info Create information (like MAX_ROWS) - fields List of fields to create - keys List of keys to create - internal_tmp_table Set to 1 if this is an internal temporary table - (From ALTER TABLE) + thd Thread object + db Database + table_name Table name + create_info [in/out] Create information (like MAX_ROWS) + alter_info [in/out] List of columns and indexes to create + internal_tmp_table Set to 1 if this is an internal temporary table + (From ALTER TABLE) DESCRIPTION If one creates a temporary table, this is automatically opened @@ -1580,6 +1580,11 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) select_field_count is also used for CREATE ... SELECT, and must be zero for standard create of table. + Note that structures passed as 'create_info' and 'alter_info' parameters + may be modified by this function. It is responsibility of the caller to + make a copy of create_info in order to provide correct execution in + prepared statements/stored routines. + RETURN VALUES FALSE OK TRUE error @@ -1587,8 +1592,8 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) bool mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, - List &fields, - List &keys,bool internal_tmp_table, + Alter_info *alter_info, + bool internal_tmp_table, uint select_field_count) { char path[FN_REFLEN]; @@ -1600,7 +1605,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_ENTER("mysql_create_table"); /* Check for duplicate fields and check type of table to create */ - if (!fields.elements) + if (!alter_info->create_list.elements) { my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS), MYF(0)); @@ -1644,10 +1649,10 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, create_info->default_table_charset= db_info.default_table_charset; } - if (mysql_prepare_table(thd, create_info, &fields, - &keys, internal_tmp_table, &db_options, file, - &key_info_buffer, &key_count, - select_field_count)) + if (mysql_prepare_table(thd, create_info, alter_info, internal_tmp_table, + &db_options, file, + &key_info_buffer, &key_count, + select_field_count)) DBUG_RETURN(TRUE); /* Check if table exists */ @@ -1737,8 +1742,8 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, create_info->table_options=db_options; if (rea_create_table(thd, path, db, table_name, - create_info, fields, key_count, - key_info_buffer)) + create_info, alter_info->create_list, + key_count, key_info_buffer)) goto end; if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { @@ -2323,9 +2328,12 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, (table->table->file->ha_check_for_upgrade(check_opt) == HA_ADMIN_NEEDS_ALTER)) { + my_bool save_no_send_ok= thd->net.no_send_ok; close_thread_tables(thd); tmp_disable_binlog(thd); // binlogging is done by caller if wanted - result_code= mysql_recreate_table(thd, table, 0); + thd->net.no_send_ok= TRUE; + result_code= mysql_recreate_table(thd, table); + thd->net.no_send_ok= save_no_send_ok; reenable_binlog(thd); goto send_result; } @@ -2404,6 +2412,7 @@ send_result_message: case HA_ADMIN_TRY_ALTER: { + my_bool save_no_send_ok= thd->net.no_send_ok; /* This is currently used only by InnoDB. ha_innobase::optimize() answers "try with alter", so here we close the table, do an ALTER TABLE, @@ -2414,7 +2423,9 @@ send_result_message: *save_next_global= table->next_global; table->next_local= table->next_global= 0; tmp_disable_binlog(thd); // binlogging is done by caller if wanted - result_code= mysql_recreate_table(thd, table, 0); + thd->net.no_send_ok= TRUE; + result_code= mysql_recreate_table(thd, table); + thd->net.no_send_ok= save_no_send_ok; reenable_binlog(thd); close_thread_tables(thd); if (!result_code) // recreation went ok @@ -2984,14 +2995,20 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, /* Alter table + + + NOTE + The structures passed as 'create_info' and 'alter_info' parameters may + be modified by this function. It is responsibility of the caller to make + a copy of create_info in order to provide correct execution in prepared + statements/stored routines. */ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - List &fields, List &keys, - uint order_num, ORDER *order, bool ignore, - ALTER_INFO *alter_info, bool do_send_ok) + Alter_info *alter_info, + uint order_num, ORDER *order, bool ignore) { TABLE *table,*new_table=0; int error; @@ -3212,8 +3229,7 @@ view_err: Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE); mysql_bin_log.write(&qinfo); } - if (do_send_ok) - send_ok(thd); + send_ok(thd); } else if (error > 0) { @@ -3240,10 +3256,9 @@ view_err: restore_record(table, s->default_values); // Empty record for DEFAULT List_iterator drop_it(alter_info->drop_list); - List_iterator def_it(fields); + List_iterator def_it(alter_info->create_list); List_iterator alter_it(alter_info->alter_list); - List create_list; // Add new fields here - List key_list; // Add new keys here + Alter_info new_info; // Add new columns and indexes here create_field *def; /* @@ -3291,13 +3306,13 @@ view_err: def->field=field; if (!def->after) { - create_list.push_back(def); + new_info.create_list.push_back(def); def_it.remove(); } } else { // Use old field value - create_list.push_back(def=new create_field(field,field)); + new_info.create_list.push_back(def= new create_field(field, field)); alter_it.rewind(); // Change default if ALTER Alter_column *alter; while ((alter=alter_it++)) @@ -3321,7 +3336,7 @@ view_err: } } def_it.rewind(); - List_iterator find_it(create_list); + List_iterator find_it(new_info.create_list); while ((def=def_it++)) // Add new columns { if (def->change && ! def->field) @@ -3330,9 +3345,9 @@ view_err: DBUG_RETURN(TRUE); } if (!def->after) - create_list.push_back(def); + new_info.create_list.push_back(def); else if (def->after == first_keyword) - create_list.push_front(def); + new_info.create_list.push_front(def); else { create_field *find; @@ -3356,7 +3371,7 @@ view_err: alter_info->alter_list.head()->name, table_name); DBUG_RETURN(TRUE); } - if (!create_list.elements) + if (!new_info.create_list.elements) { my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS), MYF(0)); @@ -3368,8 +3383,8 @@ view_err: for which some fields exists. */ - List_iterator key_it(keys); - List_iterator field_it(create_list); + List_iterator key_it(alter_info->key_list); + List_iterator field_it(new_info.create_list); List key_parts; KEY *key_info=table->key_info; @@ -3441,24 +3456,37 @@ view_err: key_part_length)); } if (key_parts.elements) - key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL : - (key_info->flags & HA_NOSAME ? - (!my_strcasecmp(system_charset_info, - key_name, primary_key_name) ? - Key::PRIMARY : Key::UNIQUE) : - (key_info->flags & HA_FULLTEXT ? - Key::FULLTEXT : Key::MULTIPLE)), - key_name, - key_info->algorithm, - test(key_info->flags & HA_GENERATED_KEY), - key_parts)); + { + Key *key; + enum Key::Keytype key_type; + + if (key_info->flags & HA_SPATIAL) + key_type= Key::SPATIAL; + else if (key_info->flags & HA_NOSAME) + { + if (! my_strcasecmp(system_charset_info, key_name, primary_key_name)) + key_type= Key::PRIMARY; + else + key_type= Key::UNIQUE; + } + else if (key_info->flags & HA_FULLTEXT) + key_type= Key::FULLTEXT; + else + key_type= Key::MULTIPLE; + + key= new Key(key_type, key_name, + key_info->algorithm, + test(key_info->flags & HA_GENERATED_KEY), + key_parts); + new_info.key_list.push_back(key); + } } { Key *key; while ((key=key_it++)) // Add new keys { if (key->type != Key::FOREIGN_KEY) - key_list.push_back(key); + new_info.key_list.push_back(key); if (key->name && !my_strcasecmp(system_charset_info,key->name,primary_key_name)) { @@ -3593,7 +3621,7 @@ view_err: { tmp_disable_binlog(thd); error= mysql_create_table(thd, new_db, tmp_name, - create_info,create_list,key_list,1,0); + create_info, &new_info, 1, 0); reenable_binlog(thd); if (error) DBUG_RETURN(error); @@ -3634,9 +3662,9 @@ view_err: { new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; new_table->next_number_field=new_table->found_next_number_field; - error=copy_data_between_tables(table, new_table, create_list, ignore, - order_num, order, &copied, &deleted, - alter_info->keys_onoff); + error= copy_data_between_tables(table, new_table, new_info.create_list, + ignore, order_num, order, + &copied, &deleted, alter_info->keys_onoff); } else if (!new_table) { @@ -3860,8 +3888,7 @@ end_temporary: my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO), (ulong) (copied + deleted), (ulong) deleted, (ulong) thd->cuted_fields); - if (do_send_ok) - send_ok(thd,copied+deleted,0L,tmp_name); + send_ok(thd, copied + deleted, 0L, tmp_name); thd->some_tables_deleted=0; DBUG_RETURN(FALSE); @@ -4060,31 +4087,26 @@ copy_data_between_tables(TABLE *from,TABLE *to, mysql_recreate_table() thd Thread handler tables Tables to recreate - do_send_ok If we should send_ok() or leave it to caller RETURN Like mysql_alter_table(). */ -bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, - bool do_send_ok) +bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) { - DBUG_ENTER("mysql_recreate_table"); - LEX *lex= thd->lex; HA_CREATE_INFO create_info; - lex->create_list.empty(); - lex->key_list.empty(); - lex->col_list.empty(); - lex->alter_info.reset(); - bzero((char*) &create_info,sizeof(create_info)); + Alter_info alter_info; + + DBUG_ENTER("mysql_recreate_table"); + + bzero((char*) &create_info, sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.row_type=ROW_TYPE_NOT_USED; create_info.default_table_charset=default_charset_info; /* Force alter table to recreate table */ - lex->alter_info.flags= ALTER_CHANGE_COLUMN; + alter_info.flags= ALTER_CHANGE_COLUMN; DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info, - table_list, lex->create_list, - lex->key_list, 0, (ORDER *) 0, - 0, &lex->alter_info, do_send_ok)); + table_list, &alter_info, + 0, (ORDER *) 0, 0)); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1fa23beb2f7..92640ea58d6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1352,8 +1352,7 @@ create: TL_READ_NO_INSERT: TL_READ))) YYABORT; - lex->create_list.empty(); - lex->key_list.empty(); + lex->alter_info.reset(); lex->col_list.empty(); lex->change=NullS; bzero((char*) &lex->create_info,sizeof(lex->create_info)); @@ -1371,16 +1370,17 @@ create: if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL, TL_OPTION_UPDATING)) YYABORT; - lex->create_list.empty(); - lex->key_list.empty(); + lex->alter_info.reset(); + lex->alter_info.flags= ALTER_ADD_INDEX; lex->col_list.empty(); lex->change=NullS; } '(' key_list ')' { LEX *lex=Lex; + Key *key= new Key($2, $4.str, $5, 0, lex->col_list); - lex->key_list.push_back(new Key($2,$4.str, $5, 0, lex->col_list)); + lex->alter_info.key_list.push_back(key); lex->col_list.empty(); } | CREATE DATABASE opt_if_not_exists ident @@ -2920,29 +2920,34 @@ key_def: key_type opt_ident key_alg '(' key_list ')' { LEX *lex=Lex; - lex->key_list.push_back(new Key($1,$2, $3, 0, lex->col_list)); + Key *key= new Key($1, $2, $3, 0, lex->col_list); + lex->alter_info.key_list.push_back(key); + lex->col_list.empty(); /* Alloced by sql_alloc */ } | opt_constraint constraint_key_type opt_ident key_alg '(' key_list ')' { LEX *lex=Lex; const char *key_name= $3 ? $3:$1; - lex->key_list.push_back(new Key($2, key_name, $4, 0, - lex->col_list)); + Key *key= new Key($2, key_name, $4, 0, lex->col_list); + lex->alter_info.key_list.push_back(key); lex->col_list.empty(); /* Alloced by sql_alloc */ } | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references { LEX *lex=Lex; - lex->key_list.push_back(new foreign_key($4 ? $4:$1, lex->col_list, - $8, - lex->ref_list, - lex->fk_delete_opt, - lex->fk_update_opt, - lex->fk_match_option)); - lex->key_list.push_back(new Key(Key::MULTIPLE, $4 ? $4 : $1, - HA_KEY_ALG_UNDEF, 1, - lex->col_list)); + const char *key_name= $4 ? $4 : $1; + Key *key= new foreign_key(key_name, lex->col_list, + $8, + lex->ref_list, + lex->fk_delete_opt, + lex->fk_update_opt, + lex->fk_match_option); + lex->alter_info.key_list.push_back(key); + key= new Key(Key::MULTIPLE, key_name, + HA_KEY_ALG_UNDEF, 1, + lex->col_list); + lex->alter_info.key_list.push_back(key); lex->col_list.empty(); /* Alloced by sql_alloc */ } | constraint opt_check_constraint @@ -3484,8 +3489,6 @@ alter: if (!lex->select_lex.add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING)) YYABORT; - lex->create_list.empty(); - lex->key_list.empty(); lex->col_list.empty(); lex->select_lex.init_order(); lex->select_lex.db= @@ -3495,8 +3498,7 @@ alter: lex->create_info.db_type= DB_TYPE_DEFAULT; lex->create_info.default_table_charset= NULL; lex->create_info.row_type= ROW_TYPE_NOT_USED; - lex->alter_info.reset(); - lex->alter_info.flags= 0; + lex->alter_info.reset(); } alter_list {} @@ -6254,7 +6256,8 @@ drop: { LEX *lex=Lex; lex->sql_command= SQLCOM_DROP_INDEX; - lex->alter_info.drop_list.empty(); + lex->alter_info.reset(); + lex->alter_info.flags= ALTER_DROP_INDEX; lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, $3.str)); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,