From 7fbc28c0740460ee5ed7bfbe1d82d438f6971b21 Mon Sep 17 00:00:00 2001 From: "lenz@mysql.com" <> Date: Tue, 15 Apr 2003 16:11:37 +0200 Subject: [PATCH 1/7] - added missing dashes to parameter "open-files-limit" in mysqld_safe (bug #264) --- scripts/mysqld_safe.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 094b1fbfcd3..e400c27b84c 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -224,7 +224,7 @@ then if test -n "$open_files" then ulimit -n $open_files - args="open-files-limit=$open_files $args" + args="--open-files-limit=$open_files $args" fi if test -n "$core_file_size" then From 1e66aec50291b1470454108a0cad23dacbd8cd2c Mon Sep 17 00:00:00 2001 From: "lenz@mysql.com" <> Date: Tue, 15 Apr 2003 17:34:56 +0200 Subject: [PATCH 2/7] - Updated variable names that have been renamed in my.cnf example files (bug #223) - Added more detailed info about how to set up replication --- support-files/my-huge.cnf.sh | 39 +++++++++++++++++++++++++++++----- support-files/my-large.cnf.sh | 39 +++++++++++++++++++++++++++++----- support-files/my-medium.cnf.sh | 37 ++++++++++++++++++++++++++++---- support-files/my-small.cnf.sh | 16 ++++++++++---- 4 files changed, 113 insertions(+), 18 deletions(-) diff --git a/support-files/my-huge.cnf.sh b/support-files/my-huge.cnf.sh index b1cf295e429..3362d5bd131 100644 --- a/support-files/my-huge.cnf.sh +++ b/support-files/my-huge.cnf.sh @@ -29,8 +29,8 @@ skip-locking set-variable = key_buffer=384M set-variable = max_allowed_packet=1M set-variable = table_cache=512 -set-variable = sort_buffer=2M -set-variable = record_buffer=2M +set-variable = sort_buffer_size=2M +set-variable = read_buffer_size=2M set-variable = myisam_sort_buffer_size=64M set-variable = thread_cache=8 # Try number of CPU's*2 for thread_concurrency @@ -53,7 +53,36 @@ log-bin # but will not function as a master if omitted server-id = 1 -# Replication Slave Server (comment out master section to use this) +# Replication Slave (comment out master section to use this) +# +# To configure this host as a replication slave, you can choose between +# two methods : +# +# 1) Use the CHANGE MASTER TO command (fully described in our manual) - +# the syntax is: +# +# CHANGE MASTER TO MASTER_HOST=, MASTER_PORT=, +# MASTER_USER=, MASTER_PASSWORD= ; +# +# where you replace , , by quoted strings and +# by the master's port number (3306 by default). +# +# Example: +# +# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306, +# MASTER_USER='joe', MASTER_PASSWORD='secret'; +# +# OR +# +# 2) Set the variables below. However, in case you choose this method, then +# start replication for the first time (even unsuccessfully, for example +# if you mistyped the password in master-password and the slave fails to +# connect), the slave will create a master.info file, and any later +# change in this file to the variables' values below will be ignored and +# overridden by the content of the master.info file, unless you shutdown +# the slave server, delete master.info and restart the slaver server. +# For that reason, you may want to leave the lines below untouched +# (commented) and instead use CHANGE MASTER TO (see above) # # required unique id between 2 and 2^32 - 1 # (and different from the master) @@ -113,13 +142,13 @@ no-auto-rehash [isamchk] set-variable = key_buffer=256M -set-variable = sort_buffer=256M +set-variable = sort_buffer_size=256M set-variable = read_buffer=2M set-variable = write_buffer=2M [myisamchk] set-variable = key_buffer=256M -set-variable = sort_buffer=256M +set-variable = sort_buffer_size=256M set-variable = read_buffer=2M set-variable = write_buffer=2M diff --git a/support-files/my-large.cnf.sh b/support-files/my-large.cnf.sh index 861c2d59fb0..5bc3a8eb0c4 100644 --- a/support-files/my-large.cnf.sh +++ b/support-files/my-large.cnf.sh @@ -29,8 +29,8 @@ skip-locking set-variable = key_buffer=256M set-variable = max_allowed_packet=1M set-variable = table_cache=256 -set-variable = sort_buffer=1M -set-variable = record_buffer=1M +set-variable = sort_buffer_size=1M +set-variable = read_buffer_size=1M set-variable = myisam_sort_buffer_size=64M set-variable = thread_cache=8 # Try number of CPU's*2 for thread_concurrency @@ -53,7 +53,36 @@ log-bin # but will not function as a master if omitted server-id = 1 -# Replication Slave Server (comment out master section to use this) +# Replication Slave (comment out master section to use this) +# +# To configure this host as a replication slave, you can choose between +# two methods : +# +# 1) Use the CHANGE MASTER TO command (fully described in our manual) - +# the syntax is: +# +# CHANGE MASTER TO MASTER_HOST=, MASTER_PORT=, +# MASTER_USER=, MASTER_PASSWORD= ; +# +# where you replace , , by quoted strings and +# by the master's port number (3306 by default). +# +# Example: +# +# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306, +# MASTER_USER='joe', MASTER_PASSWORD='secret'; +# +# OR +# +# 2) Set the variables below. However, in case you choose this method, then +# start replication for the first time (even unsuccessfully, for example +# if you mistyped the password in master-password and the slave fails to +# connect), the slave will create a master.info file, and any later +# change in this file to the variables' values below will be ignored and +# overridden by the content of the master.info file, unless you shutdown +# the slave server, delete master.info and restart the slaver server. +# For that reason, you may want to leave the lines below untouched +# (commented) and instead use CHANGE MASTER TO (see above) # # required unique id between 2 and 2^32 - 1 # (and different from the master) @@ -113,13 +142,13 @@ no-auto-rehash [isamchk] set-variable = key_buffer=128M -set-variable = sort_buffer=128M +set-variable = sort_buffer_size=128M set-variable = read_buffer=2M set-variable = write_buffer=2M [myisamchk] set-variable = key_buffer=128M -set-variable = sort_buffer=128M +set-variable = sort_buffer_size=128M set-variable = read_buffer=2M set-variable = write_buffer=2M diff --git a/support-files/my-medium.cnf.sh b/support-files/my-medium.cnf.sh index b5ce3bc6ced..084f23cbc94 100644 --- a/support-files/my-medium.cnf.sh +++ b/support-files/my-medium.cnf.sh @@ -30,7 +30,7 @@ skip-locking set-variable = key_buffer=16M set-variable = max_allowed_packet=1M set-variable = table_cache=64 -set-variable = sort_buffer=512K +set-variable = sort_buffer_size=512K set-variable = net_buffer_length=8K set-variable = myisam_sort_buffer_size=8M @@ -51,7 +51,36 @@ log-bin # but will not function as a master if omitted server-id = 1 -# Replication Slave Server (comment out master section to use this) +# Replication Slave (comment out master section to use this) +# +# To configure this host as a replication slave, you can choose between +# two methods : +# +# 1) Use the CHANGE MASTER TO command (fully described in our manual) - +# the syntax is: +# +# CHANGE MASTER TO MASTER_HOST=, MASTER_PORT=, +# MASTER_USER=, MASTER_PASSWORD= ; +# +# where you replace , , by quoted strings and +# by the master's port number (3306 by default). +# +# Example: +# +# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306, +# MASTER_USER='joe', MASTER_PASSWORD='secret'; +# +# OR +# +# 2) Set the variables below. However, in case you choose this method, then +# start replication for the first time (even unsuccessfully, for example +# if you mistyped the password in master-password and the slave fails to +# connect), the slave will create a master.info file, and any later +# change in this file to the variables' values below will be ignored and +# overridden by the content of the master.info file, unless you shutdown +# the slave server, delete master.info and restart the slaver server. +# For that reason, you may want to leave the lines below untouched +# (commented) and instead use CHANGE MASTER TO (see above) # # required unique id between 2 and 2^32 - 1 # (and different from the master) @@ -111,13 +140,13 @@ no-auto-rehash [isamchk] set-variable = key_buffer=20M -set-variable = sort_buffer=20M +set-variable = sort_buffer_size=20M set-variable = read_buffer=2M set-variable = write_buffer=2M [myisamchk] set-variable = key_buffer=20M -set-variable = sort_buffer=20M +set-variable = sort_buffer_size=20M set-variable = read_buffer=2M set-variable = write_buffer=2M diff --git a/support-files/my-small.cnf.sh b/support-files/my-small.cnf.sh index b8941184fe0..b11277c1b4a 100644 --- a/support-files/my-small.cnf.sh +++ b/support-files/my-small.cnf.sh @@ -29,10 +29,18 @@ socket = @MYSQL_UNIX_ADDR@ skip-locking set-variable = key_buffer=16K set-variable = max_allowed_packet=1M -set-variable = thread_stack=64K set-variable = table_cache=4 -set-variable = sort_buffer=64K +set-variable = sort_buffer_size=64K set-variable = net_buffer_length=2K +set-variable = thread_stack=64K + +# Don't listen on a TCP/IP port at all. This can be a security enhancement, +# if all processes that need to connect to mysqld run on the same host. +# All interaction with mysqld must be made via Unix sockets or named pipes. +# Note that using this option without enabling named pipes on Windows +# (via the "pipe" option) will render mysqld useless! +# +#skip-networking server-id = 1 # Uncomment the following if you want to log updates @@ -67,11 +75,11 @@ no-auto-rehash [isamchk] set-variable = key_buffer=8M -set-variable = sort_buffer=8M +set-variable = sort_buffer_size=8M [myisamchk] set-variable = key_buffer=8M -set-variable = sort_buffer=8M +set-variable = sort_buffer_size=8M [mysqlhotcopy] interactive-timeout From 3e6c350f6461b2dc4a2bb00143583dfcf3e38399 Mon Sep 17 00:00:00 2001 From: "monty@mashka.mysql.fi" <> Date: Tue, 15 Apr 2003 22:04:16 +0300 Subject: [PATCH 3/7] Fixed bug in SUM() with NULL:s Added logging of warnings for failed connections with ssl. Fixed bug in symbolic link handling on Windows --- include/my_global.h | 5 ++++ mysql-test/r/func_group.result | 43 +++++++++++++++++++++++++++++++++- mysql-test/t/func_group.test | 21 +++++++++++++++++ sql/item_sum.cc | 13 +++++++--- sql/item_sum.h | 8 ++++--- sql/mysqld.cc | 13 ++++------ sql/net_serv.cc | 22 ++++++++--------- sql/sql_acl.cc | 12 ++++++++++ sql/sql_show.cc | 2 +- 9 files changed, 112 insertions(+), 27 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 39b6cada0e3..b6b2dfcda32 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -19,6 +19,11 @@ #ifndef _global_h #define _global_h +#ifndef EMBEDDED_LIBRARY +#define HAVE_REPLICATION +#define HAVE_EXTERNAL_CLIENT +#endif + #if defined( __EMX__) && !defined( MYSQL_SERVER) /* moved here to use below VOID macro redefinition */ #define INCL_BASE diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index b129beaac81..80ccb6c7bb8 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -47,7 +47,7 @@ sum(all a) count(all a) avg(all a) std(all a) bit_or(all a) bit_and(all a) min(a 21 6 3.5000 1.7078 7 0 1 6 E select grp, sum(a),count(a),avg(a),std(a),bit_or(a),bit_and(a),min(a),max(a),min(c),max(c) from t1 group by grp; grp sum(a) count(a) avg(a) std(a) bit_or(a) bit_and(a) min(a) max(a) min(c) max(c) -NULL 0 0 NULL NULL 0 0 NULL NULL +NULL NULL 0 NULL NULL 0 0 NULL NULL 1 1 1 1.0000 0.0000 1 1 1 1 a a 2 5 2 2.5000 0.5000 3 2 2 3 b c 3 15 3 5.0000 0.8165 7 4 4 6 C E @@ -204,3 +204,44 @@ select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1= max(t2.a1) NULL drop table t1,t2; +CREATE TABLE t1 (a int, b int); +select count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1; +count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +0 NULL NULL NULL NULL NULL -1 0 +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +insert into t1 values (1,null); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL -1 0 +insert into t1 values (1,null); +insert into t1 values (2,null); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL 0 0 +2 0 NULL NULL NULL NULL NULL 0 0 +select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL -1 0 +2 0 NULL NULL NULL NULL NULL -1 0 +insert into t1 values (2,1); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL 0 0 +2 1 1 1.0000 0.0000 1 1 0 1 +select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL -1 0 +2 1 1 1.0000 0.0000 1 1 1 1 +insert into t1 values (3,1); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL 0 0 +2 1 1 1.0000 0.0000 1 1 0 1 +3 1 1 1.0000 0.0000 1 1 1 1 +select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b) +1 0 NULL NULL NULL NULL NULL -1 0 +2 1 1 1.0000 0.0000 1 1 1 1 +3 1 1 1.0000 0.0000 1 1 1 1 +drop table t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index 1915c2172ad..40d829d3e70 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -126,3 +126,24 @@ select max(t1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1=20; select max(t1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1=10; select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1='AAA'; drop table t1,t2; + +# +# Test of group function and NULL values +# + +CREATE TABLE t1 (a int, b int); +select count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1; +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +insert into t1 values (1,null); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +insert into t1 values (1,null); +insert into t1 values (2,null); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +insert into t1 values (2,1); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +insert into t1 values (3,1); +select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a; +drop table t1; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b6bbc12efd6..3a513505913 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -175,12 +175,14 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) void Item_sum_sum::reset() { - null_value=0; sum=0.0; Item_sum_sum::add(); + null_value=1; sum=0.0; Item_sum_sum::add(); } bool Item_sum_sum::add() { sum+=args[0]->val(); + if (!args[0]->null_value) + null_value= 0; return 0; } @@ -566,8 +568,10 @@ void Item_sum_sum::reset_field() { double nr=args[0]->val(); // Nulls also return 0 float8store(result_field->ptr,nr); - null_value=0; - result_field->set_notnull(); + if (args[0]->null_value) + result_field->set_null(); + else + result_field->set_notnull(); } @@ -623,7 +627,10 @@ void Item_sum_sum::update_field(int offset) float8get(old_nr,res+offset); nr=args[0]->val(); if (!args[0]->null_value) + { old_nr+=nr; + result_field->set_notnull(); + } float8store(res,old_nr); } diff --git a/sql/item_sum.h b/sql/item_sum.h index 29ac1f1d1b1..2369b5d1d7e 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -92,9 +92,6 @@ public: class Item_sum_int :public Item_sum_num { - void fix_length_and_dec() - { decimals=0; max_length=21; maybe_null=null_value=0; } - public: Item_sum_int(Item *item_par) :Item_sum_num(item_par) {} Item_sum_int(List &list) :Item_sum_num(list) {} @@ -102,6 +99,8 @@ public: String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } unsigned int size_of() { return sizeof(*this);} + void fix_length_and_dec() + { decimals=0; max_length=21; maybe_null=null_value=0; } }; @@ -118,6 +117,7 @@ class Item_sum_sum :public Item_sum_num double val(); void reset_field(); void update_field(int offset); + void no_rows_in_result() {} const char *func_name() const { return "sum"; } unsigned int size_of() { return sizeof(*this);} }; @@ -361,6 +361,8 @@ class Item_sum_bit :public Item_sum_int longlong val_int(); void reset_field(); unsigned int size_of() { return sizeof(*this);} + void fix_length_and_dec() + { decimals=0; max_length=21; unsigned_flag=1; maybe_null=null_value=0; } }; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4ae248acb67..24e343017e2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3562,7 +3562,7 @@ relay logs", {"skip-stack-trace", OPT_SKIP_STACK_TRACE, "Don't print a stack trace on failure", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables", + {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables. Depricated option. Use --skip-symbolic-links instead", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"skip-thread-priority", OPT_SKIP_PRIOR, "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, @@ -3606,11 +3606,12 @@ replicating a LOAD DATA INFILE command", {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running", (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef USE_SYMDIR - {"use-symbolic-links", 's', "Enable symbolic link support", + {"use-symbolic-links", 's', "Enable symbolic link support. Depricated option; Use --symbolic-links instead", + (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, + IF_PURIFY(0,1), 0, 0, 0, 0, 0}, + {"--symbolic-links", 's', "Enable symbolic link support", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, IF_PURIFY(0,1), 0, 0, 0, 0, 0}, -#endif {"user", 'u', "Run mysqld daemon as user", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit", 0, 0, 0, GET_NO_ARG, @@ -4424,9 +4425,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; myisam_concurrent_insert=0; myisam_recover_options= HA_RECOVER_NONE; - my_disable_symlinks=1; my_use_symdir=0; - have_symlink=SHOW_OPTION_DISABLED; ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE); #ifdef HAVE_QUERY_CACHE query_cache_size=0; @@ -4473,9 +4472,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), test_flags|=TEST_NO_STACKTRACE; break; case (int) OPT_SKIP_SYMLINKS: - my_disable_symlinks=1; my_use_symdir=0; - have_symlink=SHOW_OPTION_DISABLED; break; case (int) OPT_BIND_ADDRESS: if (argument && isdigit(argument[0])) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 19f68e0b631..79d9041bb6d 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -47,13 +47,6 @@ can't normally do this the client should have a bigger max_allowed_packet. */ -#ifdef MYSQL_SERVER -#define USE_QUERY_CACHE -extern uint test_flags; -extern void query_cache_insert(NET *net, const char *packet, ulong length); -#else -#endif - #if defined(__WIN__) || !defined(MYSQL_SERVER) /* The following is because alarms doesn't work on windows. */ #define NO_ALARM @@ -62,15 +55,22 @@ extern void query_cache_insert(NET *net, const char *packet, ulong length); #ifndef NO_ALARM #include "my_pthread.h" void sql_print_error(const char *format,...); +#else +#define DONT_USE_THR_ALARM +#endif /* NO_ALARM */ + +#include "thr_alarm.h" + +#ifdef MYSQL_SERVER +#define USE_QUERY_CACHE +extern uint test_flags; +extern void query_cache_insert(NET *net, const char *packet, ulong length); extern ulong bytes_sent, bytes_received; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; #else #undef statistic_add #define statistic_add(A,B,C) -#define DONT_USE_THR_ALARM -#endif /* NO_ALARM */ - -#include "thr_alarm.h" +#endif #define TEST_BLOCKING 8 #define MAX_PACKET_LENGTH (256L*256L*256L-1) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index e8cbba8aefa..9437e5ecb5b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -564,6 +564,10 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, user_access=acl_user->access; else { + if (global_system_variables.log_warnings) + sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(vio->ssl_)); user_access=NO_ACCESS; break; } @@ -581,6 +585,9 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, acl_user->x509_issuer, ptr)); if (strcmp(acl_user->x509_issuer, ptr)) { + if (global_system_variables.log_warnings) + sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'", + acl_user->x509_issuer, ptr); user_access=NO_ACCESS; free(ptr); break; @@ -596,7 +603,12 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", acl_user->x509_subject, ptr)); if (strcmp(acl_user->x509_subject,ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 subject mismatch: '%s' vs '%s'", + acl_user->x509_subject, ptr); user_access=NO_ACCESS; + } else user_access=acl_user->access; free(ptr); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 6e1cf2e8217..881e59ca53d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1225,7 +1225,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, case SHOW_RPL_STATUS: net_store_data(&packet2, rpl_status_type[(int)rpl_status]); break; -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SHOW_SLAVE_RUNNING: { LOCK_ACTIVE_MI; From 05817b7311aeb6935320247ea535db9c444a1b9a Mon Sep 17 00:00:00 2001 From: "gluh@gluh.mysql.r18.ru" <> Date: Wed, 16 Apr 2003 15:05:47 +0500 Subject: [PATCH 4/7] Fix bug: Crash server when grant ssl options are used and ssl handshake is not set --- sql/sql_acl.cc | 115 +++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9437e5ecb5b..8ee210f4e24 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -542,78 +542,79 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, break; case SSL_TYPE_X509: /* Client should have any valid certificate. */ /* - Connections with non-valid certificates are dropped already - in sslaccept() anyway, so we do not check validity here. + We need to check for absence of SSL because without SSL + we should reject connection. */ - if (SSL_get_peer_certificate(vio->ssl_)) + if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_peer_certificate(vio->ssl_)) user_access=acl_user->access; break; case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ /* - We do not check for absence of SSL because without SSL it does - not pass all checks here anyway. - If cipher name is specified, we compare it to actual cipher in - use. + We need to check for absence of SSL because without SSL + we should reject connection. */ - if (acl_user->ssl_cipher) + if (vio_type(vio) == VIO_TYPE_SSL) { - DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", - acl_user->ssl_cipher, - SSL_get_cipher(vio->ssl_))); - if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))) - user_access=acl_user->access; - else + if (acl_user->ssl_cipher) { - if (global_system_variables.log_warnings) - sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'", - acl_user->ssl_cipher, - SSL_get_cipher(vio->ssl_)); - user_access=NO_ACCESS; - break; + DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(vio->ssl_))); + if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))) + user_access=acl_user->access; + else + { + if (global_system_variables.log_warnings) + sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(vio->ssl_)); + user_access=NO_ACCESS; + break; + } } - } - /* Prepare certificate (if exists) */ - DBUG_PRINT("info",("checkpoint 1")); - X509* cert=SSL_get_peer_certificate(vio->ssl_); - DBUG_PRINT("info",("checkpoint 2")); - /* If X509 issuer is speified, we check it... */ - if (acl_user->x509_issuer) - { - DBUG_PRINT("info",("checkpoint 3")); - char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); - DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", - acl_user->x509_issuer, ptr)); - if (strcmp(acl_user->x509_issuer, ptr)) + /* Prepare certificate (if exists) */ + DBUG_PRINT("info",("checkpoint 1")); + X509* cert=SSL_get_peer_certificate(vio->ssl_); + DBUG_PRINT("info",("checkpoint 2")); + /* If X509 issuer is speified, we check it... */ + if (acl_user->x509_issuer) { - if (global_system_variables.log_warnings) - sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'", - acl_user->x509_issuer, ptr); - user_access=NO_ACCESS; + DBUG_PRINT("info",("checkpoint 3")); + char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + acl_user->x509_issuer, ptr)); + if (strcmp(acl_user->x509_issuer, ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'", + acl_user->x509_issuer, ptr); + user_access=NO_ACCESS; + free(ptr); + break; + } + user_access=acl_user->access; free(ptr); - break; } - user_access=acl_user->access; - free(ptr); - } - DBUG_PRINT("info",("checkpoint 4")); - /* X509 subject is specified, we check it .. */ - if (acl_user->x509_subject) - { - char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); - DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", - acl_user->x509_subject, ptr)); - if (strcmp(acl_user->x509_subject,ptr)) + DBUG_PRINT("info",("checkpoint 4")); + /* X509 subject is specified, we check it .. */ + if (acl_user->x509_subject) { - if (global_system_variables.log_warnings) - sql_print_error("X509 subject mismatch: '%s' vs '%s'", - acl_user->x509_subject, ptr); - user_access=NO_ACCESS; + char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", + acl_user->x509_subject, ptr)); + if (strcmp(acl_user->x509_subject,ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 subject mismatch: '%s' vs '%s'", + acl_user->x509_subject, ptr); + user_access=NO_ACCESS; + } + else + user_access=acl_user->access; + free(ptr); } - else - user_access=acl_user->access; - free(ptr); + break; } - break; } #else /* HAVE_OPENSSL */ user_access=acl_user->access; From d2bfaed9c2261df5a5d7de7da9dbeb49bde38b8e Mon Sep 17 00:00:00 2001 From: "gluh@gluh.mysql.r18.ru" <> Date: Wed, 16 Apr 2003 17:25:05 +0500 Subject: [PATCH 5/7] Fix bug (273):The x509 cert issuer seems not to be checked against the CA --- vio/viossl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vio/viossl.c b/vio/viossl.c index cf1c98b5382..0f34a45f9aa 100644 --- a/vio/viossl.c +++ b/vio/viossl.c @@ -281,7 +281,8 @@ int sslaccept(struct st_VioSSLAcceptorFd* ptr, Vio* vio, long timeout) SSL_SESSION_set_timeout(SSL_get_session(vio->ssl_), timeout); SSL_set_fd(vio->ssl_,vio->sd); SSL_set_accept_state(vio->ssl_); - if (SSL_do_handshake(vio->ssl_) < 1) + if (SSL_do_handshake(vio->ssl_) < 1 || + SSL_get_verify_result(vio->ssl_) != X509_V_OK) { DBUG_PRINT("error", ("SSL_do_handshake failure")); report_errors(); @@ -354,7 +355,8 @@ int sslconnect(struct st_VioSSLConnectorFd* ptr, Vio* vio, long timeout) SSL_SESSION_set_timeout(SSL_get_session(vio->ssl_), timeout); SSL_set_fd (vio->ssl_, vio->sd); SSL_set_connect_state(vio->ssl_); - if (SSL_do_handshake(vio->ssl_) < 1) + if (SSL_do_handshake(vio->ssl_) < 1 || + SSL_get_verify_result(vio->ssl_) != X509_V_OK) { DBUG_PRINT("error", ("SSL_do_handshake failure")); report_errors(); From cde0099e23654a34e35c0227141f91c7c0751842 Mon Sep 17 00:00:00 2001 From: "gluh@gluh.mysql.r18.ru" <> Date: Wed, 16 Apr 2003 17:47:36 +0500 Subject: [PATCH 6/7] Memory leak fixed. --- sql/mysqld.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 24e343017e2..df83d1909cb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -921,6 +921,8 @@ void clean_up(bool print_message) free_max_user_conn(); end_slave_list(); #ifdef HAVE_OPENSSL + if (ssl_acceptor_fd) + my_free((gptr) ssl_acceptor_fd, MYF(MY_ALLOW_ZERO_PTR)); free_des_key_file(); #endif /* HAVE_OPENSSL */ #ifdef USE_REGEX From f841b4ae234359210d240879236653db2dec7281 Mon Sep 17 00:00:00 2001 From: "heikki@hundin.mysql.fi" <> Date: Wed, 16 Apr 2003 16:45:01 +0300 Subject: [PATCH 7/7] Many files: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works --- innobase/buf/buf0flu.c | 12 +- innobase/dict/dict0crea.c | 8 + innobase/dict/dict0dict.c | 674 +++++++++++++++++++++++++++++------ innobase/dict/dict0load.c | 39 +- innobase/ibuf/ibuf0ibuf.c | 13 +- innobase/include/db0err.h | 2 + innobase/include/dict0dict.h | 35 ++ innobase/include/row0ins.h | 1 - innobase/lock/lock0lock.c | 42 ++- innobase/os/os0file.c | 22 +- innobase/row/row0ins.c | 160 ++++++++- innobase/row/row0mysql.c | 121 +++++-- innobase/row/row0sel.c | 35 +- innobase/row/row0upd.c | 2 +- innobase/srv/srv0srv.c | 22 +- innobase/ut/ut0ut.c | 2 +- sql/ha_innodb.cc | 14 + 17 files changed, 999 insertions(+), 205 deletions(-) diff --git a/innobase/buf/buf0flu.c b/innobase/buf/buf0flu.c index 78bde60c9b2..516056b5174 100644 --- a/innobase/buf/buf0flu.c +++ b/innobase/buf/buf0flu.c @@ -106,7 +106,7 @@ buf_flush_ready_for_replace( BUF_BLOCK_FILE_PAGE and in the LRU list*/ { ut_ad(mutex_own(&(buf_pool->mutex))); - ut_ad(block->state == BUF_BLOCK_FILE_PAGE); + ut_a(block->state == BUF_BLOCK_FILE_PAGE); if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0) || (block->buf_fix_count != 0) @@ -227,7 +227,9 @@ buf_flush_buffered_writes(void) } for (i = 0; i < trx_doublewrite->first_free; i++) { + block = trx_doublewrite->buf_block_arr[i]; + ut_a(block->state == BUF_BLOCK_FILE_PAGE); if (block->check_index_page_at_flush && !page_simple_validate(block->frame)) { @@ -236,10 +238,12 @@ buf_flush_buffered_writes(void) ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: Apparent corruption of an index page\n" + " InnoDB: Apparent corruption of an index page n:o %lu in space %lu\n" "InnoDB: to be written to data file. We intentionally crash server\n" "InnoDB: to prevent corrupt data from ending up in data\n" - "InnoDB: files.\n"); + "InnoDB: files.\n", + block->offset, block->space); + ut_a(0); } } @@ -432,6 +436,8 @@ buf_flush_try_page( block = buf_page_hash_get(space, offset); + ut_a(block->state == BUF_BLOCK_FILE_PAGE); + if (flush_type == BUF_FLUSH_LIST && block && buf_flush_ready_for_flush(block, flush_type)) { diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c index b0f84e5663a..3619ac02f4d 100644 --- a/innobase/dict/dict0crea.c +++ b/innobase/dict/dict0crea.c @@ -1173,6 +1173,7 @@ dict_create_add_foreigns_to_dictionary( if (NULL == dict_table_get_low((char *) "SYS_FOREIGN")) { fprintf(stderr, "InnoDB: table SYS_FOREIGN not found from internal data dictionary\n"); + return(DB_ERROR); } @@ -1259,6 +1260,13 @@ loop: "InnoDB: at http://www.innodb.com/ibman.html\n"); } + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Internal error in foreign key constraint creation for table %.500s.\n" +"See the MySQL .err log in the datadir for more information.\n", table->name); + mutex_exit(&dict_foreign_err_mutex); + return(error); } diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index c70e848c5c8..74fe5cd5b70 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -185,6 +185,12 @@ dict_foreign_free( /*==============*/ dict_foreign_t* foreign); /* in, own: foreign key struct */ +/* Buffer for storing detailed information about the latest foreig key +error */ +char* dict_foreign_err_buf = NULL; +mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */ + + /************************************************************************ Checks if the database name in two table names is the same. */ static @@ -573,6 +579,11 @@ dict_init(void) rw_lock_create(&dict_operation_lock); rw_lock_set_level(&dict_operation_lock, SYNC_DICT_OPERATION); + + dict_foreign_err_buf = mem_alloc(DICT_FOREIGN_ERR_BUF_LEN); + dict_foreign_err_buf[0] = '\0'; + mutex_create(&dict_foreign_err_mutex); + mutex_set_level(&dict_foreign_err_mutex, SYNC_ANY_LATCH); } /************************************************************************** @@ -1818,6 +1829,7 @@ dict_foreign_add_to_cache( dict_foreign_t* for_in_cache = NULL; dict_index_t* index; ibool added_to_referenced_list = FALSE; + char* buf = dict_foreign_err_buf; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1850,9 +1862,29 @@ dict_foreign_add_to_cache( for_in_cache->foreign_index); if (index == NULL) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s:\n" +"there is no index in referenced table which would contain\n" +"the columns as the first columns, or the data types in the\n" +"referenced table do not match to the ones in table. Constraint:\n", + for_in_cache->foreign_table_name); + dict_print_info_on_foreign_key_in_create_format( + for_in_cache, buf + strlen(buf)); + if (for_in_cache->foreign_index) { + sprintf(buf + strlen(buf), +"\nThe index in the foreign key in table is %.500s\n" +"See http://www.innodb.com/ibman.html about correct foreign key definition.\n", + for_in_cache->foreign_index->name); + } + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + if (for_in_cache == foreign) { mem_heap_free(foreign->heap); } + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -1871,6 +1903,25 @@ dict_foreign_add_to_cache( for_in_cache->referenced_index); if (index == NULL) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s:\n" +"there is no index in the table which would contain\n" +"the columns as the first columns, or the data types in the\n" +"table do not match to the ones in the referenced table. Constraint:\n", + for_in_cache->foreign_table_name); + dict_print_info_on_foreign_key_in_create_format( + for_in_cache, buf + strlen(buf)); + if (for_in_cache->foreign_index) { + sprintf(buf + strlen(buf), +"\nIndex of the foreign key in the referenced table is %.500s\n" +"See http://www.innodb.com/ibman.html about correct foreign key definition.\n", + for_in_cache->referenced_index->name); + } + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + if (for_in_cache == foreign) { if (added_to_referenced_list) { UT_LIST_REMOVE(referenced_list, @@ -2038,7 +2089,7 @@ dict_scan_col( if (*ptr == '`') { ptr++; } - + return(ptr); } @@ -2141,18 +2192,21 @@ dict_scan_table_name( } /************************************************************************* -Skips one 'word', like an id. For the lexical definition of 'word', see the -code below. */ +Scans an id. For the lexical definition of an 'id', see the code below. +Strips backquotes from around the id. */ static char* -dict_skip_word( -/*===========*/ +dict_scan_id( +/*=========*/ /* out: scanned to */ char* ptr, /* in: scanned to */ - ibool* success)/* out: TRUE if success, FALSE if just spaces left in - string */ + char** start, /* out: start of the id; NULL if no id was + scannable */ + ulint* len) /* out: length of the id */ { - *success = FALSE; + ibool scanned_backquote = FALSE; + + *start = NULL; while (isspace(*ptr)) { ptr++; @@ -2164,20 +2218,58 @@ dict_skip_word( } if (*ptr == '`') { + scanned_backquote = TRUE; ptr++; } - while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != '`' - && *ptr != '\0') { + *start = ptr; + + while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != ')' + && *ptr != '\0' && *ptr != '`') { ptr++; } - *success = TRUE; + *len = (ulint) (ptr - *start); + + if (scanned_backquote) { + if (*ptr == '`') { + ptr++; + } else { + /* Syntax error */ + *start = NULL; + } + } return(ptr); } +/************************************************************************* +Skips one id. */ +static +char* +dict_skip_word( +/*===========*/ + /* out: scanned to */ + char* ptr, /* in: scanned to */ + ibool* success)/* out: TRUE if success, FALSE if just spaces left in + string or a syntax error */ +{ + char* start; + ulint len; + + *success = FALSE; + + ptr = dict_scan_id(ptr, &start, &len); + + if (start) { + *success = TRUE; + } + + return(ptr); +} + +#ifdef currentlynotused /************************************************************************* Returns the number of opening brackets '(' subtracted by the number of closing brackets ')' between string and ptr. */ @@ -2204,6 +2296,106 @@ dict_bracket_count( return(count); } +#endif + +/************************************************************************* +Removes MySQL comments from an SQL string. A comment is either +(a) '#' to the end of the line, +(b) '--' to the end of the line, or +(c) '' till the next '' (like the familiar +C comment syntax). */ +static +char* +dict_strip_comments( +/*================*/ + /* out, own: SQL string stripped from + comments; the caller must free this + with mem_free()! */ + char* sql_string) /* in: SQL string */ +{ + char* str; + char* sptr; + char* ptr; + + str = mem_alloc(strlen(sql_string) + 1); + + sptr = sql_string; + ptr = str; + + for (;;) { + if (*sptr == '\0') { + *ptr = '\0'; + + return(str); + } + + if (*sptr == '#' + || (strlen(sptr) >= 3 && 0 == memcmp("-- ", sptr, 3))) { + for (;;) { + /* In Unix a newline is 0x0D while in Windows + it is 0x0A followed by 0x0D */ + + if (*sptr == (char)0x0A + || *sptr == (char)0x0D + || *sptr == '\0') { + + break; + } + + sptr++; + } + } + + if (strlen(sptr) >= 2 && *sptr == '/' && *(sptr + 1) == '*') { + for (;;) { + if (strlen(sptr) >= 2 + && *sptr == '*' && *(sptr + 1) == '/') { + + sptr += 2; + + break; + } + + if (*sptr == '\0') { + + break; + } + + sptr++; + } + } + + *ptr = *sptr; + + ptr++; + sptr++; + } +} + +/************************************************************************* +Reports a simple foreign key create clause syntax error. */ +static +void +dict_foreign_report_syntax_err( +/*===========================*/ + char* name, /* in: table name */ + char* start_of_latest_foreign,/* in: start of the foreign key clause + in the SQL string */ + char* ptr) /* in: place of the syntax error */ +{ + char* buf = dict_foreign_err_buf; + + mutex_enter(&dict_foreign_err_mutex); + + ut_sprintf_timestamp(buf); + + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s,\n%.500s.\n" +"Syntax error close to:\n%.500s\n", name, start_of_latest_foreign, ptr); + + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); +} /************************************************************************* Scans a table create SQL string and adds to the data dictionary the foreign @@ -2211,10 +2403,10 @@ key constraints declared in the string. This function should be called after the indexes for a table have been created. Each foreign key constraint must be accompanied with indexes in both participating tables. The indexes are allowed to contain more fields than mentioned in the constraint. */ - +static ulint -dict_create_foreign_constraints( -/*============================*/ +dict_create_foreign_constraints_low( +/*================================*/ /* out: error code or DB_SUCCESS */ trx_t* trx, /* in: transaction */ char* sql_string, /* in: table create or ALTER TABLE @@ -2230,7 +2422,9 @@ dict_create_foreign_constraints( dict_table_t* referenced_table; dict_index_t* index; dict_foreign_t* foreign; - char* ptr = sql_string; + char* ptr = sql_string; + char* start_of_latest_foreign = sql_string; + char* buf = dict_foreign_err_buf; ibool success; ulint error; ulint i; @@ -2248,6 +2442,15 @@ dict_create_foreign_constraints( table = dict_table_get_low(name); if (table == NULL) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s.\n" +"Cannot find the table from the internal data dictionary of InnoDB.\n" +"Create table statement:\n%.2000\n", name, sql_string); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_ERROR); } loop: @@ -2263,6 +2466,8 @@ loop: return(error); } + start_of_latest_foreign = ptr; + ptr = dict_accept(ptr, (char *) "FOREIGN", &success); if (!isspace(*ptr)) { @@ -2283,13 +2488,19 @@ loop: ptr = dict_skip_word(ptr, &success); if (!success) { + dict_foreign_report_syntax_err(name, + start_of_latest_foreign, ptr); + return(DB_CANNOT_ADD_CONSTRAINT); } ptr = dict_accept(ptr, (char *) "(", &success); if (!success) { - return(DB_CANNOT_ADD_CONSTRAINT); + /* We do not flag a syntax error here because in an + ALTER TABLE we may also have DROP FOREIGN KEY abc */ + + goto loop; } } @@ -2300,6 +2511,15 @@ col_loop1: ptr = dict_scan_col(ptr, &success, table, columns + i, column_names + i, column_name_lens + i); if (!success) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s,\n%.500s.\n" +"Cannot resolve column name close to:\n%.500s\n", name, + start_of_latest_foreign, ptr); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2314,6 +2534,8 @@ col_loop1: ptr = dict_accept(ptr, (char *) ")", &success); if (!success) { + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2323,12 +2545,24 @@ col_loop1: index = dict_foreign_find_index(table, column_names, i, NULL); if (!index) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s:\n" +"There is no index in the table %.500s where the columns appear\n" +"as the first columns. Constraint:\n%.500s\n" +"See http://www.innodb.com/ibman.html for correct foreign key definition.\n", + name, name, start_of_latest_foreign); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } - ptr = dict_accept(ptr, (char *) "REFERENCES", &success); if (!success || !isspace(*ptr)) { + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2358,6 +2592,15 @@ col_loop1: if (!success || (!referenced_table && trx->check_foreigns)) { dict_foreign_free(foreign); + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s,\n%.500s.\n" +"Cannot resolve table name close to:\n" +"%.500s\n", name, start_of_latest_foreign, ptr); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2365,7 +2608,8 @@ col_loop1: if (!success) { dict_foreign_free(foreign); - + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2380,6 +2624,15 @@ col_loop2: if (!success) { dict_foreign_free(foreign); + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s,\n%.500s\n" +"Cannot resolve column name close to:\n" +"%.500s\n", name, start_of_latest_foreign, ptr); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2394,6 +2647,8 @@ col_loop2: if (!success || foreign->n_fields != i) { dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2416,9 +2671,10 @@ scan_on_conditions: ptr = dict_accept(ptr, "UPDATE", &success); if (!success) { - dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, + start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2454,6 +2710,8 @@ scan_on_conditions: if (!success) { dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, + start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2471,7 +2729,8 @@ scan_on_conditions: if (!success) { dict_foreign_free(foreign); - + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2479,7 +2738,8 @@ scan_on_conditions: if (!success) { dict_foreign_free(foreign); - + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2493,6 +2753,15 @@ scan_on_conditions: dict_foreign_free(foreign); + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s,\n%.500s.\n" +"You have defined a SET NULL condition though some of the\n" +"columns is defined as NOT NULL.\n", name, start_of_latest_foreign); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } } @@ -2511,6 +2780,15 @@ try_find_index: dict_foreign_free(foreign); + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s,\n%.500s.\n" +"You have twice an ON DELETE clause or twice an ON UPDATE clause.\n", + name, start_of_latest_foreign); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -2524,6 +2802,18 @@ try_find_index: foreign->foreign_index); if (!index) { dict_foreign_free(foreign); + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in foreign key constraint of table %.500s:\n" +"Cannot find an index in the referenced table where the\n" +"referenced columns appear as the first columns, or column types\n" +"in the table and the referenced table do not match for constraint:\n%.500s\n" +"See http://www.innodb.com/ibman.html for correct foreign key definition.\n", + name, start_of_latest_foreign); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { @@ -2564,6 +2854,165 @@ try_find_index: goto loop; } +/************************************************************************* +Scans a table create SQL string and adds to the data dictionary the foreign +key constraints declared in the string. This function should be called after +the indexes for a table have been created. Each foreign key constraint must +be accompanied with indexes in both participating tables. The indexes are +allowed to contain more fields than mentioned in the constraint. */ + +ulint +dict_create_foreign_constraints( +/*============================*/ + /* out: error code or DB_SUCCESS */ + trx_t* trx, /* in: transaction */ + char* sql_string, /* in: table create or ALTER TABLE + statement where foreign keys are declared like: + FOREIGN KEY (a, b) REFERENCES table2(c, d), + table2 can be written also with the database + name before it: test.table2; the default + database id the database of parameter name */ + char* name) /* in: table full name in the normalized form + database_name/table_name */ +{ + char* str; + ulint err; + + str = dict_strip_comments(sql_string); + + err = dict_create_foreign_constraints_low(trx, str, name); + + mem_free(str); + + return(err); +} + +/************************************************************************** +Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */ + +ulint +dict_foreign_parse_drop_constraints( +/*================================*/ + /* out: DB_SUCCESS or + DB_CANNOT_DROP_CONSTRAINT if + syntax error or the constraint + id does not match */ + mem_heap_t* heap, /* in: heap from which we can + allocate memory */ + trx_t* trx, /* in: transaction */ + dict_table_t* table, /* in: table */ + ulint* n, /* out: number of constraints + to drop */ + char*** constraints_to_drop) /* out: id's of the + constraints to drop */ +{ + dict_foreign_t* foreign; + ibool success; + char* str; + char* ptr; + char* buf = dict_foreign_err_buf; + char* start; + char* id; + ulint len; + + *n = 0; + + *constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*)); + + str = dict_strip_comments(*(trx->mysql_query_str)); + ptr = str; + + ut_ad(mutex_own(&(dict_sys->mutex))); +loop: + ptr = dict_scan_to(ptr, (char *) "DROP"); + + if (*ptr == '\0') { + ut_a(*n < 1000); + + mem_free(str); + + return(DB_SUCCESS); + } + + ptr = dict_accept(ptr, (char *) "DROP", &success); + + if (!isspace(*ptr)) { + + goto loop; + } + + ptr = dict_accept(ptr, (char *) "FOREIGN", &success); + + if (!success) { + + goto loop; + } + + ptr = dict_accept(ptr, (char *) "KEY", &success); + + if (!success) { + + goto syntax_error; + } + + ptr = dict_scan_id(ptr, &start, &len); + + if (start == NULL) { + + goto syntax_error; + } + + id = mem_heap_alloc(heap, len + 1); + ut_memcpy(id, start, len); + id[len] = '\0'; + (*constraints_to_drop)[*n] = id; + (*n)++; + + /* Look for the given constraint id */ + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign != NULL) { + if (0 == ut_strcmp(foreign->id, id)) { + + /* Found */ + break; + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + if (foreign == NULL) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Error in dropping of a foreign key constraint of table %.500s,\n" +"just before:\n%s\n in SQL command\n%s\nCannot find a constraint with the\n" +"given id %s.\n", table->name, ptr, str, id); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + + mem_free(str); + + return(DB_CANNOT_DROP_CONSTRAINT); + } + + goto loop; + +syntax_error: + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), +" Syntax error in dropping of a foreign key constraint of table %.500s,\n" +"close to:\n%s\n in SQL command\n%s\n", table->name, ptr, str); + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + + mem_free(str); + + return(DB_CANNOT_DROP_CONSTRAINT); +} + /*==================== END OF FOREIGN KEY PROCESSING ====================*/ /************************************************************************** @@ -3285,7 +3734,6 @@ dict_index_print_low( n_vals = index->stat_n_diff_key_vals[1]; } - printf( " INDEX: name %s, table name %s, id %lu %lu, fields %lu/%lu, type %lu\n", index->name, index->table_name, @@ -3326,6 +3774,99 @@ dict_field_print_low( printf(" %s", field->name); } +/************************************************************************** +Sprintfs to a string info on a foreign key of a table in a format suitable +for CREATE TABLE. */ + +char* +dict_print_info_on_foreign_key_in_create_format( +/*============================================*/ + /* out: how far in buf we printed */ + dict_foreign_t* foreign,/* in: foreign key constraint */ + char* buf) /* in: buffer of at least 5000 bytes */ +{ + char* buf2 = buf; + ulint i; + + buf2 += sprintf(buf2, ",\n CONSTRAINT `%s` FOREIGN KEY (", + foreign->id); + for (i = 0; i < foreign->n_fields; i++) { + if ((ulint)(buf2 - buf) >= 4000) { + + goto no_space; + } + buf2 += sprintf(buf2, "`%.250s`", + foreign->foreign_col_names[i]); + + if (i + 1 < foreign->n_fields) { + buf2 += sprintf(buf2, ", "); + } + } + + if (dict_tables_have_same_db(foreign->foreign_table_name, + foreign->referenced_table_name)) { + /* Do not print the database name of the referenced + table */ + buf2 += sprintf(buf2, ") REFERENCES `%.500s` (", + dict_remove_db_name( + foreign->referenced_table_name)); + } else { + buf2 += sprintf(buf2, ") REFERENCES `%.500s` (", + foreign->referenced_table_name); + /* Change the '/' in the table name to '.' */ + + for (i = ut_strlen(buf); i > 0; i--) { + if (buf[i] == '/') { + + buf[i] = '.'; + + break; + } + } + } + + for (i = 0; i < foreign->n_fields; i++) { + if ((ulint)(buf2 - buf) >= 4000) { + + goto no_space; + } + buf2 += sprintf(buf2, "`%.250s`", + foreign->referenced_col_names[i]); + if (i + 1 < foreign->n_fields) { + buf2 += sprintf(buf2, ", "); + } + } + + buf2 += sprintf(buf2, ")"); + + if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) { + buf2 += sprintf(buf2, " ON DELETE CASCADE"); + } + + if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) { + buf2 += sprintf(buf2, " ON DELETE SET NULL"); + } + + if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { + buf2 += sprintf(buf2, " ON DELETE NO ACTION"); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { + buf2 += sprintf(buf2, " ON UPDATE CASCADE"); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { + buf2 += sprintf(buf2, " ON UPDATE SET NULL"); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { + buf2 += sprintf(buf2, " ON UPDATE NO ACTION"); + } + +no_space: + return(buf2); +} + /************************************************************************** Sprintfs to a string info on foreign keys of a table in a format suitable for CREATE TABLE. */ @@ -3335,13 +3876,12 @@ dict_print_info_on_foreign_keys_in_create_format( /*=============================================*/ char* buf, /* in: auxiliary buffer */ char* str, /* in/out: pointer to a string */ - ulint len, /* in: str has to be a buffer at least - len + 5000 bytes */ + ulint len, /* in: buf has to be a buffer of at least + len + 5000 bytes; str must have at least + len + 1 bytes */ dict_table_t* table) /* in: table */ { - dict_foreign_t* foreign; - ulint i; char* buf2; buf2 = buf; @@ -3357,78 +3897,12 @@ dict_print_info_on_foreign_keys_in_create_format( } while (foreign != NULL) { - buf2 += sprintf(buf2, ",\n FOREIGN KEY ("); + if ((ulint)(buf2 - buf) >= len) { + goto no_space; + } - for (i = 0; i < foreign->n_fields; i++) { - if ((ulint)(buf2 - buf) >= len) { - goto no_space; - } - buf2 += sprintf(buf2, "`%s`", - foreign->foreign_col_names[i]); - - if (i + 1 < foreign->n_fields) { - buf2 += sprintf(buf2, ", "); - } - } - - if (dict_tables_have_same_db(table->name, - foreign->referenced_table_name)) { - /* Do not print the database name of the referenced - table */ - buf2 += sprintf(buf2, ") REFERENCES `%s` (", - dict_remove_db_name( - foreign->referenced_table_name)); - } else { - buf2 += sprintf(buf2, ") REFERENCES `%s` (", - foreign->referenced_table_name); - /* Change the '/' in the table name to '.' */ - - for (i = ut_strlen(buf); i > 0; i--) { - if (buf[i] == '/') { - - buf[i] = '.'; - - break; - } - } - } - - for (i = 0; i < foreign->n_fields; i++) { - if ((ulint)(buf2 - buf) >= len) { - goto no_space; - } - buf2 += sprintf(buf2, "`%s`", - foreign->referenced_col_names[i]); - if (i + 1 < foreign->n_fields) { - buf2 += sprintf(buf2, ", "); - } - } - - buf2 += sprintf(buf2, ")"); - - if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) { - buf2 += sprintf(buf2, " ON DELETE CASCADE"); - } - - if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) { - buf2 += sprintf(buf2, " ON DELETE SET NULL"); - } - - if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { - buf2 += sprintf(buf2, " ON DELETE NO ACTION"); - } - - if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { - buf2 += sprintf(buf2, " ON UPDATE CASCADE"); - } - - if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { - buf2 += sprintf(buf2, " ON UPDATE SET NULL"); - } - - if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { - buf2 += sprintf(buf2, " ON UPDATE NO ACTION"); - } + buf2 = dict_print_info_on_foreign_key_in_create_format( + foreign, buf2); foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } @@ -3489,7 +3963,7 @@ dict_print_info_on_foreign_keys( goto no_space; } - buf2 += sprintf(buf2, "%s", + buf2 += sprintf(buf2, "%.500s", foreign->foreign_col_names[i]); if (i + 1 < foreign->n_fields) { @@ -3497,14 +3971,14 @@ dict_print_info_on_foreign_keys( } } - buf2 += sprintf(buf2, ") REFER %s(", + buf2 += sprintf(buf2, ") REFER %.500s(", foreign->referenced_table_name); for (i = 0; i < foreign->n_fields; i++) { if ((ulint)(buf2 - buf) >= len) { goto no_space; } - buf2 += sprintf(buf2, "%s", + buf2 += sprintf(buf2, "%.500s", foreign->referenced_col_names[i]); if (i + 1 < foreign->n_fields) { buf2 += sprintf(buf2, " "); diff --git a/innobase/dict/dict0load.c b/innobase/dict/dict0load.c index d8d426d2036..8f39605e493 100644 --- a/innobase/dict/dict0load.c +++ b/innobase/dict/dict0load.c @@ -456,7 +456,7 @@ dict_load_indexes( ut_ad(len == 8); id = mach_read_from_8(field); - ut_a(0 == ut_strcmp("NAME", + ut_a(0 == ut_strcmp((char*)"NAME", dict_field_get_col( dict_index_get_nth_field( dict_table_get_first_index(sys_indexes), 4))->name)); @@ -515,7 +515,7 @@ dict_load_indexes( && ((type & DICT_CLUSTERED) || ((table == dict_sys->sys_tables) && (name_len == ut_strlen("ID_IND")) - && (0 == ut_memcmp(name_buf, "ID_IND", + && (0 == ut_memcmp(name_buf, (char*)"ID_IND", name_len))))) { /* The index was created in memory already in @@ -566,6 +566,7 @@ dict_load_table( char* buf; ulint space; ulint n_cols; + ulint err; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -674,8 +675,25 @@ dict_load_table( dict_load_indexes(table, heap); - ut_a(DB_SUCCESS == dict_load_foreigns(table->name)); + err = dict_load_foreigns(table->name); +/* + if (err != DB_SUCCESS) { + + mutex_enter(&dict_foreign_err_mutex); + ut_print_timestamp(stderr); + + fprintf(stderr, +" InnoDB: Error: could not make a foreign key definition to match\n" +"InnoDB: the foreign key table or the referenced table!\n" +"InnoDB: The data dictionary of InnoDB is corrupt. You may need to drop\n" +"InnoDB: and recreate the foreign key table or the referenced table.\n" +"InnoDB: Send a detailed bug report to mysql@lists.mysql.com\n" +"InnoDB: Latest foreign key error printout:\n%s\n", dict_foreign_err_buf); + + mutex_exit(&dict_foreign_err_mutex); + } +*/ mem_heap_free(heap); return(table); @@ -978,8 +996,8 @@ dict_load_foreign( field = rec_get_nth_field(rec, 4, &len); - foreign->referenced_table_name = mem_heap_alloc(foreign->heap, 1 + len); - + foreign->referenced_table_name = mem_heap_alloc(foreign->heap, + 1 + len); ut_memcpy(foreign->referenced_table_name, field, len); foreign->referenced_table_name[len] = '\0'; @@ -988,10 +1006,19 @@ dict_load_foreign( dict_load_foreign_cols(id, foreign); + /* If the foreign table is not yet in the dictionary cache, we + have to load it so that we are able to make type comparisons + in the next function call. */ + + dict_table_get_low(foreign->foreign_table_name); + /* Note that there may already be a foreign constraint object in the dictionary cache for this constraint: then the following call only sets the pointers in it to point to the appropriate table - and index objects and frees the newly created object foreign. */ + and index objects and frees the newly created object foreign. + Adding to the cache should always succeed since we are not creating + a new foreign key constraint but loading one from the data + dictionary. */ err = dict_foreign_add_to_cache(foreign); diff --git a/innobase/ibuf/ibuf0ibuf.c b/innobase/ibuf/ibuf0ibuf.c index 5cd066afc27..187afa17047 100644 --- a/innobase/ibuf/ibuf0ibuf.c +++ b/innobase/ibuf/ibuf0ibuf.c @@ -2391,7 +2391,7 @@ ibuf_delete_rec( ut_ad(ibuf_inside()); - success = btr_cur_optimistic_delete(btr_pcur_get_btr_cur(pcur), mtr); + success = btr_cur_optimistic_delete(btr_pcur_get_btr_cur(pcur), mtr); if (success) { #ifdef UNIV_IBUF_DEBUG @@ -2401,7 +2401,7 @@ ibuf_delete_rec( return(FALSE); } - /* We have to resort to a pessimistic delete from ibuf */ + /* We have to resort to a pessimistic delete from ibuf */ btr_pcur_store_position(pcur, mtr); btr_pcur_commit_specify_mtr(pcur, mtr); @@ -2420,17 +2420,22 @@ ibuf_delete_rec( fprintf(stderr, "InnoDB: ibuf cursor restoration fails!\n"); fprintf(stderr, "InnoDB: ibuf record inserted to page %lu\n", page_no); + fflush(stderr); + rec_print(btr_pcur_get_rec(pcur)); rec_print(pcur->old_rec); dtuple_print(search_tuple); rec_print(page_rec_get_next(btr_pcur_get_rec(pcur))); + fflush(stdout); mtr_commit(mtr); fprintf(stderr, "InnoDB: Validating insert buffer tree:\n"); - ut_a(btr_validate_tree(ibuf_data->index->tree)); - fprintf(stderr, "InnoDB: Ibuf tree ok\n"); + ut_a(btr_validate_tree(ibuf_data->index->tree)); + + fprintf(stderr, "InnoDB: ibuf tree ok\n"); + fflush(stderr); } ut_a(success); diff --git a/innobase/include/db0err.h b/innobase/include/db0err.h index c67c09bad27..ab7d0caa35c 100644 --- a/innobase/include/db0err.h +++ b/innobase/include/db0err.h @@ -44,6 +44,8 @@ Created 5/24/1996 Heikki Tuuri #define DB_CORRUPTION 39 /* data structure corruption noticed */ #define DB_COL_APPEARS_TWICE_IN_INDEX 40 /* InnoDB cannot handle an index where same column appears twice */ +#define DB_CANNOT_DROP_CONSTRAINT 40 /* dropping a foreign key constraint + from a table failed */ /* The following are partial failure codes */ #define DB_FAIL 1000 diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index b5e6e04a1de..600965700ed 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -219,6 +219,24 @@ dict_create_foreign_constraints( char* name); /* in: table full name in the normalized form database_name/table_name */ /************************************************************************** +Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */ + +ulint +dict_foreign_parse_drop_constraints( +/*================================*/ + /* out: DB_SUCCESS or + DB_CANNOT_DROP_CONSTRAINT if + syntax error or the constraint + id does not match */ + mem_heap_t* heap, /* in: heap from which we can + allocate memory */ + trx_t* trx, /* in: transaction */ + dict_table_t* table, /* in: table */ + ulint* n, /* out: number of constraints + to drop */ + char*** constraints_to_drop); /* out: id's of the + constraints to drop */ +/************************************************************************** Returns a table object and memoryfixes it. NOTE! This is a high-level function to be used mainly from outside the 'dict' directory. Inside this directory dict_table_get_low is usually the appropriate function. */ @@ -333,6 +351,16 @@ dict_print_info_on_foreign_keys( char* str, /* in/out: pointer to a string */ ulint len, /* in: space in str available for info */ dict_table_t* table); /* in: table */ +/************************************************************************** +Sprintfs to a string info on a foreign key of a table in a format suitable +for CREATE TABLE. */ + +char* +dict_print_info_on_foreign_key_in_create_format( +/*============================================*/ + /* out: how far in buf we printed */ + dict_foreign_t* foreign,/* in: foreign key constraint */ + char* buf); /* in: buffer of at least 5000 bytes */ /************************************************************************ Gets the first index on the table (the clustered index). */ UNIV_INLINE @@ -808,6 +836,13 @@ void dict_mutex_exit_for_mysql(void); /*===========================*/ +/* The following len must be at least 10000 bytes! */ +#define DICT_FOREIGN_ERR_BUF_LEN 10000 + +/* Buffer for storing detailed information about the latest foreig key +error */ +extern char* dict_foreign_err_buf; +extern mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */ extern dict_sys_t* dict_sys; /* the dictionary system */ extern rw_lock_t dict_operation_lock; diff --git a/innobase/include/row0ins.h b/innobase/include/row0ins.h index cc3b9fa7e9a..a5b4b74e7fc 100644 --- a/innobase/include/row0ins.h +++ b/innobase/include/row0ins.h @@ -35,7 +35,6 @@ row_ins_check_foreign_constraint( dictionary cache if they exist at all */ dict_table_t* table, /* in: if check_ref is TRUE, then the foreign table, else the referenced table */ - dict_index_t* index, /* in: index in table */ dtuple_t* entry, /* in: index entry for index */ que_thr_t* thr); /* in: query thread */ /************************************************************************* diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 7b08d6b89b8..4bb1d243ed4 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -3092,8 +3092,7 @@ lock_deadlock_recursive( err_buf += strlen(err_buf); err_buf += sprintf(err_buf, - " LATEST DETECTED DEADLOCK:\n" - "*** (1) TRANSACTION:\n"); + "\n*** (1) TRANSACTION:\n"); trx_print(err_buf, wait_lock->trx); err_buf += strlen(err_buf); @@ -3934,24 +3933,15 @@ lock_print_info( return; } - - buf += sprintf(buf, "Trx id counter %lu %lu\n", - ut_dulint_get_high(trx_sys->max_trx_id), - ut_dulint_get_low(trx_sys->max_trx_id)); - - buf += sprintf(buf, - "Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n", - ut_dulint_get_high(purge_sys->purge_trx_no), - ut_dulint_get_low(purge_sys->purge_trx_no), - ut_dulint_get_high(purge_sys->purge_undo_no), - ut_dulint_get_low(purge_sys->purge_undo_no)); lock_mutex_enter_kernel(); - buf += sprintf(buf, - "Total number of lock structs in row lock hash table %lu\n", - lock_get_n_rec_locks()); if (lock_deadlock_found) { + + buf += sprintf(buf, +"------------------------\n" +"LATEST DETECTED DEADLOCK\n" +"------------------------\n"); if ((ulint)(buf_end - buf) < 100 + strlen(lock_latest_err_buf)) { @@ -3972,6 +3962,26 @@ lock_print_info( return; } + buf += sprintf(buf, +"------------\n" +"TRANSACTIONS\n" +"------------\n"); + + buf += sprintf(buf, "Trx id counter %lu %lu\n", + ut_dulint_get_high(trx_sys->max_trx_id), + ut_dulint_get_low(trx_sys->max_trx_id)); + + buf += sprintf(buf, + "Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n", + ut_dulint_get_high(purge_sys->purge_trx_no), + ut_dulint_get_low(purge_sys->purge_trx_no), + ut_dulint_get_high(purge_sys->purge_undo_no), + ut_dulint_get_low(purge_sys->purge_undo_no)); + + buf += sprintf(buf, + "Total number of lock structs in row lock hash table %lu\n", + lock_get_n_rec_locks()); + buf += sprintf(buf, "LIST OF TRANSACTIONS FOR EACH SESSION:\n"); /* First print info on non-active transactions */ diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 6324fcdbef1..1d1d84adda7 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -214,9 +214,14 @@ os_file_get_last_error(void) "InnoDB: the directory. It may also be you have created a subdirectory\n" "InnoDB: of the same name as a data file.\n"); } else { - fprintf(stderr, - "InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n" - "InnoDB: what the error number means.\n"); + if (strerror((int)err) != NULL) { + fprintf(stderr, + "InnoDB: Error number %lu means '%s'.\n", err, strerror((int)err)); + } + + fprintf(stderr, + "InnoDB: See also section 13.2 at http://www.innodb.com/ibman.html\n" + "InnoDB: about operating system error numbers.\n"); } } @@ -252,9 +257,14 @@ os_file_get_last_error(void) "InnoDB: The error means mysqld does not have the access rights to\n" "InnoDB: the directory.\n"); } else { - fprintf(stderr, - "InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n" - "InnoDB: what the error number means or use the perror program of MySQL.\n"); + if (strerror((int)err) != NULL) { + fprintf(stderr, + "InnoDB: Error number %lu means '%s'.\n", err, strerror((int)err)); + } + + fprintf(stderr, + "InnoDB: See also section 13.2 at http://www.innodb.com/ibman.html\n" + "InnoDB: about operating system error numbers.\n"); } } diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 990ef99b2a4..1a616b74756 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -437,6 +437,111 @@ row_ins_cascade_calc_update_vec( return(n_fields_updated); } +/************************************************************************* +Reports a foreign key error associated with an update or a delete of a +parent table index entry. */ +static +void +row_ins_foreign_report_err( +/*=======================*/ + char* errstr, /* in: error string from the viewpoint + of the parent table */ + que_thr_t* thr, /* in: query thread whose run_node + is an update node */ + dict_foreign_t* foreign, /* in: foreign key constraint */ + rec_t* rec, /* in: a matching index record in the + child table */ + dtuple_t* entry) /* in: index entry in the parent + table */ +{ + char* buf = dict_foreign_err_buf; + + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), " Transaction:\n"); + trx_print(buf + strlen(buf), thr_get_trx(thr)); + + sprintf(buf + strlen(buf), +"Foreign key constraint fails for table %.500s:\n", + foreign->foreign_table_name); + dict_print_info_on_foreign_key_in_create_format( + foreign, buf + strlen(buf)); + sprintf(buf + strlen(buf), "\n%s", errstr); + sprintf(buf + strlen(buf), +" in parent table, in index %.500s tuple:\n", + foreign->referenced_index->name); + if (entry) { + dtuple_sprintf(buf + strlen(buf), 1000, entry); + } + sprintf(buf + strlen(buf), +"\nBut in child table %.500s, in index %.500s, there is a record:\n", + foreign->foreign_table_name, foreign->foreign_index->name); + if (rec) { + rec_sprintf(buf + strlen(buf), 1000, rec); + } + sprintf(buf + strlen(buf), "\n"); + + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + + mutex_exit(&dict_foreign_err_mutex); +} + +/************************************************************************* +Reports a foreign key error to dict_foreign_err_buf when we are trying +to add an index entry to a child table. Note that the adding may be the result +of an update, too. */ +static +void +row_ins_foreign_report_add_err( +/*===========================*/ + que_thr_t* thr, /* in: query thread whose run_node + is an insert node */ + dict_foreign_t* foreign, /* in: foreign key constraint */ + rec_t* rec, /* in: a record in the parent table: + it does not match entry because we + have an error! */ + dtuple_t* entry) /* in: index entry to insert in the + child table */ +{ + char* buf = dict_foreign_err_buf; + + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), " Transaction:\n"); + trx_print(buf + strlen(buf), thr_get_trx(thr)); + sprintf(buf + strlen(buf), +"Foreign key constraint fails for table %.500s:\n", + foreign->foreign_table_name); + dict_print_info_on_foreign_key_in_create_format( + foreign, buf + strlen(buf)); + sprintf(buf + strlen(buf), +"\nTrying to add in child table, in index %.500s tuple:\n", + foreign->foreign_index->name); + if (entry) { + dtuple_sprintf(buf + strlen(buf), 1000, entry); + } + sprintf(buf + strlen(buf), +"\nBut in parent table %.500s, in index %.500s,\n" +"the closest match we can find is record:\n", + foreign->referenced_table_name, + foreign->referenced_index->name); + if (rec && page_rec_is_supremum(rec)) { + /* If the cursor ended on a supremum record, it is better + to report the previous record in the error message, so that + the user gets a more descriptive error message. */ + rec = page_rec_get_prev(rec); + } + + if (rec) { + rec_sprintf(buf + strlen(buf), 1000, rec); + } + sprintf(buf + strlen(buf), "\n"); + + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + + mutex_exit(&dict_foreign_err_mutex); +} + /************************************************************************* Perform referential actions or checks when a parent row is deleted or updated and the constraint had an ON DELETE or ON UPDATE condition which was not @@ -453,6 +558,8 @@ row_ins_foreign_check_on_constraint( type is != 0 */ btr_pcur_t* pcur, /* in: cursor placed on a matching index record in the child table */ + dtuple_t* entry, /* in: index entry in the parent + table */ mtr_t* mtr) /* in: mtr holding the latch of pcur page */ { @@ -506,6 +613,10 @@ row_ins_foreign_check_on_constraint( return(DB_SUCCESS); } + row_ins_foreign_report_err((char*)"Trying to delete", + thr, foreign, + btr_pcur_get_rec(pcur), entry); + return(DB_ROW_IS_REFERENCED); } @@ -523,6 +634,10 @@ row_ins_foreign_check_on_constraint( return(DB_SUCCESS); } + row_ins_foreign_report_err((char*)"Trying to update", + thr, foreign, + btr_pcur_get_rec(pcur), entry); + return(DB_ROW_IS_REFERENCED); } @@ -580,6 +695,10 @@ row_ins_foreign_check_on_constraint( err = DB_ROW_IS_REFERENCED; + row_ins_foreign_report_err( +(char*)"Trying an update, possibly causing a cyclic cascaded update\n" +"in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry); + goto nonstandard_exit_func; } @@ -809,11 +928,10 @@ row_ins_check_foreign_constraint( dictionary cache if they exist at all */ dict_table_t* table, /* in: if check_ref is TRUE, then the foreign table, else the referenced table */ - dict_index_t* index __attribute__((unused)),/* in: index in table */ dtuple_t* entry, /* in: index entry for index */ que_thr_t* thr) /* in: query thread */ { - upd_node_t* upd_node; + upd_node_t* upd_node; dict_table_t* check_table; dict_index_t* check_index; ulint n_fields_cmp; @@ -824,6 +942,7 @@ row_ins_check_foreign_constraint( int cmp; ulint err; ulint i; + char* buf = dict_foreign_err_buf; mtr_t mtr; run_again: @@ -884,6 +1003,25 @@ run_again: if (check_table == NULL) { if (check_ref) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), " Transaction:\n"); + trx_print(buf + strlen(buf), thr_get_trx(thr)); + sprintf(buf + strlen(buf), +"Foreign key constraint fails for table %.500s:\n", + foreign->foreign_table_name); + dict_print_info_on_foreign_key_in_create_format( + foreign, buf + strlen(buf)); + sprintf(buf + strlen(buf), +"\nTrying to add to index %.500s tuple:\n", foreign->foreign_index->name); + dtuple_sprintf(buf + strlen(buf), 1000, entry); + sprintf(buf + strlen(buf), +"\nBut the parent table %.500s does not currently exist!\n", + foreign->referenced_table_name); + + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + mutex_exit(&dict_foreign_err_mutex); + return(DB_NO_REFERENCED_ROW); } @@ -949,7 +1087,8 @@ run_again: if (cmp == 0) { if (rec_get_deleted_flag(rec)) { - err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, + err = row_ins_set_shared_rec_lock( + LOCK_ORDINARY, rec, check_index, thr); if (err != DB_SUCCESS) { @@ -989,13 +1128,17 @@ run_again: err = row_ins_foreign_check_on_constraint( - thr, foreign, &pcur, &mtr); - + thr, foreign, &pcur, entry, + &mtr); if (err != DB_SUCCESS) { break; } } else { + row_ins_foreign_report_err( + (char*)"Trying to delete or update", + thr, foreign, rec, entry); + err = DB_ROW_IS_REFERENCED; break; } @@ -1012,6 +1155,8 @@ run_again: if (check_ref) { err = DB_NO_REFERENCED_ROW; + row_ins_foreign_report_add_err( + thr, foreign, rec, entry); } else { err = DB_SUCCESS; } @@ -1025,6 +1170,9 @@ next_rec: if (!moved) { if (check_ref) { + rec = btr_pcur_get_rec(&pcur); + row_ins_foreign_report_add_err( + thr, foreign, rec, entry); err = DB_NO_REFERENCED_ROW; } else { err = DB_SUCCESS; @@ -1100,7 +1248,7 @@ row_ins_check_foreign_constraints( } err = row_ins_check_foreign_constraint(TRUE, foreign, - table, index, entry, thr); + table, entry, thr); if (got_s_lock) { row_mysql_unfreeze_data_dictionary(trx); } diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 0ca1a7516e0..81ed94e2628 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -6,7 +6,7 @@ Contains also create table and other data dictionary operations. Created 9/17/2000 Heikki Tuuri *******************************************************/ - + #include "row0mysql.h" #ifdef UNIV_NONINL @@ -1156,7 +1156,7 @@ row_mysql_recover_tmp_table( return(DB_ERROR); } - if (0 == ut_memcmp(ptr, "/rsql", 5)) { + if (0 == ut_memcmp(ptr, (char*)"/rsql", 5)) { ptr++; *ptr = '#'; @@ -1280,10 +1280,10 @@ row_create_table_for_mysql( } trx->op_info = (char *) "creating table"; - - if (0 == ut_strcmp(table->name, "mysql/host") - || 0 == ut_strcmp(table->name, "mysql/user") - || 0 == ut_strcmp(table->name, "mysql/db")) { + + if (0 == ut_strcmp(table->name, (char*)"mysql/host") + || 0 == ut_strcmp(table->name, (char*)"mysql/user") + || 0 == ut_strcmp(table->name, (char*)"mysql/db")) { fprintf(stderr, "InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n" @@ -1303,7 +1303,7 @@ row_create_table_for_mysql( if (namelen >= keywordlen && 0 == ut_memcmp(table->name + namelen - keywordlen, - "_recover_innodb_tmp_table", keywordlen)) { + (char*)"_recover_innodb_tmp_table", keywordlen)) { /* MySQL prevents accessing of tables whose name begins with #sql, that is temporary tables. If mysqld crashes in @@ -1371,7 +1371,7 @@ row_create_table_for_mysql( if (namelen >= keywordlen && 0 == ut_memcmp(table->name + namelen - keywordlen, - "innodb_mem_validate", keywordlen)) { + (char*)"innodb_mem_validate", keywordlen)) { /* We define here a debugging feature intended for developers */ @@ -1481,7 +1481,7 @@ row_create_index_for_mysql( if (namelen >= keywordlen && 0 == ut_memcmp( index->table_name + namelen - keywordlen, - "_recover_innodb_tmp_table", keywordlen)) { + (char*)"_recover_innodb_tmp_table", keywordlen)) { return(DB_SUCCESS); } @@ -1586,7 +1586,7 @@ row_table_add_foreign_constraints( if (namelen >= keywordlen && 0 == ut_memcmp( name + namelen - keywordlen, - "_recover_innodb_tmp_table", keywordlen)) { + (char*)"_recover_innodb_tmp_table", keywordlen)) { return(DB_SUCCESS); } @@ -1808,7 +1808,6 @@ row_drop_table_for_mysql( ulint len; ulint namelen; ulint keywordlen; - ulint rounds = 0; ibool locked_dictionary = FALSE; char buf[10000]; @@ -2155,7 +2154,7 @@ row_is_mysql_tmp_table_name( ulint i; for (i = 0; i <= ut_strlen(name) - 5; i++) { - if (ut_memcmp(name + i, "/#sql", 5) == 0) { + if (ut_memcmp(name + i, (char*)"/#sql", 5) == 0) { return(TRUE); } @@ -2177,12 +2176,16 @@ row_rename_table_for_mysql( { dict_table_t* table; que_thr_t* thr; - que_t* graph; + que_t* graph = NULL; ulint err; char* str1; char* str2; char* str3; + mem_heap_t* heap = NULL; + char** constraints_to_drop = NULL; + ulint n_constraints_to_drop = 0; ulint len; + ulint i; char buf[10000]; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); @@ -2200,10 +2203,10 @@ row_rename_table_for_mysql( trx_commit_for_mysql(trx); return(DB_ERROR); } - - if (0 == ut_strcmp(new_name, "mysql/host") - || 0 == ut_strcmp(new_name, "mysql/user") - || 0 == ut_strcmp(new_name, "mysql/db")) { + + if (0 == ut_strcmp(new_name, (char*)"mysql/host") + || 0 == ut_strcmp(new_name, (char*)"mysql/user") + || 0 == ut_strcmp(new_name, (char*)"mysql/db")) { fprintf(stderr, "InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n" @@ -2217,6 +2220,19 @@ row_rename_table_for_mysql( trx->op_info = (char *) "renaming table"; trx_start_if_not_started(trx); + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + row_mysql_lock_data_dictionary(trx); + + table = dict_table_get_low(old_name); + + if (!table) { + err = DB_TABLE_NOT_FOUND; + + goto funct_exit; + } + str1 = (char *) "PROCEDURE RENAME_TABLE_PROC () IS\n" "new_table_name CHAR;\n" @@ -2229,14 +2245,43 @@ row_rename_table_for_mysql( if (row_is_mysql_tmp_table_name(new_name)) { - /* We want to preserve the original foreign key - constraint definitions despite the name change */ + /* MySQL is doing an ALTER TABLE command and it renames the + original table to a temporary table name. We want to preserve + the original foreign key constraint definitions despite the + name change. An exception is those constraints for which + the ALTER TABLE contained DROP FOREIGN KEY .*/ - str3 = (char*) - "';\n" - "UPDATE SYS_TABLES SET NAME = new_table_name\n" - "WHERE NAME = old_table_name;\n" - "END;\n"; + heap = mem_heap_create(100); + + err = dict_foreign_parse_drop_constraints(heap, trx, + table, + &n_constraints_to_drop, + &constraints_to_drop); + if (err != DB_SUCCESS) { + + goto funct_exit; + } + + str3 = mem_heap_alloc(heap, + 1000 + 500 * n_constraints_to_drop); + *str3 = '\0'; + sprintf(str3, + "';\n" + "UPDATE SYS_TABLES SET NAME = new_table_name\n" + "WHERE NAME = old_table_name;\n"); + + for (i = 0; i < n_constraints_to_drop; i++) { + sprintf(str3 + strlen(str3), + "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n" + "DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n", + constraints_to_drop[i], + constraints_to_drop[i]); + } + + sprintf(str3 + strlen(str3), + "END;\n"); + + ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop); } else { str3 = (char*) "';\n" @@ -2267,13 +2312,6 @@ row_rename_table_for_mysql( ut_memcpy(buf + len, str3, ut_strlen(str3) + 1); - /* Serialize data dictionary operations with dictionary mutex: - no deadlocks can occur then in these operations */ - - row_mysql_lock_data_dictionary(trx); - - table = dict_table_get_low(old_name); - graph = pars_sql(buf); ut_a(graph); @@ -2283,12 +2321,6 @@ row_rename_table_for_mysql( graph->fork_type = QUE_FORK_MYSQL_INTERFACE; - if (!table) { - err = DB_TABLE_NOT_FOUND; - - goto funct_exit; - } - ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0)); que_run_threads(thr); @@ -2329,6 +2361,13 @@ row_rename_table_for_mysql( if (row_is_mysql_tmp_table_name(old_name)) { + /* MySQL is doing an ALTER TABLE command and it + renames the created temporary table to the name + of the original table. In the ALTER TABLE we maybe + created some FOREIGN KEY constraints for the temporary + table. But we want to load also the foreign key + constraint definitions for the original table name. */ + err = dict_load_foreigns(new_name); if (err != DB_SUCCESS) { @@ -2354,7 +2393,13 @@ row_rename_table_for_mysql( funct_exit: row_mysql_unlock_data_dictionary(trx); - que_graph_free(graph); + if (graph) { + que_graph_free(graph); + } + + if (heap) { + mem_heap_free(heap); + } trx_commit_for_mysql(trx); diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 4bc81ad8a8e..40994c9962c 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2601,6 +2601,24 @@ row_search_for_mysql( printf("N tables locked %lu\n", trx->mysql_n_tables_locked); */ + /*-------------------------------------------------------------*/ + /* PHASE 0: Release a possible s-latch we are holding on the + adaptive hash index latch if there is someone waiting behind */ + + if (trx->has_search_latch + && btr_search_latch.writer != RW_LOCK_NOT_LOCKED) { + + /* There is an x-latch request on the adaptive hash index: + release the s-latch to reduce starvation and wait for + BTR_SEA_TIMEOUT rounds before trying to keep it again over + calls from MySQL */ + + rw_lock_s_unlock(&btr_search_latch); + trx->has_search_latch = FALSE; + + trx->search_latch_timeout = BTR_SEA_TIMEOUT; + } + /*-------------------------------------------------------------*/ /* PHASE 1: Try to pop the row from the prefetch cache */ @@ -2736,23 +2754,7 @@ row_search_for_mysql( NOT prepared to inserts interleaved with the SELECT, and if we try that, we can deadlock on the adaptive hash index semaphore! */ - - if (btr_search_latch.writer != RW_LOCK_NOT_LOCKED) { - /* There is an x-latch request: release - a possible s-latch to reduce starvation - and wait for BTR_SEA_TIMEOUT rounds before - trying to keep it again over calls from - MySQL */ - if (trx->has_search_latch) { - rw_lock_s_unlock(&btr_search_latch); - trx->has_search_latch = FALSE; - } - - trx->search_latch_timeout = BTR_SEA_TIMEOUT; - - goto no_shortcut; - } #ifndef UNIV_SEARCH_DEBUG if (!trx->has_search_latch) { rw_lock_s_lock(&btr_search_latch); @@ -2810,7 +2812,6 @@ row_search_for_mysql( } } -no_shortcut: /*-------------------------------------------------------------*/ /* PHASE 3: Open or restore index cursor position */ diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c index 64569bf3f96..5fce1c1861b 100644 --- a/innobase/row/row0upd.c +++ b/innobase/row/row0upd.c @@ -218,7 +218,7 @@ row_upd_check_references_constraints( being dropped while the check is running. */ err = row_ins_check_foreign_constraint(FALSE, foreign, - table, index, entry, thr); + table, entry, thr); if (foreign->foreign_table) { mutex_enter(&(dict_sys->mutex)); diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index c6f3bd40dfe..ccb3755b252 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -1869,11 +1869,11 @@ retry: /* Go to wait for the event; when a thread leaves InnoDB it will release this thread */ - trx->op_info = "waiting in InnoDB queue"; + trx->op_info = (char*)"waiting in InnoDB queue"; os_event_wait(slot->event); - trx->op_info = ""; + trx->op_info = (char*)""; os_fast_mutex_lock(&srv_conc_mutex); @@ -2346,12 +2346,22 @@ srv_sprintf_innodb_monitor( buf = buf + strlen(buf); ut_a(buf < buf_end + 1500); - buf += sprintf(buf, "------------\n" - "TRANSACTIONS\n" - "------------\n"); + if (*dict_foreign_err_buf != '\0') { + buf += sprintf(buf, + "------------------------\n" + "LATEST FOREIGN KEY ERROR\n" + "------------------------\n"); + + if (buf_end - buf > 6000) { + buf+= sprintf(buf, "%.4000s", dict_foreign_err_buf); + } + } + + ut_a(buf < buf_end + 1500); + lock_print_info(buf, buf_end); buf = buf + strlen(buf); - + buf += sprintf(buf, "--------\n" "FILE I/O\n" "--------\n"); diff --git a/innobase/ut/ut0ut.c b/innobase/ut/ut0ut.c index bb5eb662cd7..95037ec3570 100644 --- a/innobase/ut/ut0ut.c +++ b/innobase/ut/ut0ut.c @@ -204,7 +204,7 @@ ut_get_year_month_day( cal_tm_ptr = localtime(&tm); - *year = (ulint)cal_tm_ptr->tm_year; + *year = (ulint)cal_tm_ptr->tm_year + 1900; *month = (ulint)cal_tm_ptr->tm_mon + 1; *day = (ulint)cal_tm_ptr->tm_mday; #endif diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index ea8eaf6653e..edafc142fd3 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -242,6 +242,10 @@ convert_error_code_to_mysql( return(HA_ERR_CANNOT_ADD_FOREIGN); + } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) { + + return(HA_WRONG_CREATE_OPTION); + } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { return(HA_ERR_CRASHED); @@ -3048,6 +3052,9 @@ ha_innobase::create( trx_search_latch_release_if_reserved(parent_trx); trx = trx_allocate_for_mysql(); + + trx->mysql_thd = thd; + trx->mysql_query_str = &((*thd).query); if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) { trx->check_foreigns = FALSE; @@ -3231,6 +3238,9 @@ ha_innobase::delete_table( trx = trx_allocate_for_mysql(); + trx->mysql_thd = current_thd; + trx->mysql_query_str = &((*current_thd).query); + name_len = strlen(name); assert(name_len < 1000); @@ -3309,6 +3319,8 @@ innobase_drop_database( casedn_str(namebuf); #endif trx = trx_allocate_for_mysql(); + trx->mysql_thd = current_thd; + trx->mysql_query_str = &((*current_thd).query); error = row_drop_database_for_mysql(namebuf, trx); @@ -3368,6 +3380,8 @@ ha_innobase::rename_table( } trx = trx_allocate_for_mysql(); + trx->mysql_thd = current_thd; + trx->mysql_query_str = &((*current_thd).query); name_len1 = strlen(from); name_len2 = strlen(to);