diff --git a/VERSION b/VERSION index fa5486099ce..904aeda33ab 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=44 +MYSQL_VERSION_PATCH=45 MYSQL_VERSION_EXTRA= diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index e0945c1266b..b0f63a1361a 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -1066,7 +1066,7 @@ int main(int argc, char **argv) printf("This installation of MySQL is already upgraded to %s, " "use --force if you still need to run mysql_upgrade\n", MYSQL_SERVER_VERSION); - die(NULL); + goto end; } if (opt_version_check && check_version_match()) @@ -1089,6 +1089,7 @@ int main(int argc, char **argv) /* Create a file indicating upgrade has been performed */ create_mysql_upgrade_info_file(); +end: free_used_memory(); my_end(my_end_arg); exit(0); diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 57492577e91..250dc609891 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -66,6 +66,7 @@ ulong server_id = 0; ulong bytes_sent = 0L, bytes_received = 0L; ulong mysqld_net_retry_count = 10L; ulong open_files_limit; +ulong opt_binlog_rows_event_max_size; uint test_flags = 0; static uint opt_protocol= 0; static FILE *result_file; @@ -1432,6 +1433,12 @@ that may lead to an endless loop.", "Used to reserve file descriptors for use by this program.", &open_files_limit, &open_files_limit, 0, GET_ULONG, REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0}, + {"binlog-row-event-max-size", 0, + "The maximum size of a row-based binary log event in bytes. Rows will be " + "grouped into events smaller than this size if possible. " + "This value must be a multiple of 256.", + &opt_binlog_rows_event_max_size, &opt_binlog_rows_event_max_size, 0, + GET_ULONG, REQUIRED_ARG, UINT_MAX, 256, ULONG_MAX, 0, 256, 0}, {"verify-binlog-checksum", 'c', "Verify checksum binlog events.", (uchar**) &opt_verify_binlog_checksum, (uchar**) &opt_verify_binlog_checksum, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/client/mysqlslap.c b/client/mysqlslap.c index 536535be294..03301b01efb 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2005, 2012, Oracle and/or its affiliates. + Copyright (c) 2005, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -244,7 +245,7 @@ void print_conclusions_csv(conclusions *con); void generate_stats(conclusions *con, option_string *eng, stats *sptr); uint parse_comma(const char *string, uint **range); uint parse_delimiter(const char *script, statement **stmt, char delm); -uint parse_option(const char *origin, option_string **stmt, char delm); +int parse_option(const char *origin, option_string **stmt, char delm); static int drop_schema(MYSQL *mysql, const char *db); uint get_random_string(char *buf); static statement *build_table_string(void); @@ -1259,7 +1260,13 @@ get_options(int *argc,char ***argv) if (num_int_cols_opt) { option_string *str; - parse_option(num_int_cols_opt, &str, ','); + if(parse_option(num_int_cols_opt, &str, ',') == -1) + { + fprintf(stderr, "Invalid value specified for the option " + "'number-int-cols'\n"); + option_cleanup(str); + return 1; + } num_int_cols= atoi(str->string); if (str->option) num_int_cols_index= atoi(str->option); @@ -1270,7 +1277,13 @@ get_options(int *argc,char ***argv) if (num_char_cols_opt) { option_string *str; - parse_option(num_char_cols_opt, &str, ','); + if(parse_option(num_char_cols_opt, &str, ',') == -1) + { + fprintf(stderr, "Invalid value specified for the option " + "'number-char-cols'\n"); + option_cleanup(str); + return 1; + } num_char_cols= atoi(str->string); if (str->option) num_char_cols_index= atoi(str->option); @@ -1507,7 +1520,13 @@ get_options(int *argc,char ***argv) printf("Parsing engines to use.\n"); if (default_engine) - parse_option(default_engine, &engine_options, ','); + { + if(parse_option(default_engine, &engine_options, ',') == -1) + { + fprintf(stderr, "Invalid value specified for the option 'engine'\n"); + return 1; + } + } if (tty_password) opt_password= get_tty_password(NullS); @@ -1984,7 +2003,7 @@ end: DBUG_RETURN(0); } -uint +int parse_option(const char *origin, option_string **stmt, char delm) { char *retstr; @@ -2009,6 +2028,13 @@ parse_option(const char *origin, option_string **stmt, char delm) char buffer[HUGE_STRING_LENGTH]= ""; char *buffer_ptr; + /* + Return an error if the length of the any of the comma seprated value + exceeds HUGE_STRING_LENGTH. + */ + if ((size_t)(retstr - ptr) > HUGE_STRING_LENGTH) + return -1; + count++; strncpy(buffer, ptr, (size_t)(retstr - ptr)); /* @@ -2048,6 +2074,13 @@ parse_option(const char *origin, option_string **stmt, char delm) { char *origin_ptr; + /* + Return an error if the length of the any of the comma seprated value + exceeds HUGE_STRING_LENGTH. + */ + if (strlen(ptr) > HUGE_STRING_LENGTH) + return -1; + if ((origin_ptr= strchr(ptr, ':'))) { char *option_ptr; @@ -2058,13 +2091,13 @@ parse_option(const char *origin, option_string **stmt, char delm) option_ptr= (char *)ptr + 1 + tmp->length; /* Move past the : and the first string */ - tmp->option_length= (size_t)((ptr + length) - option_ptr); + tmp->option_length= strlen(option_ptr); tmp->option= my_strndup(option_ptr, tmp->option_length, MYF(MY_FAE)); } else { - tmp->length= (size_t)((ptr + length) - ptr); + tmp->length= strlen(ptr); tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE)); } diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test index b41bfeaba74..336b11113ec 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test @@ -228,7 +228,7 @@ rollback; create table t0 (n int); insert t0 select * from t1; set autocommit=1; -insert into t0 select GET_LOCK("lock1",null); +insert into t0 select GET_LOCK("lock1",0); set autocommit=0; create table t2 (n int) engine=innodb; insert into t2 values (3); diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 21f9fbe763a..9078174eb5a 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4810,6 +4810,7 @@ sub extract_warning_lines ($$) { qr|Plugin 'FEEDBACK' registration as a INFORMATION SCHEMA failed|, qr|Failed to setup SSL|, qr|SSL error: Failed to set ciphers to use|, + qr/Plugin 'InnoDB' will be forced to shutdown/, ); my $matched_lines= []; diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index f12a0c1127a..0bc31a5e85b 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -1103,3 +1103,19 @@ ORDER BY field; field c,c drop table t3, t2, t1; +# +# MDEV-7821 - Server crashes in Item_func_group_concat::fix_fields on 2nd +# execution of PS +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1),(2); +PREPARE stmt FROM "SELECT GROUP_CONCAT(t1a.a ORDER BY 1, t1a.a=0) FROM t1 AS t1a, t1 AS t1b GROUP BY t1a.a"; +EXECUTE stmt; +GROUP_CONCAT(t1a.a ORDER BY 1, t1a.a=0) +1,1 +2,2 +EXECUTE stmt; +GROUP_CONCAT(t1a.a ORDER BY 1, t1a.a=0) +1,1 +2,2 +DROP TABLE t1; diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index d5db28f04b8..d654dbccb0e 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -361,5 +361,18 @@ set optimizer_switch=@optimizer_switch_save; drop view v_merge, vm; drop table t1,tv; # +# MDEV-4017 - GET_LOCK() with negative timeouts has strange behavior +# +SELECT GET_LOCK('ul1', NULL); +GET_LOCK('ul1', NULL) +NULL +Warnings: +Warning 1411 Incorrect timeout value: 'NULL' for function get_lock +SELECT GET_LOCK('ul1', -1); +GET_LOCK('ul1', -1) +NULL +Warnings: +Warning 1411 Incorrect timeout value: '-1' for function get_lock +# # End of 5.5 tests # diff --git a/mysql-test/r/innodb_load_xa.result b/mysql-test/r/innodb_load_xa.result index e738ca6e3af..8b8de2032e5 100644 --- a/mysql-test/r/innodb_load_xa.result +++ b/mysql-test/r/innodb_load_xa.result @@ -18,3 +18,5 @@ mysqld-bin.000001 # Query # # use `test`; insert t1 values (2) mysqld-bin.000001 # Query # # COMMIT drop table t1; uninstall plugin innodb; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown diff --git a/mysql-test/r/merge_recover.result b/mysql-test/r/myisam_recover.result similarity index 62% rename from mysql-test/r/merge_recover.result rename to mysql-test/r/myisam_recover.result index 871c12ca4c0..0829c1e8b82 100644 --- a/mysql-test/r/merge_recover.result +++ b/mysql-test/r/myisam_recover.result @@ -1,5 +1,7 @@ # -# Test of MyISAM MRG tables with corrupted children. +# Tests for corrupted MyISAM tables and MyISAMMRG tables with corrupted +# children.. +# # Run with --myisam-recover=force option. # # Preparation: we need to make sure that the merge parent @@ -44,20 +46,20 @@ drop procedure p_create; # Switching to connection 'default' # # -# We have to disable the ps-protocol, to avoid +# We have to disable the ps-protocol, to avoid # "Prepared statement needs to be re-prepared" errors # -- table def versions change all the time with full table cache. -# +# drop table if exists t1, t1_mrg, t1_copy; # # Prepare a MERGE engine table, that refers to a corrupted # child. -# +# create table t1 (a int, key(a)) engine=myisam; create table t1_mrg (a int) union (t1) engine=merge; # # Create a table with a corrupted index file: -# save an old index file, insert more rows, +# save an old index file, insert more rows, # overwrite the new index file with the old one. # insert into t1 (a) values (1), (2), (3); @@ -101,3 +103,48 @@ execute stmt; deallocate prepare stmt; set @@global.table_definition_cache=default; set @@global.table_open_cache=default; +# +# 18075170 - sql node restart required to avoid deadlock after +# restore +# +# Check that auto-repair for MyISAM tables can now happen in the +# middle of transaction, without aborting it. +create table t1 (a int, key(a)) engine=myisam; +create table t2 (a int); +insert into t2 values (1); +# Create a table with a corrupted index file: +# save an old index file, insert more rows, +# overwrite the new index file with the old one. +insert into t1 (a) values (1); +flush table t1; +insert into t1 (a) values (4); +flush table t1; +# Check table is needed to mark the table as crashed. +check table t1; +Table Op Msg_type Msg_text +test.t1 check warning Size of datafile is: 14 Should be: 7 +test.t1 check error Record-count is not ok; is 2 Should be: 1 +test.t1 check warning Found 2 key parts. Should be: 1 +test.t1 check error Corrupt +# At this point we have a corrupt t1 +set autocommit = 0; +select * from t2; +a +1 +# Without fix select from t1 will break the transaction. After the fix +# transaction should be active and should hold lock on table t2. Alter +# table from con2 will wait only if the transaction is not broken. +select * from t1; +a +1 +4 +Warnings: +Error 145 Table 't1' is marked as crashed and should be repaired +Error 1194 Table 't1' is marked as crashed and should be repaired +Error 1034 Number of rows changed from 1 to 2 +ALTER TABLE t2 ADD val INT; +# With fix we should have alter table waiting for t2 lock here. +ROLLBACK; +SET autocommit = 1; +# Cleanup +drop table t1, t2; diff --git a/mysql-test/r/sp-group.result b/mysql-test/r/sp-group.result new file mode 100644 index 00000000000..9744bbee6b6 --- /dev/null +++ b/mysql-test/r/sp-group.result @@ -0,0 +1,156 @@ +drop table if exists t1; +Warnings: +Note 1051 Unknown table 't1' +drop view if exists view_t1; +Warnings: +Note 1051 Unknown table 'test.view_t1' +SET sql_mode=ONLY_FULL_GROUP_BY; +CREATE TABLE t1 ( +pk INT, +f0 INT, f1 INT, f2 INT, f3 INT, f4 INT, +f5 INT, f6 INT, f7 INT, f8 INT, f9 INT, +PRIMARY KEY (pk) +); +CREATE VIEW view_t1 AS SELECT * FROM t1; +CREATE PROCEDURE s1() +SELECT * FROM ( +INFORMATION_SCHEMA.`INNODB_BUFFER_PAGE_LRU` AS table1 +LEFT JOIN test.view_t1 AS table2 +ON ( table2.`f6` = table1.FREE_PAGE_CLOCK) +) +ORDER BY table1.NUMBER_RECORDS +LIMIT 0 +; +CALL s1; +POOL_ID LRU_POSITION SPACE PAGE_NUMBER PAGE_TYPE FLUSH_TYPE FIX_COUNT IS_HASHED NEWEST_MODIFICATION OLDEST_MODIFICATION ACCESS_TIME TABLE_NAME INDEX_NAME NUMBER_RECORDS DATA_SIZE COMPRESSED_SIZE COMPRESSED IO_FIX IS_OLD FREE_PAGE_CLOCK pk f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 +CALL s1; +POOL_ID LRU_POSITION SPACE PAGE_NUMBER PAGE_TYPE FLUSH_TYPE FIX_COUNT IS_HASHED NEWEST_MODIFICATION OLDEST_MODIFICATION ACCESS_TIME TABLE_NAME INDEX_NAME NUMBER_RECORDS DATA_SIZE COMPRESSED_SIZE COMPRESSED IO_FIX IS_OLD FREE_PAGE_CLOCK pk f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 +drop table t1; +drop view view_t1; +drop procedure s1; +CREATE TABLE A ( +pk INTEGER AUTO_INCREMENT, +col_int_key INTEGER, +col_varchar_key VARCHAR(1), +PRIMARY KEY (pk) +) ENGINE=MyISAM; +CREATE VIEW view_A AS SELECT * FROM A; +CREATE TABLE C ( +pk INTEGER AUTO_INCREMENT, +col_int_nokey INTEGER, +col_int_key INTEGER, +col_date_key DATE, +col_date_nokey DATE, +col_time_key TIME, +col_time_nokey TIME, +col_datetime_key DATETIME, +col_datetime_nokey DATETIME, +col_varchar_key VARCHAR(1), +col_varchar_nokey VARCHAR(1), +PRIMARY KEY (pk) +) ENGINE=MyISAM; +CREATE VIEW view_C AS SELECT * FROM C; +CREATE TABLE AA ( +pk INTEGER AUTO_INCREMENT, +col_int_nokey INTEGER, +col_int_key INTEGER, +col_date_key DATE, +col_date_nokey DATE, +col_time_key TIME, +col_time_nokey TIME, +col_datetime_key DATETIME, +col_datetime_nokey DATETIME, +col_varchar_key VARCHAR(1), +col_varchar_nokey VARCHAR(1), +PRIMARY KEY (pk), +KEY (col_varchar_key, col_int_key) +) ENGINE=MyISAM; +CREATE VIEW view_AA AS SELECT * FROM AA; +CREATE TABLE BB ( +pk INTEGER AUTO_INCREMENT, +col_int_key INTEGER, +col_varchar_key VARCHAR(1), +col_varchar_nokey VARCHAR(1), +PRIMARY KEY (pk), +KEY (col_varchar_key, col_int_key) +) ENGINE=MyISAM; +CREATE VIEW view_BB AS SELECT * FROM BB; +CREATE TABLE DD ( +pk INTEGER AUTO_INCREMENT, +col_int_key INTEGER, +col_date_key DATE, +col_time_key TIME, +col_datetime_key DATETIME, +col_varchar_key VARCHAR(1), +PRIMARY KEY (pk), +KEY (col_varchar_key, col_int_key) +) ENGINE=MyISAM; +CREATE VIEW view_DD AS SELECT * FROM DD; +CREATE TRIGGER k BEFORE INSERT ON `DD` FOR EACH ROW INSERT INTO `view_BB` SELECT * FROM `view_A` LIMIT 0 ; +CREATE TRIGGER r BEFORE INSERT ON `A` FOR EACH ROW INSERT INTO `view_AA` SELECT * FROM `view_C` LIMIT 0 ; +ALTER TABLE `DD` DROP PRIMARY KEY; +ERROR 42000: Incorrect table definition; there can be only one auto column and it must be defined as a key +INSERT INTO `view_A` ( `pk` ) VALUES (NULL); +INSERT INTO `DD` ( `pk` ) VALUES (NULL); +INSERT INTO `A` ( `pk` ) VALUES (NULL); +INSERT INTO `view_DD` ( `pk` ) VALUES (NULL); +drop trigger r; +drop trigger k; +drop view view_A,view_AA,view_C,view_BB,view_DD; +drop table A,C,AA,BB,DD; +CREATE TABLE A ( +i INT, +i1 INT, +i2 INT, +d1 DATE, +d2 DATE, +col_time_nokey1 TIME, +col_time_nokey2 TIME, +col_datetime_nokey1 DATETIME, +col_datetime_nokey2 DATETIME, +col_varchar_nokey1 VARCHAR(1), +col_varchar_nokey2 VARCHAR(1) +) ENGINE=MyISAM; +CREATE VIEW view_A AS SELECT * FROM A; +CREATE TABLE B ( +col_varchar_nokey VARCHAR(1) +) ENGINE=MyISAM; +CREATE TABLE AA ( +i INT, +i1 INT, +i2 INT, +d1 DATE, +d2 DATE, +col_time_nokey1 TIME, +col_time_nokey2 TIME, +col_datetime_nokey1 DATETIME, +col_datetime_nokey2 DATETIME, +col_varchar_nokey1 VARCHAR(1), +col_varchar_nokey2 VARCHAR(1) +) ENGINE=MyISAM; +CREATE VIEW view_AA AS SELECT * FROM AA; +CREATE TABLE DD ( +i INT, +i1 INT, +i2 INT, +d1 DATE, +d2 DATE, +col_time_nokey1 TIME, +col_time_nokey2 TIME, +col_datetime_nokey1 DATETIME, +col_datetime_nokey2 DATETIME, +col_varchar_nokey1 VARCHAR(1), +col_varchar_nokey2 VARCHAR(1) +) ENGINE=MyISAM; +CREATE VIEW view_DD AS SELECT * FROM DD; +CREATE TRIGGER tr1 BEFORE INSERT ON `AA` FOR EACH ROW INSERT INTO `view_A` SELECT * FROM `view_AA` LIMIT 0 ; +CREATE TRIGGER tr2 BEFORE INSERT ON `B` FOR EACH ROW INSERT INTO `D` SELECT * FROM `A` LIMIT 0 ; +INSERT INTO `view_AA` ( `i` ) VALUES (1); +INSERT INTO `AA` ( `i` ) VALUES (2); +DELETE FROM `B`; +INSERT INTO `view_DD` ( `i` ) VALUES (1); +INSERT INTO `view_AA` ( `i` ) VALUES (3); +drop trigger tr1; +drop trigger tr2; +drop view view_A, view_AA,view_DD; +drop table A,B,AA,DD; diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index 7039bbfca15..e4843929fcb 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -2145,6 +2145,24 @@ drop database mysqltest1; drop database mysqltest2; drop database mysqltest3; drop database mysqltest4; +# +# MDEV-7810 Wrong result on execution of a query as a PS +# (both 1st and further executions) +CREATE TABLE t1 (a INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0),(8); +SELECT a FROM (SELECT DISTINCT * FROM t1) AS sq WHERE a IN (SELECT MIN(t2.a) FROM (t1 AS t2)); +a +0 +PREPARE stmt FROM " +SELECT a FROM (SELECT DISTINCT * FROM t1) AS sq WHERE a IN (SELECT MIN(t2.a) FROM (t1 AS t2)) +"; +execute stmt; +a +0 +execute stmt; +a +0 +drop table t1; # End of 5.5 tests set @subselect_mat_test_optimizer_switch_value=null; set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index 58810670395..cfcbf612030 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -2185,4 +2185,22 @@ drop database mysqltest1; drop database mysqltest2; drop database mysqltest3; drop database mysqltest4; +# +# MDEV-7810 Wrong result on execution of a query as a PS +# (both 1st and further executions) +CREATE TABLE t1 (a INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0),(8); +SELECT a FROM (SELECT DISTINCT * FROM t1) AS sq WHERE a IN (SELECT MIN(t2.a) FROM (t1 AS t2)); +a +0 +PREPARE stmt FROM " +SELECT a FROM (SELECT DISTINCT * FROM t1) AS sq WHERE a IN (SELECT MIN(t2.a) FROM (t1 AS t2)) +"; +execute stmt; +a +0 +execute stmt; +a +0 +drop table t1; # End of 5.5 tests diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index cbacb2c1ef8..431e8d2d862 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -5429,6 +5429,21 @@ View Create View character_set_client collation_connection v2 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select (`t1`.`b` + 1) AS `c` from `t1` latin1 latin1_swedish_ci drop view v2; drop table t1; +# +# MDEV-8554: Server crashes in base_list_iterator::next_fast on 1st execution of PS with a multi-table update +# +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 (b INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (3),(4); +CREATE TABLE t3 (c INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (5),(6); +CREATE OR REPLACE ALGORITHM=MERGE VIEW v3 AS SELECT * FROM t3; +PREPARE stmt FROM 'UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM t3 )'; +UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM v3 ); +EXECUTE stmt; +DROP TABLE t1, t2, t3; +DROP VIEW v3; # ----------------------------------------------------------------- # -- End of 5.5 tests. # ----------------------------------------------------------------- diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result index ac36412c360..ed6e711af6b 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result @@ -258,7 +258,7 @@ Warning 1196 Some non-transactional changed tables couldn't be rolled back create table t0 (n int); insert t0 select * from t1; set autocommit=1; -insert into t0 select GET_LOCK("lock1",null); +insert into t0 select GET_LOCK("lock1",0); set autocommit=0; create table t2 (n int) engine=innodb; insert into t2 values (3); diff --git a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result index c10aa7abd05..7309c611d29 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result @@ -242,7 +242,7 @@ Warning 1196 Some non-transactional changed tables couldn't be rolled back create table t0 (n int); insert t0 select * from t1; set autocommit=1; -insert into t0 select GET_LOCK("lock1",null); +insert into t0 select GET_LOCK("lock1",0); Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave. set autocommit=0; @@ -288,7 +288,7 @@ master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert t0 select * from t1 master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # BEGIN -master-bin.000001 # Query # # use `test`; insert into t0 select GET_LOCK("lock1",null) +master-bin.000001 # Query # # use `test`; insert into t0 select GET_LOCK("lock1",0) master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # use `test`; create table t2 (n int) engine=innodb master-bin.000001 # Query # # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `t1`,`ti` diff --git a/mysql-test/suite/innodb/r/add_constraint.result b/mysql-test/suite/innodb/r/add_constraint.result new file mode 100644 index 00000000000..cbbcb4e8fa8 --- /dev/null +++ b/mysql-test/suite/innodb/r/add_constraint.result @@ -0,0 +1,13 @@ +# +# Bug #20762798 FK DDL: CRASH IN DICT_FOREIGN_REMOVE_FROM_CACHE +# +create table t1(a int, b int, key(a),key(b))engine=innodb; +create table t2(a int, b int, key(a),key(b))engine=innodb; +alter table t2 add constraint b foreign key (b) references t1(a); +alter table t1 add constraint b1 foreign key (b) references t2(a); +alter table t2 add constraint b1 foreign key (b) references t1(a); +ERROR HY000: Can't create table '#sql-temporary' (errno: 121) +alter table t2 drop foreign key b; +alter table t1 drop foreign key b1; +drop table t2; +drop table t1; diff --git a/mysql-test/suite/innodb/r/innodb-fk-warnings.result b/mysql-test/suite/innodb/r/innodb-fk-warnings.result new file mode 100644 index 00000000000..542fc972880 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb-fk-warnings.result @@ -0,0 +1,105 @@ +CREATE TABLE t1 ( +id int(11) NOT NULL PRIMARY KEY, +a int(11) NOT NULL, +b int(11) NOT NULL, +c int not null, +CONSTRAINT test FOREIGN KEY (b) REFERENCES t1 (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +CREATE TABLE t2 ( +id int(11) NOT NULL PRIMARY KEY, +a int(11) NOT NULL, +b int(11) NOT NULL, +c int not null, +CONSTRAINT mytest FOREIGN KEY (c) REFERENCES t1(id), +CONSTRAINT test FOREIGN KEY (b) REFERENCES t2 (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Can't create table 'test.t2' (errno: 121) +show warnings; +Level Code Message +Warning 121 Create or Alter table `test`.`t2` with foreign key constraint failed. Foreign key constraint `test/test` already exists on data dictionary. Foreign key constraint names need to be unique in database. Error in foreign key definition: CONSTRAINT `test` FOREIGN KEY (`b`) REFERENCES `test`.`t2` (`id`). +Error 1005 Can't create table 'test.t2' (errno: 121) +drop table t1; +create table t1(a int) engine=innodb; +create table t2(a int, constraint a foreign key a (a) references t1(a)) engine=innodb; +ERROR HY000: Can't create table 'test.t2' (errno: 150) +show warnings; +Level Code Message +Warning 150 Create table '`test`.`t2`' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns. Error close to foreign key a (a) references t1(a)) engine=innodb. +Error 1005 Can't create table 'test.t2' (errno: 150) +drop table t1; +create table t1(a int not null primary key, b int) engine=innodb; +create table t2(a int, b int, constraint a foreign key a (a) references t1(a), +constraint a foreign key a (a) references t1(b)) engine=innodb; +ERROR HY000: Can't create table 'test.t2' (errno: 150) +show warnings; +Level Code Message +Warning 150 Create table '`test`.`t2`' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns. Error close to foreign key a (a) references t1(b)) engine=innodb. +Error 1005 Can't create table 'test.t2' (errno: 150) +create table t2(a int, b int, constraint a foreign key a (a) references t1(a)) engine=innodb; +alter table t2 add constraint b foreign key (b) references t2(b); +ERROR HY000: Can't create table '#sql-temporary' (errno: 150) +show warnings; +Level Code Message +Warning 150 Alter table '`test`.`t2`' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns. Error close to foreign key (b) references t2(b). +Error 1005 Can't create table '#sql-temporary' (errno: 150) +drop table t2, t1; +create table t1 (f1 integer primary key) engine=innodb; +alter table t1 add constraint c1 foreign key (f1) references t11(f1); +ERROR HY000: Can't create table '#sql-temporary' (errno: 150) +show warnings; +Level Code Message +Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Referenced table `test`.`t11` not found in the data dictionary close to foreign key (f1) references t11(f1). +Error 1005 Can't create table '#sql-temporary' (errno: 150) +drop table t1; +create temporary table t1(a int not null primary key, b int, key(b)) engine=innodb; +create temporary table t2(a int, foreign key(a) references t1(a)) engine=innodb; +ERROR HY000: Can't create table 'test.t2' (errno: 150) +show warnings; +Level Code Message +Warning 150 Create table `mysqld.1`.`t2` with foreign key constraint failed. Referenced table `mysqld.1`.`t1` not found in the data dictionary close to foreign key(a) references t1(a)) engine=innodb. +Error 1005 Can't create table 'test.t2' (errno: 150) +alter table t1 add foreign key(b) references t1(a); +ERROR HY000: Can't create table '#sql-temporary' (errno: 150) +show warnings; +Level Code Message +Warning 150 Alter table `mysqld.1`.`t1` with foreign key constraint failed. Referenced table `mysqld.1`.`t1` not found in the data dictionary close to foreign key(b) references t1(a). +Error 1005 Can't create table '#sql-temporary' (errno: 150) +drop table t1; +create table t1(a int not null primary key, b int, key(b)) engine=innodb; +alter table t1 add foreign key(a,b) references t1(a); +ERROR HY000: Can't create table '#sql-temporary' (errno: 150) +show warnings; +Level Code Message +Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Foreign key constraint parse error in foreign key(a,b) references t1(a) close to ). Too few referenced columns, you have 1 when you should have 2. +Error 1005 Can't create table '#sql-temporary' (errno: 150) +drop table t1; +create table t1(a int not null primary key, b int, key(b)) engine=innodb; +alter table t1 add foreign key(a) references t1(a,b); +ERROR HY000: Can't create table '#sql-temporary' (errno: 150) +show warnings; +Level Code Message +Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Foreign key constraint parse error in foreign key(a) references t1(a,b) close to ). Too few referenced columns, you have 2 when you should have 1. +Error 1005 Can't create table '#sql-temporary' (errno: 150) +drop table t1; +create table t1 (f1 integer not null primary key) engine=innodb; +alter table t1 add constraint c1 foreign key (f1) references t1(f1) on update set null; +ERROR HY000: Can't create table '#sql-temporary' (errno: 150) +show warnings; +Level Code Message +Warning 150 Alter table `test`.`t1` with foreign key constraint failed. You have defined a SET NULL condition but column f1 is defined as NOT NULL in foreign key (f1) references t1(f1) on update set null close to on update set null. +Error 1005 Can't create table '#sql-temporary' (errno: 150) +create table t2(a int not null, foreign key(a) references t1(f1) on delete set null) engine=innodb; +ERROR HY000: Can't create table 'test.t2' (errno: 150) +show warnings; +Level Code Message +Warning 150 Create table `test`.`t2` with foreign key constraint failed. You have defined a SET NULL condition but column a is defined as NOT NULL in foreign key(a) references t1(f1) on delete set null) engine=innodb close to on delete set null) engine=innodb. +Error 1005 Can't create table 'test.t2' (errno: 150) +drop table t1; +create table t1 (id int not null primary key, f1 int, f2 int, key(f1)) engine=innodb; +create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=innodb; +ERROR HY000: Can't create table 'test.t2' (errno: 150) +show warnings; +Level Code Message +Warning 150 Create table `test`.`t2` with foreign key constraint failed. Field type or character set for column a does not mach referenced column f1 close to foreign key(a) references t1(f1)) engine=innodb +Error 1005 Can't create table 'test.t2' (errno: 150) +drop table t1; diff --git a/mysql-test/suite/innodb/r/innodb-fk.result b/mysql-test/suite/innodb/r/innodb-fk.result index 3c557534dbd..2b53bc21b31 100644 --- a/mysql-test/suite/innodb/r/innodb-fk.result +++ b/mysql-test/suite/innodb/r/innodb-fk.result @@ -50,6 +50,8 @@ CONSTRAINT fk3 FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE ERROR HY000: Can't create table 'test.t2' (errno: 150) show warnings; Level Code Message +Warning 150 Create table `test`.`t2` with foreign key constraint failed. Referenced table `test`.`t3` not found in the data dictionary close to FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE +) ENGINE=InnoDB. Error 1005 Can't create table 'test.t2' (errno: 150) CREATE TABLE t2 ( id int(11) NOT NULL AUTO_INCREMENT, @@ -62,6 +64,7 @@ ALTER TABLE t2 ADD CONSTRAINT fk3 FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE ERROR HY000: Can't create table '#sql-temporary' (errno: 150) show warnings; Level Code Message +Warning 150 Alter table `test`.`t2` with foreign key constraint failed. Referenced table `test`.`t3` not found in the data dictionary close to FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE. Error 1005 Can't create table '#sql-temporary' (errno: 150) drop table t2; drop table t1; diff --git a/mysql-test/suite/innodb/r/innodb_uninstall.result b/mysql-test/suite/innodb/r/innodb_uninstall.result new file mode 100644 index 00000000000..2064269a02e --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_uninstall.result @@ -0,0 +1,22 @@ +install plugin innodb soname 'ha_innodb'; +create table t1(a int not null primary key) engine=innodb; +begin; +insert into t1 values(1); +flush tables; +uninstall plugin innodb; +select sleep(1); +sleep(1) +0 +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown +drop table t1; +install plugin innodb soname 'ha_innodb'; +create table t2(a int not null primary key) engine=innodb; +insert into t2 values(1); +drop table t2; +uninstall plugin innodb; +select sleep(1); +sleep(1) +0 +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown diff --git a/mysql-test/suite/innodb/t/add_constraint.test b/mysql-test/suite/innodb/t/add_constraint.test new file mode 100644 index 00000000000..eabf06434f4 --- /dev/null +++ b/mysql-test/suite/innodb/t/add_constraint.test @@ -0,0 +1,21 @@ +--source include/have_innodb.inc + +--echo # +--echo # Bug #20762798 FK DDL: CRASH IN DICT_FOREIGN_REMOVE_FROM_CACHE +--echo # + +create table t1(a int, b int, key(a),key(b))engine=innodb; +create table t2(a int, b int, key(a),key(b))engine=innodb; + +alter table t2 add constraint b foreign key (b) references t1(a); +alter table t1 add constraint b1 foreign key (b) references t2(a); + +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +alter table t2 add constraint b1 foreign key (b) references t1(a); + +alter table t2 drop foreign key b; +alter table t1 drop foreign key b1; + +drop table t2; +drop table t1; diff --git a/mysql-test/suite/innodb/t/innodb-fk-warnings.test b/mysql-test/suite/innodb/t/innodb-fk-warnings.test new file mode 100644 index 00000000000..fe92f1345f4 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb-fk-warnings.test @@ -0,0 +1,130 @@ +--source include/have_innodb.inc + +# +# MDEV-8524: Improve error messaging when there is duplicate key or foreign key names +# +CREATE TABLE t1 ( + id int(11) NOT NULL PRIMARY KEY, + a int(11) NOT NULL, + b int(11) NOT NULL, + c int not null, + CONSTRAINT test FOREIGN KEY (b) REFERENCES t1 (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +# +# Below create table fails because constraint name test +# is reserved for above table. +# +--error 1005 +CREATE TABLE t2 ( + id int(11) NOT NULL PRIMARY KEY, + a int(11) NOT NULL, + b int(11) NOT NULL, + c int not null, + CONSTRAINT mytest FOREIGN KEY (c) REFERENCES t1(id), + CONSTRAINT test FOREIGN KEY (b) REFERENCES t2 (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +show warnings; + +drop table t1; + +# +# MDEV-6697: Improve foreign keys warnings/errors +# + +# +# No index for referenced columns +# +create table t1(a int) engine=innodb; +--error 1005 +create table t2(a int, constraint a foreign key a (a) references t1(a)) engine=innodb; +show warnings; +drop table t1; + +create table t1(a int not null primary key, b int) engine=innodb; +--error 1005 +create table t2(a int, b int, constraint a foreign key a (a) references t1(a), +constraint a foreign key a (a) references t1(b)) engine=innodb; +show warnings; +create table t2(a int, b int, constraint a foreign key a (a) references t1(a)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +alter table t2 add constraint b foreign key (b) references t2(b); +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t2, t1; + +# +# Referenced table does not exists +# + +create table t1 (f1 integer primary key) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +alter table t1 add constraint c1 foreign key (f1) references t11(f1); +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t1; + +# +# Foreign key on temporal tables +# + +create temporary table t1(a int not null primary key, b int, key(b)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +create temporary table t2(a int, foreign key(a) references t1(a)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +alter table t1 add foreign key(b) references t1(a); +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t1; + +# +# Column numbers do not match +# +create table t1(a int not null primary key, b int, key(b)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +alter table t1 add foreign key(a,b) references t1(a); +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t1; +create table t1(a int not null primary key, b int, key(b)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +alter table t1 add foreign key(a) references t1(a,b); +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t1; + +# +# ON UPDATE/DELETE SET NULL on NOT NULL column +# +create table t1 (f1 integer not null primary key) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +alter table t1 add constraint c1 foreign key (f1) references t1(f1) on update set null; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +create table t2(a int not null, foreign key(a) references t1(f1) on delete set null) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t1; + +# +# Incorrect types +# +create table t1 (id int not null primary key, f1 int, f2 int, key(f1)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1005 +create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=innodb; +--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/ +show warnings; +drop table t1; diff --git a/mysql-test/suite/innodb/t/innodb_uninstall.opt b/mysql-test/suite/innodb/t/innodb_uninstall.opt new file mode 100644 index 00000000000..918855a7b01 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_uninstall.opt @@ -0,0 +1,3 @@ +--ignore-builtin-innodb +--loose-innodb + diff --git a/mysql-test/suite/innodb/t/innodb_uninstall.test b/mysql-test/suite/innodb/t/innodb_uninstall.test new file mode 100644 index 00000000000..34fc8345a02 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_uninstall.test @@ -0,0 +1,58 @@ +--source include/not_embedded.inc +--source include/not_windows.inc + +if (!$HA_INNODB_SO) { + --skip Need InnoDB plugin +} + +# +# MDEV-8474: InnoDB sets per-connection data unsafely +# Below test caused hang +# +install plugin innodb soname 'ha_innodb'; +create table t1(a int not null primary key) engine=innodb; + +connect (con1, localhost, root); +connection con1; +begin; +insert into t1 values(1); + +connection default; +flush tables; +send uninstall plugin innodb; + +connection con1; +select sleep(1); +disconnect con1; + +connection default; +reap; + +--source include/restart_mysqld.inc + +drop table t1; + +# +# Another test that caused hang. +# + +connect (con1, localhost, root); +connection con1; +install plugin innodb soname 'ha_innodb'; +create table t2(a int not null primary key) engine=innodb; +insert into t2 values(1); +drop table t2; + +connection default; +send uninstall plugin innodb; + +connection con1; +select sleep(1); +disconnect con1; + +connection default; +reap; + +--source include/restart_mysqld.inc + + diff --git a/mysql-test/suite/ndb/r/ndb_restore_discover.result b/mysql-test/suite/ndb/r/ndb_restore_discover.result new file mode 100644 index 00000000000..de10af87047 --- /dev/null +++ b/mysql-test/suite/ndb/r/ndb_restore_discover.result @@ -0,0 +1,33 @@ +# +# 18075170 - sql node restart required to avoid deadlock after +# restore +# +CREATE TABLE t1 (id INT) ENGINE=NDBCluster; +CREATE TABLE t2 (id INT) ENGINE=NDBCluster; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); +DROP TABLE t1; +DROP TABLE t2; +SET autocommit = 0; +SELECT * FROM t1; +id +1 +SELECT * FROM t2; +id +1 +ROLLBACK; +SET autocommit = 1; +drop table t1; +drop table t2; +SET autocommit = 0; +SELECT * FROM t1; +id +1 +SELECT * FROM t2; +id +1 +ALTER TABLE t1 ADD val INT; +ROLLBACK; +SET autocommit = 1; +drop table t1; +drop table t2; diff --git a/mysql-test/suite/ndb/t/ndb_restore_discover.test b/mysql-test/suite/ndb/t/ndb_restore_discover.test new file mode 100644 index 00000000000..6631c74d5c8 --- /dev/null +++ b/mysql-test/suite/ndb/t/ndb_restore_discover.test @@ -0,0 +1,70 @@ +-- source include/have_ndb.inc +-- source include/count_sessions.inc + +--echo # +--echo # 18075170 - sql node restart required to avoid deadlock after +--echo # restore +--echo # +# Test Auto Discover option within a transaction +# and make sure the transaction is not broken. +CREATE TABLE t1 (id INT) ENGINE=NDBCluster; +CREATE TABLE t2 (id INT) ENGINE=NDBCluster; + +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); + +-- source include/ndb_backup.inc + +DROP TABLE t1; +DROP TABLE t2; + +-- source include/ndb_restore_master.inc + +SET autocommit = 0; +SELECT * FROM t1; + +# Without fix below select was resulting in DEADLOCK error. With fix select +# should succeed. +SELECT * FROM t2; +ROLLBACK; +SET autocommit = 1; + +drop table t1; +drop table t2; + +# +# Checking lock preservation in transaction +# +# Using existing backup to create the scenario. Tables are deleted as part of +# above test cleanup. Thus restoring the backup will bring the system to +# required state. +-- source include/ndb_restore_master.inc + +SET autocommit = 0; +SELECT * FROM t1; +SELECT * FROM t2; + +connect(con2, localhost, root); +--SEND ALTER TABLE t1 ADD val INT + +connection default; +# Alter from con2 will be in waiting state as there is a lock on t1 from +# default connection due to active transaction. We check for this condition +# then releasing the lock by rollbacking active transaction. +let $wait_condition= + SELECT count(*) = 1 FROM information_schema.processlist WHERE state + LIKE "Waiting%" AND info = "ALTER TABLE t1 ADD val INT"; +--source include/wait_condition.inc +ROLLBACK; +SET autocommit = 1; + +connection con2; +--REAP + +disconnect con2; +connection default; +drop table t1; +drop table t2; + +# Wait till all disconnects are completed +-- source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/rpl/r/rpl_alter.result b/mysql-test/suite/rpl/r/rpl_alter.result index 2cffa70d778..7be3bc576f3 100644 --- a/mysql-test/suite/rpl/r/rpl_alter.result +++ b/mysql-test/suite/rpl/r/rpl_alter.result @@ -14,4 +14,109 @@ select * from mysqltest.t3; n 45 drop database mysqltest; +use test; +# +# Test bug where ALTER TABLE MODIFY didn't replicate properly +# +create table t1 (a int unsigned primary key, b int); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(10) unsigned NOT NULL, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +insert into t1 (a) values (1),((1<<32)-1); +select * from t1; +a b +1 NULL +4294967295 NULL +alter table t1 modify a bigint; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +select * from t1; +a b +1 NULL +4294967295 NULL +alter table t1 modify a int unsigned; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(10) unsigned NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +select * from t1; +a b +1 NULL +4294967295 NULL +alter table t1 modify a bigint unsigned; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +select * from t1; +a b +1 NULL +4294967295 NULL +use test; +select * from t1; +a b +1 NULL +4294967295 NULL +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +create table t2 (a int unsigned auto_increment primary key, b int); +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(10) unsigned NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +alter table t2 modify a bigint; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` bigint(20) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +alter table t2 modify a bigint auto_increment; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t1,t2; +# +# MDEV-8432: Slave cannot replicate signed integer-type values +# with high bit set to 1 +# Test replication when we have int on master and bigint on slave +# +create table t1 (a int unsigned primary key, b int); +SET GLOBAL SLAVE_TYPE_CONVERSIONS='ALL_NON_LOSSY'; +alter table t1 modify a bigint unsigned; +insert into t1 (a) values (1),((1<<32)-1); +select * from t1; +a b +1 NULL +4294967295 NULL +SET GLOBAL SLAVE_TYPE_CONVERSIONS=''; +drop table t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_alter.test b/mysql-test/suite/rpl/t/rpl_alter.test index 630197f8637..8b8bcfb3d26 100644 --- a/mysql-test/suite/rpl/t/rpl_alter.test +++ b/mysql-test/suite/rpl/t/rpl_alter.test @@ -15,4 +15,57 @@ drop database mysqltest; sync_slave_with_master; # End of 4.1 tests + +connection master; +use test; + +--echo # +--echo # Test bug where ALTER TABLE MODIFY didn't replicate properly +--echo # + +create table t1 (a int unsigned primary key, b int); +show create table t1; +insert into t1 (a) values (1),((1<<32)-1); +select * from t1; +alter table t1 modify a bigint; +show create table t1; +select * from t1; +alter table t1 modify a int unsigned; +show create table t1; +select * from t1; +alter table t1 modify a bigint unsigned; +show create table t1; +select * from t1; +sync_slave_with_master; +use test; +select * from t1; +show create table t1; +connection master; +# +create table t2 (a int unsigned auto_increment primary key, b int); +show create table t2; +alter table t2 modify a bigint; +show create table t2; +alter table t2 modify a bigint auto_increment; +show create table t2; +drop table t1,t2; + +--echo # +--echo # MDEV-8432: Slave cannot replicate signed integer-type values +--echo # with high bit set to 1 +--echo # Test replication when we have int on master and bigint on slave +--echo # + +create table t1 (a int unsigned primary key, b int); +sync_slave_with_master; +SET GLOBAL SLAVE_TYPE_CONVERSIONS='ALL_NON_LOSSY'; +alter table t1 modify a bigint unsigned; +connection master; +insert into t1 (a) values (1),((1<<32)-1); +sync_slave_with_master; +select * from t1; +SET GLOBAL SLAVE_TYPE_CONVERSIONS=''; +connection master; +drop table t1; + --source include/rpl_end.inc diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 42a30760a86..5550eebf1a3 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -821,3 +821,14 @@ FROM ( SELECT * FROM t2 ) AS sq2, t3 ORDER BY field; drop table t3, t2, t1; + +--echo # +--echo # MDEV-7821 - Server crashes in Item_func_group_concat::fix_fields on 2nd +--echo # execution of PS +--echo # +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1),(2); +PREPARE stmt FROM "SELECT GROUP_CONCAT(t1a.a ORDER BY 1, t1a.a=0) FROM t1 AS t1a, t1 AS t1b GROUP BY t1a.a"; +EXECUTE stmt; +EXECUTE stmt; +DROP TABLE t1; diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index 24fe1ef7984..25d8cc1067a 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -391,6 +391,11 @@ set optimizer_switch=@optimizer_switch_save; drop view v_merge, vm; drop table t1,tv; +--echo # +--echo # MDEV-4017 - GET_LOCK() with negative timeouts has strange behavior +--echo # +SELECT GET_LOCK('ul1', NULL); +SELECT GET_LOCK('ul1', -1); --echo # --echo # End of 5.5 tests diff --git a/mysql-test/t/innodb_load_xa.test b/mysql-test/t/innodb_load_xa.test index 7eac20441ca..9ebcb3faaba 100644 --- a/mysql-test/t/innodb_load_xa.test +++ b/mysql-test/t/innodb_load_xa.test @@ -17,3 +17,6 @@ commit; --source include/show_binlog_events.inc drop table t1; uninstall plugin innodb; + +--source include/restart_mysqld.inc + diff --git a/mysql-test/t/merge_recover-master.opt b/mysql-test/t/myisam_recover-master.opt similarity index 100% rename from mysql-test/t/merge_recover-master.opt rename to mysql-test/t/myisam_recover-master.opt diff --git a/mysql-test/t/merge_recover.test b/mysql-test/t/myisam_recover.test similarity index 61% rename from mysql-test/t/merge_recover.test rename to mysql-test/t/myisam_recover.test index f2cb204eeb6..49fe9c33460 100644 --- a/mysql-test/t/merge_recover.test +++ b/mysql-test/t/myisam_recover.test @@ -1,5 +1,9 @@ +--source include/count_sessions.inc + +--echo # +--echo # Tests for corrupted MyISAM tables and MyISAMMRG tables with corrupted +--echo # children.. --echo # ---echo # Test of MyISAM MRG tables with corrupted children. --echo # Run with --myisam-recover=force option. --echo # --echo # Preparation: we need to make sure that the merge parent @@ -57,10 +61,10 @@ eval $lock; --echo # connection default; --echo # ---echo # We have to disable the ps-protocol, to avoid +--echo # We have to disable the ps-protocol, to avoid --echo # "Prepared statement needs to be re-prepared" errors --echo # -- table def versions change all the time with full table cache. ---echo # +--echo # --disable_ps_protocol --disable_warnings drop table if exists t1, t1_mrg, t1_copy; @@ -69,12 +73,12 @@ let $MYSQLD_DATADIR=`select @@datadir`; --echo # --echo # Prepare a MERGE engine table, that refers to a corrupted --echo # child. ---echo # +--echo # create table t1 (a int, key(a)) engine=myisam; create table t1_mrg (a int) union (t1) engine=merge; --echo # --echo # Create a table with a corrupted index file: ---echo # save an old index file, insert more rows, +--echo # save an old index file, insert more rows, --echo # overwrite the new index file with the old one. --echo # insert into t1 (a) values (1), (2), (3); @@ -111,3 +115,66 @@ set @@global.table_open_cache=default; disconnect con1; connection default; --enable_ps_protocol + +--echo # +--echo # 18075170 - sql node restart required to avoid deadlock after +--echo # restore +--echo # +--echo # Check that auto-repair for MyISAM tables can now happen in the +--echo # middle of transaction, without aborting it. +--enable_prepare_warnings + +connection default; + +create table t1 (a int, key(a)) engine=myisam; +create table t2 (a int); +insert into t2 values (1); + +--echo # Create a table with a corrupted index file: +--echo # save an old index file, insert more rows, +--echo # overwrite the new index file with the old one. +insert into t1 (a) values (1); +flush table t1; +--copy_file $MYSQLD_DATADIR/test/t1.MYI $MYSQLD_DATADIR/test/t1_copy.MYI +insert into t1 (a) values (4); +flush table t1; +--remove_file $MYSQLD_DATADIR/test/t1.MYI +--copy_file $MYSQLD_DATADIR/test/t1_copy.MYI $MYSQLD_DATADIR/test/t1.MYI +--remove_file $MYSQLD_DATADIR/test/t1_copy.MYI + +--echo # Check table is needed to mark the table as crashed. +check table t1; + +--echo # At this point we have a corrupt t1 +set autocommit = 0; +select * from t2; +--echo # Without fix select from t1 will break the transaction. After the fix +--echo # transaction should be active and should hold lock on table t2. Alter +--echo # table from con2 will wait only if the transaction is not broken. +--replace_regex /'.*[\/\\]/'/ +select * from t1; + +connect(con2, localhost, root); +--SEND ALTER TABLE t2 ADD val INT + +connection default; +--echo # With fix we should have alter table waiting for t2 lock here. +let $wait_condition= + SELECT count(*) = 1 FROM information_schema.processlist WHERE state + LIKE "Waiting%" AND info = "ALTER TABLE t2 ADD val INT"; + +--source include/wait_condition.inc +ROLLBACK; +SET autocommit = 1; + +connection con2; +--REAP + +connection default; +disconnect con2; + +--echo # Cleanup +drop table t1, t2; + +# Wait till all disconnects are completed +-- source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/mysql_upgrade.test b/mysql-test/t/mysql_upgrade.test index e4923e5f429..13be03aa9bb 100644 --- a/mysql-test/t/mysql_upgrade.test +++ b/mysql-test/t/mysql_upgrade.test @@ -14,7 +14,6 @@ file_exists $MYSQLD_DATADIR/mysql_upgrade_info; --echo Run it again - should say already completed --replace_result $MYSQL_SERVER_VERSION VERSION ---error 1 --exec $MYSQL_UPGRADE 2>&1 # It should have created a file in the MySQL Servers datadir diff --git a/mysql-test/t/sp-group.test b/mysql-test/t/sp-group.test new file mode 100644 index 00000000000..2083ac97595 --- /dev/null +++ b/mysql-test/t/sp-group.test @@ -0,0 +1,187 @@ +--source include/have_innodb.inc + +drop table if exists t1; +drop view if exists view_t1; + +# +# Test case for MDEV 7601, MDEV-7594 and MDEV-7555 +# Server crashes in functions related to stored procedures +# Server crashes in different ways while executing concurrent +# flow involving views and non-empty sql_mode with ONLY_FULL_GROUP_BY +# + +SET sql_mode=ONLY_FULL_GROUP_BY; + +CREATE TABLE t1 ( + pk INT, + f0 INT, f1 INT, f2 INT, f3 INT, f4 INT, + f5 INT, f6 INT, f7 INT, f8 INT, f9 INT, + PRIMARY KEY (pk) +); + +CREATE VIEW view_t1 AS SELECT * FROM t1; +CREATE PROCEDURE s1() + SELECT * FROM ( + INFORMATION_SCHEMA.`INNODB_BUFFER_PAGE_LRU` AS table1 + LEFT JOIN test.view_t1 AS table2 + ON ( table2.`f6` = table1.FREE_PAGE_CLOCK) + ) + ORDER BY table1.NUMBER_RECORDS + LIMIT 0 +; +CALL s1; +CALL s1; + +drop table t1; +drop view view_t1; +drop procedure s1; + +# +# MDEV-7590 +# Server crashes in st_select_lex_unit::cleanup on executing a trigger +# + +CREATE TABLE A ( + pk INTEGER AUTO_INCREMENT, + col_int_key INTEGER, + col_varchar_key VARCHAR(1), + PRIMARY KEY (pk) +) ENGINE=MyISAM; +CREATE VIEW view_A AS SELECT * FROM A; +CREATE TABLE C ( + pk INTEGER AUTO_INCREMENT, + col_int_nokey INTEGER, + col_int_key INTEGER, + col_date_key DATE, + col_date_nokey DATE, + col_time_key TIME, + col_time_nokey TIME, + col_datetime_key DATETIME, + col_datetime_nokey DATETIME, + col_varchar_key VARCHAR(1), + col_varchar_nokey VARCHAR(1), + PRIMARY KEY (pk) +) ENGINE=MyISAM; +CREATE VIEW view_C AS SELECT * FROM C; +CREATE TABLE AA ( + pk INTEGER AUTO_INCREMENT, + col_int_nokey INTEGER, + col_int_key INTEGER, + col_date_key DATE, + col_date_nokey DATE, + col_time_key TIME, + col_time_nokey TIME, + col_datetime_key DATETIME, + col_datetime_nokey DATETIME, + col_varchar_key VARCHAR(1), + col_varchar_nokey VARCHAR(1), + PRIMARY KEY (pk), + KEY (col_varchar_key, col_int_key) +) ENGINE=MyISAM; +CREATE VIEW view_AA AS SELECT * FROM AA; +CREATE TABLE BB ( + pk INTEGER AUTO_INCREMENT, + col_int_key INTEGER, + col_varchar_key VARCHAR(1), + col_varchar_nokey VARCHAR(1), + PRIMARY KEY (pk), + KEY (col_varchar_key, col_int_key) +) ENGINE=MyISAM; +CREATE VIEW view_BB AS SELECT * FROM BB; +CREATE TABLE DD ( + pk INTEGER AUTO_INCREMENT, + col_int_key INTEGER, + col_date_key DATE, + col_time_key TIME, + col_datetime_key DATETIME, + col_varchar_key VARCHAR(1), + PRIMARY KEY (pk), + KEY (col_varchar_key, col_int_key) +) ENGINE=MyISAM; +CREATE VIEW view_DD AS SELECT * FROM DD; +CREATE TRIGGER k BEFORE INSERT ON `DD` FOR EACH ROW INSERT INTO `view_BB` SELECT * FROM `view_A` LIMIT 0 ; +CREATE TRIGGER r BEFORE INSERT ON `A` FOR EACH ROW INSERT INTO `view_AA` SELECT * FROM `view_C` LIMIT 0 ; +--error ER_WRONG_AUTO_KEY +ALTER TABLE `DD` DROP PRIMARY KEY; +INSERT INTO `view_A` ( `pk` ) VALUES (NULL); +--error 0,ER_WRONG_VALUE_COUNT_ON_ROW +INSERT INTO `DD` ( `pk` ) VALUES (NULL); +INSERT INTO `A` ( `pk` ) VALUES (NULL); +--error 0,ER_WRONG_VALUE_COUNT_ON_ROW +INSERT INTO `view_DD` ( `pk` ) VALUES (NULL); + +drop trigger r; +drop trigger k; +drop view view_A,view_AA,view_C,view_BB,view_DD; +drop table A,C,AA,BB,DD; + +# +# MDEV-7581 +# Server crashes in st_select_lex_unit::cleanup after a sequence of statements +# + +CREATE TABLE A ( + i INT, + i1 INT, + i2 INT, + d1 DATE, + d2 DATE, + col_time_nokey1 TIME, + col_time_nokey2 TIME, + col_datetime_nokey1 DATETIME, + col_datetime_nokey2 DATETIME, + col_varchar_nokey1 VARCHAR(1), + col_varchar_nokey2 VARCHAR(1) +) ENGINE=MyISAM; + +CREATE VIEW view_A AS SELECT * FROM A; + +CREATE TABLE B ( + col_varchar_nokey VARCHAR(1) +) ENGINE=MyISAM; + +CREATE TABLE AA ( + i INT, + i1 INT, + i2 INT, + d1 DATE, + d2 DATE, + col_time_nokey1 TIME, + col_time_nokey2 TIME, + col_datetime_nokey1 DATETIME, + col_datetime_nokey2 DATETIME, + col_varchar_nokey1 VARCHAR(1), + col_varchar_nokey2 VARCHAR(1) +) ENGINE=MyISAM; + +CREATE VIEW view_AA AS SELECT * FROM AA; + +CREATE TABLE DD ( + i INT, + i1 INT, + i2 INT, + d1 DATE, + d2 DATE, + col_time_nokey1 TIME, + col_time_nokey2 TIME, + col_datetime_nokey1 DATETIME, + col_datetime_nokey2 DATETIME, + col_varchar_nokey1 VARCHAR(1), + col_varchar_nokey2 VARCHAR(1) +) ENGINE=MyISAM; + +CREATE VIEW view_DD AS SELECT * FROM DD; + +CREATE TRIGGER tr1 BEFORE INSERT ON `AA` FOR EACH ROW INSERT INTO `view_A` SELECT * FROM `view_AA` LIMIT 0 ; +CREATE TRIGGER tr2 BEFORE INSERT ON `B` FOR EACH ROW INSERT INTO `D` SELECT * FROM `A` LIMIT 0 ; + +INSERT INTO `view_AA` ( `i` ) VALUES (1); +INSERT INTO `AA` ( `i` ) VALUES (2); +DELETE FROM `B`; +INSERT INTO `view_DD` ( `i` ) VALUES (1); +INSERT INTO `view_AA` ( `i` ) VALUES (3); + +drop trigger tr1; +drop trigger tr2; +drop view view_A, view_AA,view_DD; +drop table A,B,AA,DD; diff --git a/mysql-test/t/subselect_sj_mat.test b/mysql-test/t/subselect_sj_mat.test index 912e9d5befd..dd30b21201d 100644 --- a/mysql-test/t/subselect_sj_mat.test +++ b/mysql-test/t/subselect_sj_mat.test @@ -1841,5 +1841,20 @@ drop database mysqltest2; drop database mysqltest3; drop database mysqltest4; ---echo # End of 5.5 tests +--echo # +--echo # MDEV-7810 Wrong result on execution of a query as a PS +--echo # (both 1st and further executions) +CREATE TABLE t1 (a INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0),(8); + +SELECT a FROM (SELECT DISTINCT * FROM t1) AS sq WHERE a IN (SELECT MIN(t2.a) FROM (t1 AS t2)); +PREPARE stmt FROM " +SELECT a FROM (SELECT DISTINCT * FROM t1) AS sq WHERE a IN (SELECT MIN(t2.a) FROM (t1 AS t2)) +"; +execute stmt; +execute stmt; + +drop table t1; + +--echo # End of 5.5 tests diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 0432db0ba31..ca70cd1efa7 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -5380,6 +5380,27 @@ show create view v2; drop view v2; drop table t1; +--echo # +--echo # MDEV-8554: Server crashes in base_list_iterator::next_fast on 1st execution of PS with a multi-table update +--echo # +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1),(2); # Not necessary, the table can be empty + +CREATE TABLE t2 (b INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (3),(4); # Not necessary, the table can be empty + +CREATE TABLE t3 (c INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (5),(6); # Not necessary, the table can be empty + +CREATE OR REPLACE ALGORITHM=MERGE VIEW v3 AS SELECT * FROM t3; + +PREPARE stmt FROM 'UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM t3 )'; +UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM v3 ); +EXECUTE stmt; + +DROP TABLE t1, t2, t3; +DROP VIEW v3; + --echo # ----------------------------------------------------------------- --echo # -- End of 5.5 tests. --echo # ----------------------------------------------------------------- diff --git a/mysys/ptr_cmp.c b/mysys/ptr_cmp.c index a481b4d961c..6e373e98972 100644 --- a/mysys/ptr_cmp.c +++ b/mysys/ptr_cmp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -91,6 +91,7 @@ static int ptr_compare(size_t *compare_length, uchar **a, uchar **b) reg3 int length= *compare_length; reg1 uchar *first,*last; + DBUG_ASSERT(length > 0); first= *a; last= *b; while (--length) { diff --git a/regex/regcomp.c b/regex/regcomp.c index abc18174f56..b1074a1d79c 100644 --- a/regex/regcomp.c +++ b/regex/regcomp.c @@ -1,3 +1,11 @@ +/* Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved. + See file COPYRIGHT for details. + + This file was modified by Oracle on 2015-05-18 for 32-bit compatibility. + + Modifications copyright (c) 2015, Oracle and/or its affiliates. All rights + reserved. */ + #include #include #include @@ -133,12 +141,26 @@ CHARSET_INFO *charset; } else len = strlen((char *)pattern); + /* + Find the maximum len we can safely process + without a rollover and a mis-malloc. + p->ssize is a sopno is a long (32+ bit signed); + size_t is 16+ bit unsigned. + */ + { + size_t new_ssize = len / (size_t)2 * (size_t)3 + (size_t)1; /* ugh */ + if ((new_ssize < len) || /* size_t rolled over */ + ((SIZE_T_MAX / sizeof(sop)) < new_ssize) || /* malloc arg */ + (new_ssize > LONG_MAX)) /* won't fit in ssize */ + return(REG_ESPACE); /* MY_REG_ESPACE or MY_REG_INVARG */ + p->ssize = new_ssize; + } + /* do the mallocs early so failure handling is easy */ g = (struct re_guts *)malloc(sizeof(struct re_guts) + (NC-1)*sizeof(cat_t)); if (g == NULL) return(REG_ESPACE); - p->ssize = (long) (len/(size_t)2*(size_t)3 + (size_t)1); /* ugh */ p->strip = (sop *)malloc(p->ssize * sizeof(sop)); p->slen = 0; if (p->strip == NULL) { diff --git a/sql/field.cc b/sql/field.cc index a24ffe28600..aeeacf7f88d 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7612,7 +7612,8 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) } Field_blob::store_length(length); - if (table->copy_blobs || length <= MAX_FIELD_WIDTH) + if ((table->copy_blobs || length <= MAX_FIELD_WIDTH) && + from != value.ptr()) { // Must make a copy value.copy(from, length, cs); from= value.ptr(); diff --git a/sql/item.cc b/sql/item.cc index f8d1c85f447..878c9604ca2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1146,18 +1146,24 @@ Item *Item::safe_charset_converter(CHARSET_INFO *tocs) because Item_singlerow_subselect later calls Item_cache-specific methods, e.g. row[i]->store() and row[i]->cache_value(). - Let's wrap Item_func_conv_charset to a new Item_cache, + Let's wrap Item_func_conv_charset in a new Item_cache, so the Item_cache-specific methods can still be used for Item_singlerow_subselect::row[i] safely. + As a bonus we cache the converted value, instead of converting every time + TODO: we should eventually check all other use cases of change_item_tree(). Perhaps some more potentially dangerous substitution examples exist. */ Item *Item_cache::safe_charset_converter(CHARSET_INFO *tocs) { - Item_func_conv_charset *conv= new Item_func_conv_charset(example, tocs, 1); + if (!example) + return Item::safe_charset_converter(tocs); + Item *conv= example->safe_charset_converter(tocs); + if (conv == example) + return this; Item_cache *cache; - if (!conv || !conv->safe || !(cache= new Item_cache_str(conv))) + if (!conv || !(cache= new Item_cache_str(conv))) return NULL; // Safe conversion is not possible, or OEM cache->setup(conv); cache->fixed= false; // Make Item::fix_fields() happy @@ -4409,18 +4415,23 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, Item_ident *resolved_item, Item_ident *mark_item) { - const char *db_name= (resolved_item->db_name ? - resolved_item->db_name : ""); - const char *table_name= (resolved_item->table_name ? - resolved_item->table_name : ""); + DBUG_ENTER("mark_as_dependent"); + /* store pointer on SELECT_LEX from which item is dependent */ if (mark_item && mark_item->can_be_depended) + { + DBUG_PRINT("info", ("mark_item: %p lex: %p", mark_item, last)); mark_item->depended_from= last; - if (current->mark_as_dependent(thd, last, /** resolved_item psergey-thu - **/mark_item)) - return TRUE; + } + if (current->mark_as_dependent(thd, last, + /** resolved_item psergey-thu **/ mark_item)) + DBUG_RETURN(TRUE); if (thd->lex->describe & DESCRIBE_EXTENDED) { + const char *db_name= (resolved_item->db_name ? + resolved_item->db_name : ""); + const char *table_name= (resolved_item->table_name ? + resolved_item->table_name : ""); push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED), db_name, (db_name[0] ? "." : ""), @@ -4428,7 +4439,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, resolved_item->field_name, current->select_number, last->select_number); } - return FALSE; + DBUG_RETURN(FALSE); } @@ -4877,7 +4888,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) non aggregated fields of the outer select. */ marker= select->cur_pos_in_select_list; - select->non_agg_fields.push_back(this); + select->join->non_agg_fields.push_back(this); } if (*from_field != view_ref_found) { @@ -5293,9 +5304,10 @@ bool Item_field::fix_fields(THD *thd, Item **reference) fixed= 1; if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !outer_fixed && !thd->lex->in_sum_func && - thd->lex->current_select->cur_pos_in_select_list != UNDEF_POS) + thd->lex->current_select->cur_pos_in_select_list != UNDEF_POS && + thd->lex->current_select->join) { - thd->lex->current_select->non_agg_fields.push_back(this); + thd->lex->current_select->join->non_agg_fields.push_back(this); marker= thd->lex->current_select->cur_pos_in_select_list; } mark_non_agg_field: @@ -6742,7 +6754,7 @@ Item_ref::Item_ref(Name_resolution_context *context_arg, /* This constructor used to create some internals references over fixed items */ - if (ref && *ref && (*ref)->fixed) + if ((set_properties_only= (ref && *ref && (*ref)->fixed))) set_properties(); } @@ -6786,7 +6798,7 @@ Item_ref::Item_ref(TABLE_LIST *view_arg, Item **item, /* This constructor is used to create some internal references over fixed items */ - if (ref && *ref && (*ref)->fixed) + if ((set_properties_only= (ref && *ref && (*ref)->fixed))) set_properties(); } @@ -6861,7 +6873,11 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) DBUG_ASSERT(fixed == 0); SELECT_LEX *current_sel= thd->lex->current_select; - if (!ref || ref == not_found_item) + if (set_properties_only) + { + /* do nothing */ + } + else if (!ref || ref == not_found_item) { DBUG_ASSERT(reference_trough_name != 0); if (!(ref= resolve_ref_in_select_and_group(thd, this, @@ -6879,7 +6895,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) { /* The current reference cannot be resolved in this query. */ my_error(ER_BAD_FIELD_ERROR,MYF(0), - this->full_name(), current_thd->where); + this->full_name(), thd->where); goto error; } @@ -7014,7 +7030,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) goto error; thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex, - thd->lex->current_select, fld, fld); + current_sel, fld, fld); /* A reference is resolved to a nest level that's outer or the same as the nest level of the enclosing set function : adjust the value of @@ -7031,7 +7047,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) { /* The item was not a table field and not a reference */ my_error(ER_BAD_FIELD_ERROR, MYF(0), - this->full_name(), current_thd->where); + this->full_name(), thd->where); goto error; } /* Should be checked in resolve_ref_in_select_and_group(). */ diff --git a/sql/item.h b/sql/item.h index 4aaa67d9a1d..f8e8ead47dd 100644 --- a/sql/item.h +++ b/sql/item.h @@ -631,7 +631,7 @@ public: */ uint name_length; /* Length of name */ uint decimals; - int8 marker; + int marker; bool maybe_null; /* If item may be null */ bool in_rollup; /* If used in GROUP BY list of a query with ROLLUP */ @@ -2946,6 +2946,7 @@ class Item_ref :public Item_ident { protected: void set_properties(); + bool set_properties_only; // the item doesn't need full fix_fields public: enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF }; Field *result_field; /* Save result here */ @@ -2955,7 +2956,7 @@ public: const char *db_arg, const char *table_name_arg, const char *field_name_arg) :Item_ident(context_arg, db_arg, table_name_arg, field_name_arg), - result_field(0), ref(0), reference_trough_name(1) {} + set_properties_only(0), result_field(0), ref(0), reference_trough_name(1) {} /* This constructor is used in two scenarios: A) *item = NULL @@ -2978,7 +2979,7 @@ public: /* Constructor need to process subselect with temporary tables (see Item) */ Item_ref(THD *thd, Item_ref *item) - :Item_ident(thd, item), result_field(item->result_field), ref(item->ref) {} + :Item_ident(thd, item), set_properties_only(0), result_field(item->result_field), ref(item->ref) {} enum Type type() const { return REF_ITEM; } enum Type real_type() const { return ref ? (*ref)->type() : REF_ITEM; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 34569cb938f..0c34ce5af4d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4196,7 +4196,25 @@ longlong Item_func_get_lock::val_int() it's not guaranteed to be same as on master. */ if (thd->slave_thread) + { + null_value= 0; DBUG_RETURN(1); + } + + if (args[1]->null_value || + (!args[1]->unsigned_flag && ((longlong) timeout < 0))) + { + char buf[22]; + if (args[1]->null_value) + strmov(buf, "NULL"); + else + llstr(((longlong) timeout), buf); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), + "timeout", buf, "get_lock"); + null_value= 1; + DBUG_RETURN(0); + } mysql_mutex_lock(&LOCK_user_locks); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 62df666b71f..4837cb45a91 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -464,6 +464,7 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, { List_iterator it(upper_refs); Ref_to_outside *upper; + DBUG_ENTER("recalc_used_tables"); used_tables_cache= 0; while ((upper= it++)) @@ -523,6 +524,8 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, he has done const table detection, and that will be our chance to update const_tables_cache. */ + DBUG_PRINT("exit", ("used_tables_cache: %llx", used_tables_cache)); + DBUG_VOID_RETURN; } @@ -1989,7 +1992,7 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join) */ bool -Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, +Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, Item **where_item, Item **having_item) { @@ -1999,7 +2002,6 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, during JOIN::optimize: this->tmp_having= this->having; this->having= 0; */ Item* join_having= join->having ? join->having : join->tmp_having; - DBUG_ENTER("Item_in_subselect::create_single_in_to_exists_cond"); *where_item= NULL; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index d8970ca26b5..adf48f6feec 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3514,9 +3514,17 @@ bool Item_func_group_concat::setup(THD *thd) "all_fields". The resulting field list is used as input to create tmp table columns. */ - if (arg_count_order && - setup_order(thd, args, context->table_list, list, all_fields, *order)) - DBUG_RETURN(TRUE); + if (arg_count_order) + { + uint n_elems= arg_count_order + all_fields.elements; + ref_pointer_array= static_cast(thd->alloc(sizeof(Item*) * n_elems)); + if (!ref_pointer_array) + DBUG_RETURN(TRUE); + memcpy(ref_pointer_array, args, arg_count * sizeof(Item*)); + if (setup_order(thd, ref_pointer_array, context->table_list, list, + all_fields, *order)) + DBUG_RETURN(TRUE); + } count_field_types(select_lex, tmp_table_param, all_fields, 0); tmp_table_param->force_copy_fields= force_copy_fields; diff --git a/sql/item_sum.h b/sql/item_sum.h index f074cc6c822..86093f5d4f5 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1394,6 +1394,7 @@ class Item_func_group_concat : public Item_sum String *separator; TREE tree_base; TREE *tree; + Item **ref_pointer_array; /** If DISTINCT is used with this GROUP_CONCAT, this member is used to filter diff --git a/sql/log.cc b/sql/log.cc index 9a20706ae18..2f64474ce3f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3778,6 +3778,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) { int error; char *to_purge_if_included= NULL; + ulonglong log_space_reclaimed= 0; DBUG_ENTER("purge_first_log"); DBUG_ASSERT(is_open()); @@ -3826,17 +3827,13 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_SUICIDE();); - mysql_mutex_lock(&rli->log_space_lock); rli->relay_log.purge_logs(to_purge_if_included, included, - 0, 0, &rli->log_space_total); - mysql_mutex_unlock(&rli->log_space_lock); + 0, 0, &log_space_reclaimed); - /* - Ok to broadcast after the critical region as there is no risk of - the mutex being destroyed by this thread later - this helps save - context switches - */ + mysql_mutex_lock(&rli->log_space_lock); + rli->log_space_total-= log_space_reclaimed; mysql_cond_broadcast(&rli->log_space_cond); + mysql_mutex_unlock(&rli->log_space_lock); /* * Need to update the log pos because purge logs has been called @@ -3885,8 +3882,8 @@ int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads @param need_mutex @param need_update_threads If we want to update the log coordinates of all threads. False for relay logs, true otherwise. - @param freed_log_space If not null, decrement this variable of - the amount of log space freed + @param reclaimeed_log_space If not null, increment this variable to + the amount of log space freed @note If any of the logs before the deleted one is in use, @@ -3902,10 +3899,10 @@ int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads */ int MYSQL_BIN_LOG::purge_logs(const char *to_log, - bool included, - bool need_mutex, - bool need_update_threads, - ulonglong *decrease_log_space) + bool included, + bool need_mutex, + bool need_update_threads, + ulonglong *reclaimed_space) { int error= 0; bool exit_loop= 0; @@ -3970,7 +3967,7 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log, err: /* Read each entry from purge_index_file and delete the file. */ if (is_inited_purge_index_file() && - (error= purge_index_entry(thd, decrease_log_space, FALSE))) + (error= purge_index_entry(thd, reclaimed_space, FALSE))) sql_print_error("MSYQL_BIN_LOG::purge_logs failed to process registered files" " that would be purged."); close_purge_index_file(); @@ -4075,7 +4072,7 @@ int MYSQL_BIN_LOG::register_create_index_entry(const char *entry) DBUG_RETURN(register_purge_index_entry(entry)); } -int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, +int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space, bool need_mutex) { DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry"); @@ -4195,8 +4192,8 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, DBUG_PRINT("info",("purging %s",log_info.log_file_name)); if (!my_delete(log_info.log_file_name, MYF(0))) { - if (decrease_log_space) - *decrease_log_space-= s.st_size; + if (reclaimed_space) + *reclaimed_space+= s.st_size; } else { diff --git a/sql/log_event.cc b/sql/log_event.cc index f7df264f416..0e10b148097 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1252,9 +1252,10 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, } data_len= uint4korr(buf + EVENT_LEN_OFFSET); if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN || - data_len > current_thd->variables.max_allowed_packet) + data_len > max(current_thd->variables.max_allowed_packet, + opt_binlog_rows_event_max_size + MAX_LOG_EVENT_HEADER)) { - DBUG_PRINT("error",("data_len: %ld", data_len)); + DBUG_PRINT("error",("data_len: %lu", data_len)); result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS : LOG_READ_TOO_LARGE); goto end; @@ -1375,7 +1376,7 @@ failed my_b_read")); */ DBUG_RETURN(0); } - uint data_len = uint4korr(head + EVENT_LEN_OFFSET); + ulong data_len = uint4korr(head + EVENT_LEN_OFFSET); char *buf= 0; const char *error= 0; Log_event *res= 0; @@ -1384,7 +1385,8 @@ failed my_b_read")); uint max_allowed_packet= thd ? slave_max_allowed_packet:~(uint)0; #endif - if (data_len > max_allowed_packet) + if (data_len > max(max_allowed_packet, + opt_binlog_rows_event_max_size + MAX_LOG_EVENT_HEADER)) { error = "Event too big"; goto err; @@ -1418,7 +1420,7 @@ err: { DBUG_ASSERT(error != 0); sql_print_error("Error in Log_event::read_log_event(): " - "'%s', data_len: %d, event_type: %d", + "'%s', data_len: %lu, event_type: %d", error,data_len,head[EVENT_TYPE_OFFSET]); my_free(buf); /* diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2008fc54245..357b62ff2a8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2644,7 +2644,6 @@ void unlink_thd(THD *thd) thd->add_status_to_global(); mysql_mutex_lock(&LOCK_thread_count); - thread_count--; thd->unlink(); /* Used by binlog_reset_master. It would be cleaner to use @@ -2652,6 +2651,16 @@ void unlink_thd(THD *thd) sync feature has been shut down at this point. */ DBUG_EXECUTE_IF("sleep_after_lock_thread_count_before_delete_thd", sleep(5);); + if (unlikely(abort_loop)) + { + /* + During shutdown, we have to delete thd inside the mutex + to not refer to mutexes that may be deleted during shutdown + */ + delete thd; + thd= 0; + } + thread_count--; mysql_mutex_unlock(&LOCK_thread_count); delete thd; diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc index 9480a9e0454..5b75d6c30ea 100644 --- a/sql/rpl_handler.cc +++ b/sql/rpl_handler.cc @@ -38,8 +38,6 @@ typedef struct Trans_binlog_info { char log_file[FN_REFLEN]; } Trans_binlog_info; -static pthread_key(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); - int get_user_var_int(const char *name, long long int *value, int *null_value) { @@ -143,13 +141,6 @@ int delegates_init() } #endif - if (pthread_key_create(&RPL_TRANS_BINLOG_INFO, NULL)) - { - sql_print_error("Error while creating pthread specific data key for replication. " - "Please report a bug."); - return 1; - } - return 0; } @@ -195,27 +186,27 @@ void delegates_destroy() int Trans_delegate::after_commit(THD *thd, bool all) { Trans_param param; + Trans_binlog_info *log_info; bool is_real_trans= (all || thd->transaction.all.ha_list == 0); + int ret= 0; param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; - Trans_binlog_info *log_info= - my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); + log_info= thd->semisync_info; - param.log_file= log_info ? log_info->log_file : 0; + param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; param.log_pos= log_info ? log_info->log_pos : 0; - int ret= 0; FOREACH_OBSERVER(ret, after_commit, false, (¶m)); /* This is the end of a real transaction or autocommit statement, we - can free the memory allocated for binlog file and position. + can mark the memory unused. */ if (is_real_trans && log_info) { - my_pthread_setspecific_ptr(RPL_TRANS_BINLOG_INFO, NULL); - my_free(log_info); + log_info->log_file[0]= 0; + log_info->log_pos= 0; } return ret; } @@ -223,27 +214,27 @@ int Trans_delegate::after_commit(THD *thd, bool all) int Trans_delegate::after_rollback(THD *thd, bool all) { Trans_param param; + Trans_binlog_info *log_info; bool is_real_trans= (all || thd->transaction.all.ha_list == 0); + int ret= 0; param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; - Trans_binlog_info *log_info= - my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); - - param.log_file= log_info ? log_info->log_file : 0; + log_info= thd->semisync_info; + + param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; param.log_pos= log_info ? log_info->log_pos : 0; - int ret= 0; FOREACH_OBSERVER(ret, after_rollback, false, (¶m)); /* This is the end of a real transaction or autocommit statement, we - can free the memory allocated for binlog file and position. + can mark the memory unused. */ if (is_real_trans && log_info) { - my_pthread_setspecific_ptr(RPL_TRANS_BINLOG_INFO, NULL); - my_free(log_info); + log_info->log_file[0]= 0; + log_info->log_pos= 0; } return ret; } @@ -254,25 +245,24 @@ int Binlog_storage_delegate::after_flush(THD *thd, bool synced) { Binlog_storage_param param; + Trans_binlog_info *log_info; uint32 flags=0; + int ret= 0; + if (synced) flags |= BINLOG_STORAGE_IS_SYNCED; - Trans_binlog_info *log_info= - my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); - - if (!log_info) + if (!(log_info= thd->semisync_info)) { if(!(log_info= - (Trans_binlog_info *)my_malloc(sizeof(Trans_binlog_info), MYF(0)))) + (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0)))) return 1; - my_pthread_setspecific_ptr(RPL_TRANS_BINLOG_INFO, log_info); + thd->semisync_info= log_info; } - + strcpy(log_info->log_file, log_file+dirname_length(log_file)); log_info->log_pos = log_pos; - int ret= 0; FOREACH_OBSERVER(ret, after_flush, false, (¶m, log_info->log_file, log_info->log_pos, flags)); return ret; diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index e05ad5a8d43..9a20f71f4f7 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -876,6 +876,7 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * { Create_field *field_def= (Create_field*) alloc_root(thd->mem_root, sizeof(Create_field)); + bool unsigned_flag= 0; if (field_list.push_back(field_def)) DBUG_RETURN(NULL); @@ -885,8 +886,7 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * uint32 max_length= max_display_length_for_field(type(col), field_metadata(col)); - switch(type(col)) - { + switch(type(col)) { int precision; case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: @@ -925,6 +925,18 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * pack_length= field_metadata(col) & 0x00ff; break; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + /* + As we don't know if the integer was signed or not on the master, + assume we have same sign on master and slave. This is true when not + using conversions so it should be true also when using conversions. + */ + unsigned_flag= ((Field_num*) target_table->field[col])->unsigned_flag; + break; default: break; } @@ -932,12 +944,13 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d," " maybe_null: %d, unsigned_flag: %d, pack_length: %u", type(col), target_table->field[col]->field_name, - max_length, decimals, TRUE, FALSE, pack_length)); + max_length, decimals, TRUE, unsigned_flag, + pack_length)); field_def->init_for_tmp_table(type(col), max_length, decimals, TRUE, // maybe_null - FALSE, // unsigned_flag + unsigned_flag, pack_length); field_def->charset= target_table->field[col]->charset(); field_def->interval= interval; diff --git a/sql/slave.cc b/sql/slave.cc index 8ecdb484dbf..4636cb330e5 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3531,9 +3531,7 @@ pthread_handler_t handle_slave_sql(void *arg) rli->clear_error(); //tell the I/O thread to take relay_log_space_limit into account from now on - mysql_mutex_lock(&rli->log_space_lock); rli->ignore_log_space_limit= 0; - mysql_mutex_unlock(&rli->log_space_lock); rli->trans_retries= 0; // start from "no error" DBUG_PRINT("info", ("rli->trans_retries: %lu", rli->trans_retries)); @@ -5235,14 +5233,8 @@ static Log_event* next_event(Relay_log_info* rli) rli->ignore_log_space_limit= true; } - /* - If the I/O thread is blocked, unblock it. Ok to broadcast - after unlock, because the mutex is only destroyed in - ~Relay_log_info(), i.e. when rli is destroyed, and rli will - not be destroyed before we exit the present function. - */ - mysql_mutex_unlock(&rli->log_space_lock); mysql_cond_broadcast(&rli->log_space_cond); + mysql_mutex_unlock(&rli->log_space_lock); // Note that wait_for_update_relay_log unlocks lock_log ! rli->relay_log.wait_for_update_relay_log(rli->sql_thd); // re-acquire data lock since we released it earlier diff --git a/sql/sql_base.cc b/sql/sql_base.cc index dd10caa54f9..acf553b1041 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4067,10 +4067,11 @@ request_backoff_action(enum_open_table_action action_arg, * We met a broken table that needs repair, or a table that is not present on this MySQL server and needs re-discovery. To perform the action, we need an exclusive metadata lock on - the table. Acquiring an X lock while holding other shared - locks is very deadlock-prone. If this is a multi- statement - transaction that holds metadata locks for completed - statements, we don't do it, and report an error instead. + the table. Acquiring X lock while holding other shared + locks can easily lead to deadlocks. We rely on MDL deadlock + detector to discover them. If this is a multi-statement + transaction that holds metadata locks for completed statements, + we should keep these locks after discovery/repair. The action type in this case is OT_DISCOVER or OT_REPAIR. * Our attempt to acquire an MDL lock lead to a deadlock, detected by the MDL deadlock detector. The current @@ -4111,7 +4112,7 @@ request_backoff_action(enum_open_table_action action_arg, keep tables open between statements and a livelock is not possible. */ - if (action_arg != OT_REOPEN_TABLES && m_has_locks) + if (action_arg == OT_BACKOFF_AND_RETRY && m_has_locks) { my_error(ER_LOCK_DEADLOCK, MYF(0)); m_thd->mark_transaction_to_rollback(true); @@ -4138,6 +4139,32 @@ request_backoff_action(enum_open_table_action action_arg, } +/** + An error handler to mark transaction to rollback on DEADLOCK error + during DISCOVER / REPAIR. +*/ +class MDL_deadlock_discovery_repair_handler : public Internal_error_handler +{ +public: + virtual bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl) + { + if (sql_errno == ER_LOCK_DEADLOCK) + { + thd->mark_transaction_to_rollback(true); + } + /* + We have marked this transaction to rollback. Return false to allow + error to be reported or handled by other handlers. + */ + return false; + } +}; + /** Recover from failed attempt of open table by performing requested action. @@ -4153,6 +4180,12 @@ Open_table_context:: recover_from_failed_open() { bool result= FALSE; + MDL_deadlock_discovery_repair_handler handler; + /* + Install error handler to mark transaction to rollback on DEADLOCK error. + */ + m_thd->push_internal_handler(&handler); + /* Execute the action. */ switch (m_action) { @@ -4174,7 +4207,12 @@ recover_from_failed_open() m_thd->warning_info->clear_warning_info(m_thd->query_id); m_thd->clear_error(); // Clear error message - m_thd->mdl_context.release_transactional_locks(); + /* + Rollback to start of the current statement to release exclusive lock + on table which was discovered but preserve locks from previous statements + in current transaction. + */ + m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp()); break; } case OT_REPAIR: @@ -4188,12 +4226,18 @@ recover_from_failed_open() m_failed_table->table_name, FALSE); result= auto_repair_table(m_thd, m_failed_table); - m_thd->mdl_context.release_transactional_locks(); + /* + Rollback to start of the current statement to release exclusive lock + on table which was discovered but preserve locks from previous statements + in current transaction. + */ + m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp()); break; } default: DBUG_ASSERT(0); } + m_thd->pop_internal_handler(); /* Reset the pointers to conflicting MDL request and the TABLE_LIST element, set when we need auto-discovery or repair, @@ -6879,6 +6923,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (item->cached_table) { + DBUG_PRINT("info", ("using cached table")); /* This shortcut is used by prepared statements. We assume that TABLE_LIST *first_table is not changed during query execution (which diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f1260cbf516..e13cee8de4a 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1058,6 +1058,7 @@ THD::THD() file_id = 0; query_id= 0; query_name_consts= 0; + semisync_info= 0; db_charset= global_system_variables.collation_database; bzero(ha_data, sizeof(ha_data)); mysys_var=0; @@ -1467,6 +1468,8 @@ void THD::init(void) bzero((char *) &status_var, sizeof(status_var)); bzero((char *) &org_status_var, sizeof(org_status_var)); start_bytes_received= 0; + status_in_global= 0; + #ifdef WITH_WSREP wsrep_exec_mode= wsrep_applier ? REPL_RECV : LOCAL_STATE; wsrep_conflict_state= NO_CONFLICT; @@ -1491,8 +1494,8 @@ void THD::init(void) */ if (variables.wsrep_causal_reads) variables.wsrep_sync_wait|= WSREP_SYNC_WAIT_BEFORE_READ; +#endif /* WITH_WSREP */ -#endif if (variables.sql_log_bin) variables.option_bits|= OPTION_BIN_LOG; else @@ -1595,6 +1598,7 @@ void THD::change_user(void) cleanup(); reset_killed(); cleanup_done= 0; + status_in_global= 0; init(); stmt_map.reset(); my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, @@ -1727,6 +1731,7 @@ THD::~THD() mysql_audit_free_thd(this); if (rli_slave) rli_slave->cleanup_after_session(); + my_free(semisync_info); #endif free_root(&main_mem_root, MYF(0)); diff --git a/sql/sql_class.h b/sql/sql_class.h index c51d0f8dcc3..418c8043673 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -59,7 +59,6 @@ struct wsrep_thd_shadow { #endif class Reprepare_observer; class Relay_log_info; - class Query_log_event; class Load_log_event; class Slave_log_event; @@ -71,6 +70,7 @@ class Rows_log_event; class Sroutine_hash_entry; class User_level_lock; class user_var_entry; +struct Trans_binlog_info; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; @@ -1689,6 +1689,9 @@ public: */ const char *where; + /* Needed by MariaDB semi sync replication */ + Trans_binlog_info *semisync_info; + ulong client_capabilities; /* What the client supports */ ulong max_client_packet_length; @@ -1752,11 +1755,11 @@ public: /* Do not set socket timeouts for wait_timeout (used with threadpool) */ bool skip_wait_timeout; - /* container for handler's private per-connection data */ - Ha_data ha_data[MAX_HA]; - bool prepare_derived_at_open; + /* Set to 1 if status of this THD is already in global status */ + bool status_in_global; + /* To signal that the tmp table to be created is created for materialized derived table or a view. @@ -1765,6 +1768,9 @@ public: bool save_prep_leaf_list; + /* container for handler's private per-connection data */ + Ha_data ha_data[MAX_HA]; + #ifndef MYSQL_CLIENT binlog_cache_mngr * binlog_setup_trx_data(); @@ -3175,6 +3181,8 @@ public: { mysql_mutex_lock(&LOCK_status); add_to_status(&global_status_var, &status_var); + /* Mark that this THD status has already been added in global status */ + status_in_global= 1; mysql_mutex_unlock(&LOCK_status); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e2db7f76803..ca134ebccc5 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1928,7 +1928,6 @@ void st_select_lex::init_select() with_sum_func= 0; is_correlated= 0; cur_pos_in_select_list= UNDEF_POS; - non_agg_fields.empty(); cond_value= having_value= Item::COND_UNDEF; inner_refs_list.empty(); insert_tables= 0; @@ -1936,6 +1935,7 @@ void st_select_lex::init_select() m_non_agg_field_used= false; m_agg_func_used= false; name_visibility_map= 0; + join= 0; } /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a0f8e456800..aa59d76245b 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -877,8 +877,6 @@ public: bool no_wrap_view_item; /* exclude this select from check of unique_table() */ bool exclude_from_table_unique_test; - /* List of fields that aren't under an aggregate function */ - List non_agg_fields; /* index in the select list of the expression currently being fixed */ int cur_pos_in_select_list; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a0b98ec5601..499cda302b5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6175,6 +6175,8 @@ void THD::reset_for_next_command() thd->reset_current_stmt_binlog_format_row(); thd->binlog_unsafe_warning_flags= 0; + thd->save_prep_leaf_list= false; + DBUG_PRINT("debug", ("is_current_stmt_binlog_format_row(): %d", thd->is_current_stmt_binlog_format_row())); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fb7cafc595f..e542b08f911 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4904,6 +4904,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, KEY_FIELD *key_fields, *end, *field; uint sz; uint m= max(select_lex->max_equal_elems,1); + DBUG_ENTER("update_ref_and_keys"); + DBUG_PRINT("enter", ("normal_tables: %llx", normal_tables)); SELECT_LEX *sel=thd->lex->current_select; sel->cond_count= 0; @@ -4950,7 +4952,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, sz= max(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))* ((sel->cond_count*2 + sel->between_count)*m+1); if (!(key_fields=(KEY_FIELD*) thd->alloc(sz))) - return TRUE; /* purecov: inspected */ + DBUG_RETURN(TRUE); /* purecov: inspected */ and_level= 0; field= end= key_fields; *sargables= (SARGABLE_PARAM *) key_fields + @@ -4959,7 +4961,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, (*sargables)[0].field= 0; if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64)) - return TRUE; + DBUG_RETURN(TRUE); if (cond) { @@ -5009,16 +5011,16 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, for ( ; field != end ; field++) { if (add_key_part(keyuse,field)) - return TRUE; + DBUG_RETURN(TRUE); } if (select_lex->ftfunc_list->elements) { if (add_ft_keys(keyuse,join_tab,cond,normal_tables)) - return TRUE; + DBUG_RETURN(TRUE); } - return FALSE; + DBUG_RETURN(FALSE); } @@ -20705,7 +20707,7 @@ setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, Item_field *field; int cur_pos_in_select_list= 0; List_iterator li(fields); - List_iterator naf_it(thd->lex->current_select->non_agg_fields); + List_iterator naf_it(thd->lex->current_select->join->non_agg_fields); field= naf_it++; while (field && (item=li++)) diff --git a/sql/sql_select.h b/sql/sql_select.h index de5baeee151..4f807ff5b93 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -922,6 +922,9 @@ public: Item *pre_sort_idx_pushed_cond; void clean_pre_sort_join_tab(); + /* List of fields that aren't under an aggregate function */ + List non_agg_fields; + /* For "Using temporary+Using filesort" queries, JOIN::join_tab can point to either: @@ -1301,6 +1304,7 @@ public: all_fields= fields_arg; if (&fields_list != &fields_arg) /* Avoid valgrind-warning */ fields_list= fields_arg; + non_agg_fields.empty(); bzero((char*) &keyuse,sizeof(keyuse)); tmp_table_param.init(); tmp_table_param.end_write_records= HA_POS_ERROR; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e7a82927a40..df0b2035d29 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3126,7 +3126,10 @@ void calc_sum_of_all_status(STATUS_VAR *to) /* Add to this status from existing threads */ while ((tmp= it++)) - add_to_status(to, &tmp->status_var); + { + if (!tmp->status_in_global) + add_to_status(to, &tmp->status_var); + } mysql_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; diff --git a/sql/sql_test.cc b/sql/sql_test.cc index ec426e39ee3..dc6bc8187ff 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -248,7 +248,7 @@ TEST_join(JOIN *join) #define FT_KEYPART (MAX_REF_PARTS+10) -void print_keyuse(KEYUSE *keyuse) +static void print_keyuse(KEYUSE *keyuse) { char buff[256]; char buf2[64]; @@ -266,14 +266,11 @@ void print_keyuse(KEYUSE *keyuse) else fieldname= key_info->key_part[keyuse->keypart].field->field_name; ll2str(keyuse->used_tables, buf2, 16, 0); - DBUG_LOCK_FILE; fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s optimize: %u used_tables: %s " "ref_table_rows: %lu keypart_map: %0lx\n", keyuse->table->alias.c_ptr(), fieldname, str.ptr(), (uint) keyuse->optimize, buf2, (ulong) keyuse->ref_table_rows, (ulong) keyuse->keypart_map); - DBUG_UNLOCK_FILE; - //key_part_map keypart_map; --?? there can be several? } @@ -282,9 +279,9 @@ void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array) { DBUG_LOCK_FILE; fprintf(DBUG_FILE, "KEYUSE array (%d elements)\n", keyuse_array->elements); - DBUG_UNLOCK_FILE; for(uint i=0; i < keyuse_array->elements; i++) print_keyuse((KEYUSE*)dynamic_array_ptr(keyuse_array, i)); + DBUG_UNLOCK_FILE; } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 6e2e6e06ff7..87d3e86b2c7 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -1021,7 +1021,6 @@ bool st_select_lex::cleanup() { error= (bool) ((uint) error | (uint) lex_unit->cleanup()); } - non_agg_fields.empty(); inner_refs_list.empty(); exclude_from_table_unique_test= FALSE; DBUG_RETURN(error); @@ -1032,6 +1031,7 @@ void st_select_lex::cleanup_all_joins(bool full) { SELECT_LEX_UNIT *unit; SELECT_LEX *sl; + DBUG_ENTER("st_select_lex::cleanup_all_joins"); if (join) join->cleanup(full); @@ -1039,6 +1039,7 @@ void st_select_lex::cleanup_all_joins(bool full) for (unit= first_inner_unit(); unit; unit= unit->next_unit()) for (sl= unit->first_select(); sl; sl= sl->next_select()) sl->cleanup_all_joins(full); + DBUG_VOID_RETURN; } diff --git a/sql/table.cc b/sql/table.cc index b9310a196df..513508137ec 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5203,7 +5203,7 @@ Item *Field_iterator_table::create_item(THD *thd) if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS) { - select->non_agg_fields.push_back(item); + select->join->non_agg_fields.push_back(item); item->marker= select->cur_pos_in_select_list; select->set_non_agg_field_used(true); } diff --git a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c index ac8a1eac03c..dcbca492b08 100644 --- a/storage/innobase/dict/dict0crea.c +++ b/storage/innobase/dict/dict0crea.c @@ -1419,6 +1419,104 @@ dict_create_add_foreign_field_to_dictionary( table, foreign, trx)); } +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx) /*!< in: trx */ +{ + char* fk_def = mem_heap_alloc(foreign->heap, 4*1024); + const char* tbname; + char tablebuf[MAX_TABLE_NAME_LEN + 1] = ""; + int i; + char* bufend; + + tbname = dict_remove_db_name(foreign->id); + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + tbname, strlen(tbname), trx->mysql_thd, FALSE); + tablebuf[bufend - tablebuf] = '\0'; + + sprintf(fk_def, + (char *)"CONSTRAINT %s FOREIGN KEY (", (char *)tablebuf); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[i], + strlen(foreign->foreign_col_names[i]), + trx->mysql_thd, FALSE); + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + + strcat(fk_def,(char *)") REFERENCES "); + + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + foreign->referenced_table_name, + strlen(foreign->referenced_table_name), + trx->mysql_thd, TRUE); + tablebuf[bufend - tablebuf] = '\0'; + + strcat(fk_def, tablebuf); + strcat(fk_def, " ("); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[i], + strlen(foreign->referenced_col_names[i]), + trx->mysql_thd, FALSE); + buf[bufend - buf] = '\0'; + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + strcat(fk_def, (char *)")"); + + return fk_def; +} + +/********************************************************************//** +Convert foreign key column names from data dictionary to SQL-layer. +*/ +static +void +dict_foreign_def_get_fields( +/*========================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx, /*!< in: trx */ + char** field, /*!< out: foreign column */ + char** field2, /*!< out: referenced column */ + int col_no) /*!< in: column number */ +{ + char* bufend; + char* fieldbuf = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + char* fieldbuf2 = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + + bufend = innobase_convert_name(fieldbuf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[col_no], + strlen(foreign->foreign_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf[bufend - fieldbuf] = '\0'; + + bufend = innobase_convert_name(fieldbuf2, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[col_no], + strlen(foreign->referenced_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf2[bufend - fieldbuf2] = '\0'; + *field = fieldbuf; + *field2 = fieldbuf2; +} + /********************************************************************//** Add a single foreign key definition to the data dictionary tables in the database. We also generate names to constraints that were not named by the @@ -1501,6 +1599,29 @@ dict_create_add_foreign_to_dictionary( if (error != DB_SUCCESS) { + if (error == DB_DUPLICATE_KEY) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + + fk_def = dict_foreign_def_get(foreign, trx); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Foreign key constraint %s" + " already exists on data dictionary." + " Foreign key constraint names need to be unique in database." + " Error in foreign key definition: %s.", + tablename, buf, fk_def); + } + return(error); } @@ -1509,6 +1630,26 @@ dict_create_add_foreign_to_dictionary( i, table, foreign, trx); if (error != DB_SUCCESS) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* field=NULL; + char* field2=NULL; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + fk_def = dict_foreign_def_get(foreign, trx); + dict_foreign_def_get_fields(foreign, trx, &field, &field2, i); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Error adding foreign key constraint name %s" + " fields %s or %s to the dictionary." + " Error in foreign key definition: %s.", + tablename, buf, i+1, fk_def); return(error); } @@ -1593,7 +1734,7 @@ dict_create_add_foreigns_to_dictionary( foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) { error = dict_create_add_foreign_to_dictionary(&number, table, - foreign, trx); + foreign, trx); if (error != DB_SUCCESS) { diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index c99b568ba1d..27e79a8fa21 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -2534,8 +2534,9 @@ dict_foreign_remove_from_cache( const ib_rbt_node_t* node = rbt_lookup(rbt, foreign->id); - if (node) { - dict_foreign_t* val = *(dict_foreign_t**) node->value; + if (node != NULL) { + dict_foreign_t* val + = *(dict_foreign_t**) node->value; if (val == foreign) { rbt_delete(rbt, foreign->id); @@ -2555,9 +2556,10 @@ dict_foreign_remove_from_cache( if (rbt != NULL && foreign->id != NULL) { const ib_rbt_node_t* node = rbt_lookup(rbt, foreign->id); - if (node) { - dict_foreign_t* val = *(dict_foreign_t**) node->value; + if (node != NULL) { + dict_foreign_t* val + = *(dict_foreign_t**) node->value; if (val == foreign) { rbt_delete(rbt, foreign->id); @@ -2614,6 +2616,11 @@ dict_foreign_find( DBUG_RETURN(NULL); } +#define DB_FOREIGN_KEY_IS_PREFIX_INDEX 200 +#define DB_FOREIGN_KEY_COL_NOT_NULL 201 +#define DB_FOREIGN_KEY_COLS_NOT_EQUAL 202 +#define DB_FOREIGN_KEY_INDEX_NOT_FOUND 203 + /*********************************************************************//** Tries to find an index whose first fields are the columns in the array, in the same order and is not marked for deletion and is not the same @@ -2631,12 +2638,21 @@ dict_foreign_find_index( ibool check_charsets, /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where error happened */ + dict_index_t** err_index) + /*!< out: index where error happened */ { dict_index_t* index; + if (error) { + *error = DB_FOREIGN_KEY_INDEX_NOT_FOUND; + } + index = dict_table_get_first_index(table); while (index != NULL) { @@ -2662,6 +2678,12 @@ dict_foreign_find_index( /* We do not accept column prefix indexes here */ + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_IS_PREFIX_INDEX; + *err_col_no = i; + *err_index = index; + } + break; } @@ -2673,6 +2695,11 @@ dict_foreign_find_index( if (check_null && (field->col->prtype & DATA_NOT_NULL)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COL_NOT_NULL; + *err_col_no = i; + *err_index = index; + } return(NULL); } @@ -2682,6 +2709,12 @@ dict_foreign_find_index( i), check_charsets)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COLS_NOT_EQUAL; + *err_col_no = i; + *err_index = index; + } + break; } } @@ -2689,6 +2722,10 @@ dict_foreign_find_index( if (i == n_cols) { /* We found a matching index */ + if (error) { + *error = DB_SUCCESS; + } + return(index); } } @@ -2715,8 +2752,9 @@ wsrep_dict_foreign_find_index( /*!< in: nonzero if none of the columns must be declared NOT NULL */ { - return dict_foreign_find_index( - table, columns, n_cols, types_idx, check_charsets, check_null); + return dict_foreign_find_index(table, columns, n_cols, types_idx, + check_charsets, check_null, + NULL, NULL, NULL); } #endif /* WITH_WSREP */ /**********************************************************************//** @@ -2739,7 +2777,7 @@ dict_foreign_find_equiv_index( foreign->foreign_table, foreign->foreign_col_names, foreign->n_fields, foreign->foreign_index, TRUE, /* check types */ - FALSE/* allow columns to be NULL */)); + FALSE/* allow columns to be NULL */, NULL, NULL, NULL)); } #endif /* !UNIV_HOTBACKUP */ @@ -2902,11 +2940,15 @@ dict_foreign_add_to_cache( } if (for_in_cache->referenced_table == NULL && ref_table) { + ulint index_error; + ulint err_col; + dict_index_t *err_index=NULL; + index = dict_foreign_find_index( ref_table, for_in_cache->referenced_col_names, for_in_cache->n_fields, for_in_cache->foreign_index, - check_charsets, FALSE); + check_charsets, FALSE, &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -2938,6 +2980,9 @@ dict_foreign_add_to_cache( } if (for_in_cache->foreign_table == NULL && for_table) { + ulint index_error; + ulint err_col; + dict_index_t* err_index=NULL; index = dict_foreign_find_index( for_table, @@ -2946,7 +2991,8 @@ dict_foreign_add_to_cache( for_in_cache->referenced_index, check_charsets, for_in_cache->type & (DICT_FOREIGN_ON_DELETE_SET_NULL - | DICT_FOREIGN_ON_UPDATE_SET_NULL)); + | DICT_FOREIGN_ON_UPDATE_SET_NULL), + &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -3557,6 +3603,8 @@ static void dict_foreign_report_syntax_err( /*===========================*/ + const char* fmt, /*!< in: syntax err msg */ + const char* oper, /*!< in: operation */ const char* name, /*!< in: table name */ const char* start_of_latest_foreign, /*!< in: start of the foreign key clause @@ -3567,11 +3615,101 @@ dict_foreign_report_syntax_err( mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nSyntax error close to:\n%s\n", - start_of_latest_foreign, ptr); + fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); } +/*********************************************************************//** +Push warning message to SQL-layer based on foreign key constraint +index match error. */ +static +void +dict_foreign_push_index_error( +/*==========================*/ + trx_t* trx, /*!< in: trx */ + const char* operation, /*!< in: operation create or alter + */ + const char* create_name, /*!< in: table name in create or + alter table */ + const char* latest_foreign, /*!< in: start of latest foreign key + constraint name */ + const char** columns, /*!< in: foreign key columns */ + ulint index_error, /*!< in: error code */ + ulint err_col, /*!< in: column where error happened + */ + dict_index_t* err_index, /*!< in: index where error happened + */ + dict_table_t* table, /*!< in: table */ + FILE* ef) /*!< in: output stream */ +{ + switch (index_error) { + case DB_FOREIGN_KEY_INDEX_NOT_FOUND: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_IS_PREFIX_INDEX: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_COL_NOT_NULL: { + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s\n", + operation, create_name, columns[err_col], latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s", + operation, create_name, columns[err_col], latest_foreign); + break; + } + case DB_FOREIGN_KEY_COLS_NOT_EQUAL: { + dict_field_t* field; + const char* col_name; + field = dict_index_get_nth_field(err_index, err_col); + + col_name = dict_table_get_col_name( + table, dict_col_get_no(field->col)); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s\n", + operation, create_name, columns[err_col], col_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s", + operation, create_name, columns[err_col], col_name, latest_foreign); + break; + } + default: + ut_error; + } +} + /*********************************************************************//** 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 @@ -3600,15 +3738,20 @@ dict_create_foreign_constraints_low( DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ { - dict_table_t* table; - dict_table_t* referenced_table; - dict_table_t* table_to_alter; + dict_table_t* table = NULL; + dict_table_t* referenced_table = NULL; + dict_table_t* table_to_alter = NULL; + dict_table_t* table_to_create = NULL; ulint highest_id_so_far = 0; - dict_index_t* index; - dict_foreign_t* foreign; + dict_index_t* index = NULL; + dict_foreign_t* foreign = NULL; const char* ptr = sql_string; const char* start_of_latest_foreign = sql_string; + const char* start_of_latest_set = NULL; FILE* ef = dict_foreign_err_file; + ulint index_error = DB_SUCCESS; + dict_index_t* err_index = NULL; + ulint err_col; const char* constraint_name; ibool success; ulint error; @@ -3621,29 +3764,68 @@ dict_create_foreign_constraints_low( ulint n_on_updates; const dict_col_t*columns[500]; const char* column_names[500]; + const char* ref_column_names[500]; const char* referenced_table_name; + const char* create_table_name; + const char* orig; + char create_name[MAX_TABLE_NAME_LEN + 1]; + const char operation[8]; ut_ad(mutex_own(&(dict_sys->mutex))); table = dict_table_get_low(name, DICT_ERR_IGNORE_NONE); + /* First check if we are actually doing an ALTER TABLE, and in that + case look for the table being altered */ + ptr = dict_accept(cs, ptr, "ALTER", &success); + + strcpy((char *)operation, success ? "Alter " : "Create "); + + if (!success) { + orig = ptr; + ptr = dict_scan_to(ptr, "CREATE"); + ptr = dict_scan_to(ptr, "TABLE"); + ptr = dict_accept(cs, ptr, "TABLE", &success); + + if (success) { + ptr = dict_scan_table_name(cs, ptr, &table_to_create, name, + &success, heap, &create_table_name); + } + + if (success) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + create_table_name, strlen(create_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + ptr = orig; + } else { + char *bufend; + ptr = orig; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + name, strlen(name), trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } + + goto loop; + } if (table == NULL) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, - "Cannot find the table in the internal" - " data dictionary of InnoDB.\n" - "Create table statement:\n%s\n", sql_string); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); - + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, start_of_latest_foreign); return(DB_ERROR); } - /* First check if we are actually doing an ALTER TABLE, and in that - case look for the table being altered */ - - ptr = dict_accept(cs, ptr, "ALTER", &success); - + /* If not alter table jump to loop */ if (!success) { goto loop; @@ -3658,13 +3840,40 @@ dict_create_foreign_constraints_low( /* We are doing an ALTER TABLE: scan the table name we are altering */ + orig = ptr; ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, &success, heap, &referenced_table_name); + + if (table_to_alter) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + table_to_alter->name, strlen(table_to_alter->name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } else { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + + } + if (!success) { - fprintf(stderr, - "InnoDB: Error: could not find" - " the table being ALTERED in:\n%s\n", - sql_string); + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, orig); + mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, orig); return(DB_ERROR); } @@ -3730,7 +3939,19 @@ loop: if so, immediately reject the command if the table is a temporary one. For now, this kludge will work. */ if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.\n", + operation, create_name, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.", + operation, create_name, start_of_latest_foreign); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3766,11 +3987,21 @@ loop: if (!success) { /* MySQL allows also an index id before the '('; we skip it */ + orig = ptr; ptr = dict_skip_word(cs, ptr, &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3790,15 +4021,26 @@ loop: /* Scan the columns in the first list */ col_loop1: ut_a(i < (sizeof column_names) / sizeof *column_names); + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, table, columns + i, heap, column_names + i); if (!success) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3810,11 +4052,22 @@ col_loop1: goto col_loop1; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3822,27 +4075,41 @@ col_loop1: as the first fields and in the right order */ index = dict_foreign_find_index(table, column_names, i, - NULL, TRUE, FALSE); + NULL, TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fputs("There is no index in table ", ef); - ut_print_name(ef, NULL, TRUE, name); + ut_print_name(ef, NULL, TRUE, create_name); fprintf(ef, " where the columns appear\n" "as the first columns. Constraint:\n%s\n" "See " REFMAN "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); - mutex_exit(&dict_foreign_err_mutex); - return(DB_CHILD_NO_INDEX); + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, table, ef); + + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } + + orig = ptr; ptr = dict_accept(cs, ptr, "REFERENCES", &success); if (!success || !my_isspace(cs, *ptr)) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3891,24 +4158,50 @@ col_loop1: checking of foreign key constraints! */ if (!success || (!referenced_table && trx->check_foreigns)) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char* bufend; + + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + buf[bufend - buf] = '\0'; + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.", + operation, create_name, buf, start_of_latest_foreign); + dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve table name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.\n", + operation, create_name, buf, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3916,20 +4209,29 @@ col_loop1: i = 0; col_loop2: + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, - heap, column_names + i); + heap, ref_column_names + i); i++; if (!success) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3939,13 +4241,23 @@ col_loop2: goto col_loop2; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success || foreign->n_fields != i) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns, you have %d when you should have %d.", + operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3955,6 +4267,7 @@ col_loop2: scan_on_conditions: /* Loop here as long as we can find ON ... conditions */ + start_of_latest_set = ptr; ptr = dict_accept(cs, ptr, "ON", &success); if (!success) { @@ -3965,13 +4278,24 @@ scan_on_conditions: ptr = dict_accept(cs, ptr, "DELETE", &success); if (!success) { + orig = ptr; ptr = dict_accept(cs, ptr, "UPDATE", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4003,12 +4327,22 @@ scan_on_conditions: ptr = dict_accept(cs, ptr, "NO", &success); if (success) { + orig = ptr; ptr = dict_accept(cs, ptr, "ACTION", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4022,42 +4356,73 @@ scan_on_conditions: goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "SET", &success); if (!success) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "NULL", &success); if (!success) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } for (j = 0; j < foreign->n_fields; j++) { if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype) & DATA_NOT_NULL) { + const dict_col_t* col + = dict_index_get_nth_col(foreign->foreign_index, j); + const char* col_name = dict_table_get_col_name(foreign->foreign_index->table, + dict_col_get_no(col)); /* It is not sensible to define SET NULL if the column is not allowed to be NULL! */ - dict_foreign_free(foreign); - mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have defined a SET NULL condition" - " though some of the\n" - "columns are defined as NOT NULL.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.\n", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); + + dict_foreign_free(foreign); return(DB_CANNOT_ADD_CONSTRAINT); } } @@ -4074,16 +4439,22 @@ try_find_index: if (n_on_deletes > 1 || n_on_updates > 1) { /* It is an error to define more than 1 action */ - dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have twice an ON DELETE clause" - " or twice an ON UPDATE clause.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + dict_foreign_free(foreign); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4093,13 +4464,13 @@ try_find_index: if (referenced_table) { index = dict_foreign_find_index(referenced_table, - column_names, i, - foreign->foreign_index, - TRUE, FALSE); + ref_column_names, i, + foreign->foreign_index, + TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fprintf(ef, "%s:\n" "Cannot find an index in the" " referenced table where the\n" @@ -4117,9 +4488,13 @@ try_find_index: "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); + + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, referenced_table, ef); + mutex_exit(&dict_foreign_err_mutex); - return(DB_PARENT_NO_INDEX); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { ut_a(trx->check_foreigns == FALSE); @@ -4137,7 +4512,7 @@ try_find_index: i * sizeof(void*)); for (i = 0; i < foreign->n_fields; i++) { foreign->referenced_col_names[i] - = mem_heap_strdup(foreign->heap, column_names[i]); + = mem_heap_strdup(foreign->heap, ref_column_names[i]); } /* We found an ok constraint definition: add to the lists */ @@ -5270,7 +5645,8 @@ dict_table_replace_index_in_foreign_list( foreign->referenced_table, foreign->referenced_col_names, foreign->n_fields, index, - /*check_charsets=*/TRUE, /*check_null=*/FALSE); + /*check_charsets=*/TRUE, /*check_null=*/FALSE, + NULL, NULL, NULL); ut_ad(new_index || !trx->check_foreigns); ut_ad(!new_index || new_index->table == index->table); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index ff02c927b80..773e74e9ab0 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1654,6 +1654,7 @@ check_trx_exists( if (trx == NULL) { trx = innobase_trx_allocate(thd); + thd_set_ha_data(thd, innodb_hton_ptr, trx); } else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) { mem_analyze_corruption(trx); ut_error; @@ -13711,3 +13712,29 @@ ib_warn_row_too_big(const dict_table_t* table) " ROW_FORMAT=COMPRESSED ": "" , prefix ? DICT_MAX_FIXED_COL_LEN : 0); } + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +extern "C" UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)trx->mysql_thd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + convert_error_code_to_mysql(error, 0, thd), + buf); + my_free(buf); + va_end(args); +} diff --git a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h index 9faf580b0cc..a58bcb181aa 100644 --- a/storage/innobase/include/dict0crea.h +++ b/storage/innobase/include/dict0crea.h @@ -106,6 +106,17 @@ UNIV_INTERN ulint dict_create_or_check_foreign_constraint_tables(void); /*================================================*/ + +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx); /*!< in: trx */ + /********************************************************************//** Adds foreign key definitions to data dictionary tables in the database. We look at table->foreign_list, and also generate names to constraints that were diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index e72da36b2e0..1d8c5072a6f 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -339,5 +339,14 @@ innobase_convert_to_filename_charset( const char* from, /* in: identifier to convert */ ulint len); /* in: length of 'to', in bytes */ +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); #endif diff --git a/storage/innobase/include/row0purge.h b/storage/innobase/include/row0purge.h index fa9c9291d5d..9a638c80493 100644 --- a/storage/innobase/include/row0purge.h +++ b/storage/innobase/include/row0purge.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -111,6 +111,17 @@ struct purge_node_struct{ purge of a row */ }; +#ifdef UNIV_DEBUG +/***********************************************************//** +Validate the persisent cursor in the purge node. The purge node has two +references to the clustered index record - one via the ref member, and the +other via the persistent cursor. These two references must match each +other if the found_clust flag is set. +@return true if the persistent cursor is consistent with the ref member.*/ +ibool +row_purge_validate_pcur(purge_node_t* node); +#endif /* UNIV_DEBUG */ + #ifndef UNIV_NONINL #include "row0purge.ic" #endif diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c index 7f5a09a572e..12b4ead3c7a 100644 --- a/storage/innobase/os/os0file.c +++ b/storage/innobase/os/os0file.c @@ -1,6 +1,6 @@ /*********************************************************************** -Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. Portions of this file contain modifications contributed and copyrighted @@ -1287,18 +1287,21 @@ os_file_create_simple_no_error_handling_func( #else /* __WIN__ */ os_file_t file; int create_flag; + const char* mode_str = NULL; ut_a(name); if (create_mode != OS_FILE_OPEN && create_mode != OS_FILE_OPEN_RAW) WAIT_ALLOW_WRITES(); if (create_mode == OS_FILE_OPEN) { + mode_str = "OPEN"; if (access_type == OS_FILE_READ_ONLY) { create_flag = O_RDONLY; } else { create_flag = O_RDWR; } } else if (create_mode == OS_FILE_CREATE) { + mode_str = "CREATE"; create_flag = O_RDWR | O_CREAT | O_EXCL; } else { create_flag = 0; @@ -1323,6 +1326,14 @@ os_file_create_simple_no_error_handling_func( #endif } else { *success = TRUE; + + /* This function is always called for data files, we should + disable OS caching (O_DIRECT) here as we do in + os_file_create_func(), so we open the same file in the same + mode, see man page of open(2). */ + if (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT) { + os_file_set_nocache(file, name, mode_str); + } } return(file); diff --git a/storage/innobase/row/row0purge.c b/storage/innobase/row/row0purge.c index efcfdc3bac5..9018582f5d6 100644 --- a/storage/innobase/row/row0purge.c +++ b/storage/innobase/row/row0purge.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1997, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -43,6 +43,7 @@ Created 3/14/1997 Heikki Tuuri #include "row0vers.h" #include "row0mysql.h" #include "log0log.h" +#include "rem0cmp.h" /************************************************************************* IMPORTANT NOTE: Any operation that generates redo MUST check that there @@ -80,7 +81,7 @@ row_purge_node_create( /***********************************************************//** Repositions the pcur in the purge node on the clustered index record, -if found. +if found. If the record is not found, close pcur. @return TRUE if the record was found */ static ibool @@ -90,23 +91,28 @@ row_purge_reposition_pcur( purge_node_t* node, /*!< in: row purge node */ mtr_t* mtr) /*!< in: mtr */ { - ibool found; - if (node->found_clust) { - found = btr_pcur_restore_position(mode, &(node->pcur), mtr); + ut_ad(row_purge_validate_pcur(node)); - return(found); + node->found_clust = btr_pcur_restore_position( + mode, &(node->pcur), mtr); + + } else { + + node->found_clust = row_search_on_row_ref( + &(node->pcur), mode, node->table, node->ref, mtr); + + if (node->found_clust) { + btr_pcur_store_position(&(node->pcur), mtr); + } } - found = row_search_on_row_ref(&(node->pcur), mode, node->table, - node->ref, mtr); - node->found_clust = found; - - if (found) { - btr_pcur_store_position(&(node->pcur), mtr); + /* Close the current cursor if we fail to position it correctly. */ + if (!node->found_clust) { + btr_pcur_close(&node->pcur); } - return(found); + return(node->found_clust); } /***********************************************************//** @@ -143,8 +149,8 @@ row_purge_remove_clust_if_poss_low( if (!success) { /* The record is already removed */ - - btr_pcur_commit_specify_mtr(pcur, &mtr); + /* Persistent cursor is closed if reposition fails. */ + mtr_commit(&mtr); return(TRUE); } @@ -258,7 +264,12 @@ row_purge_poss_sec( btr_pcur_get_rec(&node->pcur), &mtr, index, entry); - btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + /* Persistent cursor is closed if reposition fails. */ + if (node->found_clust) { + btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + } else { + mtr_commit(&mtr); + } return(can_delete); } @@ -806,3 +817,53 @@ row_purge_step( return(thr); } + +#ifdef UNIV_DEBUG +/***********************************************************//** +Validate the persisent cursor in the purge node. The purge node has two +references to the clustered index record - one via the ref member, and the +other via the persistent cursor. These two references must match each +other if the found_clust flag is set. +@return true if the stored copy of persistent cursor is consistent +with the ref member.*/ +ibool +row_purge_validate_pcur( + purge_node_t* node) +{ + dict_index_t* clust_index; + ulint* offsets; + int st; + + if (!node->found_clust) { + return(TRUE); + } + + if (node->index == NULL) { + return(TRUE); + } + + if (node->pcur.old_stored != BTR_PCUR_OLD_STORED) { + return(TRUE); + } + + clust_index = node->pcur.btr_cur.index; + + offsets = rec_get_offsets(node->pcur.old_rec, clust_index, NULL, + node->pcur.old_n_fields, &node->heap); + + /* Here we are comparing the purge ref record and the stored initial + part in persistent cursor. Both cases we store n_uniq fields of the + cluster index and so it is fine to do the comparison. We note this + dependency here as pcur and ref belong to different modules. */ + st = cmp_dtuple_rec(node->ref, node->pcur.old_rec, offsets); + + if (st != 0) { + fprintf(stderr, "Purge node pcur validation failed\n"); + dtuple_print(stderr, node->ref); + rec_print(stderr, node->pcur.old_rec, clust_index); + return(FALSE); + } + + return(TRUE); +} +#endif /* UNIV_DEBUG */ diff --git a/util/tests/sm-basic.cc b/storage/tokudb/ft-index/util/tests/sm-basic.cc similarity index 100% rename from util/tests/sm-basic.cc rename to storage/tokudb/ft-index/util/tests/sm-basic.cc diff --git a/util/tests/sm-crash-double-free.cc b/storage/tokudb/ft-index/util/tests/sm-crash-double-free.cc similarity index 100% rename from util/tests/sm-crash-double-free.cc rename to storage/tokudb/ft-index/util/tests/sm-crash-double-free.cc diff --git a/mysql-test/suite/tokudb.bugs/r/db805.result b/storage/tokudb/mysql-test/tokudb_bugs/r/db805.result similarity index 100% rename from mysql-test/suite/tokudb.bugs/r/db805.result rename to storage/tokudb/mysql-test/tokudb_bugs/r/db805.result diff --git a/mysql-test/suite/tokudb.bugs/r/db806.result b/storage/tokudb/mysql-test/tokudb_bugs/r/db806.result similarity index 100% rename from mysql-test/suite/tokudb.bugs/r/db806.result rename to storage/tokudb/mysql-test/tokudb_bugs/r/db806.result diff --git a/mysql-test/suite/tokudb.bugs/r/db811.result b/storage/tokudb/mysql-test/tokudb_bugs/r/db811.result similarity index 100% rename from mysql-test/suite/tokudb.bugs/r/db811.result rename to storage/tokudb/mysql-test/tokudb_bugs/r/db811.result diff --git a/mysql-test/suite/tokudb.bugs/r/db811s.result b/storage/tokudb/mysql-test/tokudb_bugs/r/db811s.result similarity index 100% rename from mysql-test/suite/tokudb.bugs/r/db811s.result rename to storage/tokudb/mysql-test/tokudb_bugs/r/db811s.result diff --git a/mysql-test/suite/tokudb.bugs/r/db823.result b/storage/tokudb/mysql-test/tokudb_bugs/r/db823.result similarity index 100% rename from mysql-test/suite/tokudb.bugs/r/db823.result rename to storage/tokudb/mysql-test/tokudb_bugs/r/db823.result diff --git a/mysql-test/suite/tokudb.bugs/t/db805.test b/storage/tokudb/mysql-test/tokudb_bugs/t/db805.test similarity index 100% rename from mysql-test/suite/tokudb.bugs/t/db805.test rename to storage/tokudb/mysql-test/tokudb_bugs/t/db805.test diff --git a/mysql-test/suite/tokudb.bugs/t/db806.test b/storage/tokudb/mysql-test/tokudb_bugs/t/db806.test similarity index 100% rename from mysql-test/suite/tokudb.bugs/t/db806.test rename to storage/tokudb/mysql-test/tokudb_bugs/t/db806.test diff --git a/mysql-test/suite/tokudb.bugs/t/db811.test b/storage/tokudb/mysql-test/tokudb_bugs/t/db811.test similarity index 100% rename from mysql-test/suite/tokudb.bugs/t/db811.test rename to storage/tokudb/mysql-test/tokudb_bugs/t/db811.test diff --git a/mysql-test/suite/tokudb.bugs/t/db811s.test b/storage/tokudb/mysql-test/tokudb_bugs/t/db811s.test similarity index 100% rename from mysql-test/suite/tokudb.bugs/t/db811s.test rename to storage/tokudb/mysql-test/tokudb_bugs/t/db811s.test diff --git a/mysql-test/suite/tokudb.bugs/t/db823.test b/storage/tokudb/mysql-test/tokudb_bugs/t/db823.test similarity index 100% rename from mysql-test/suite/tokudb.bugs/t/db823.test rename to storage/tokudb/mysql-test/tokudb_bugs/t/db823.test diff --git a/storage/xtradb/buf/buf0buf.c b/storage/xtradb/buf/buf0buf.c index 44e93ae2122..890a04fe892 100644 --- a/storage/xtradb/buf/buf0buf.c +++ b/storage/xtradb/buf/buf0buf.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -1666,6 +1666,10 @@ buf_pool_watch_set( /* buf_pool->watch is protected by zip_mutex for now */ mutex_enter(&buf_pool->zip_mutex); + + /* The maximum number of purge threads should never exceed + BUF_POOL_WATCH_SIZE. So there is no way for purge thread + instance to hold a watch when setting another watch. */ for (i = 0; i < BUF_POOL_WATCH_SIZE; i++) { bpage = &buf_pool->watch[i]; diff --git a/storage/xtradb/dict/dict0crea.c b/storage/xtradb/dict/dict0crea.c index c88979b20f9..f7663b999b7 100644 --- a/storage/xtradb/dict/dict0crea.c +++ b/storage/xtradb/dict/dict0crea.c @@ -1626,6 +1626,104 @@ dict_create_add_foreign_field_to_dictionary( table, foreign, trx)); } +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx) /*!< in: trx */ +{ + char* fk_def = mem_heap_alloc(foreign->heap, 4*1024); + const char* tbname; + char tablebuf[MAX_TABLE_NAME_LEN + 1] = ""; + int i; + char* bufend; + + tbname = dict_remove_db_name(foreign->id); + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + tbname, strlen(tbname), trx->mysql_thd, FALSE); + tablebuf[bufend - tablebuf] = '\0'; + + sprintf(fk_def, + (char *)"CONSTRAINT %s FOREIGN KEY (", (char *)tablebuf); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[i], + strlen(foreign->foreign_col_names[i]), + trx->mysql_thd, FALSE); + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + + strcat(fk_def,(char *)") REFERENCES "); + + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + foreign->referenced_table_name, + strlen(foreign->referenced_table_name), + trx->mysql_thd, TRUE); + tablebuf[bufend - tablebuf] = '\0'; + + strcat(fk_def, tablebuf); + strcat(fk_def, " ("); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[i], + strlen(foreign->referenced_col_names[i]), + trx->mysql_thd, FALSE); + buf[bufend - buf] = '\0'; + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + strcat(fk_def, (char *)")"); + + return fk_def; +} + +/********************************************************************//** +Convert foreign key column names from data dictionary to SQL-layer. +*/ +static +void +dict_foreign_def_get_fields( +/*========================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx, /*!< in: trx */ + char** field, /*!< out: foreign column */ + char** field2, /*!< out: referenced column */ + int col_no) /*!< in: column number */ +{ + char* bufend; + char* fieldbuf = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + char* fieldbuf2 = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + + bufend = innobase_convert_name(fieldbuf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[col_no], + strlen(foreign->foreign_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf[bufend - fieldbuf] = '\0'; + + bufend = innobase_convert_name(fieldbuf2, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[col_no], + strlen(foreign->referenced_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf2[bufend - fieldbuf2] = '\0'; + *field = fieldbuf; + *field2 = fieldbuf2; +} + /********************************************************************//** Add a single foreign key definition to the data dictionary tables in the database. We also generate names to constraints that were not named by the @@ -1708,6 +1806,29 @@ dict_create_add_foreign_to_dictionary( if (error != DB_SUCCESS) { + if (error == DB_DUPLICATE_KEY) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + + fk_def = dict_foreign_def_get(foreign, trx); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Foreign key constraint %s" + " already exists on data dictionary." + " Foreign key constraint names need to be unique in database." + " Error in foreign key definition: %s.", + tablename, buf, fk_def); + } + return(error); } @@ -1716,6 +1837,26 @@ dict_create_add_foreign_to_dictionary( i, table, foreign, trx); if (error != DB_SUCCESS) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* field=NULL; + char* field2=NULL; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + fk_def = dict_foreign_def_get(foreign, trx); + dict_foreign_def_get_fields(foreign, trx, &field, &field2, i); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Error adding foreign key constraint name %s" + " fields %s or %s to the dictionary." + " Error in foreign key definition: %s.", + tablename, buf, i+1, fk_def); return(error); } @@ -1800,7 +1941,7 @@ dict_create_add_foreigns_to_dictionary( foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) { error = dict_create_add_foreign_to_dictionary(&number, table, - foreign, trx); + foreign, trx); if (error != DB_SUCCESS) { diff --git a/storage/xtradb/dict/dict0dict.c b/storage/xtradb/dict/dict0dict.c index a67ca850c3a..7f6a8180b22 100644 --- a/storage/xtradb/dict/dict0dict.c +++ b/storage/xtradb/dict/dict0dict.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -2663,6 +2663,7 @@ dict_foreign_remove_from_cache( foreign); rbt = foreign->referenced_table->referenced_rbt; + if (rbt != NULL && foreign->id != NULL) { const ib_rbt_node_t* node = rbt_lookup(rbt, foreign->id); @@ -2747,6 +2748,11 @@ dict_foreign_find( DBUG_RETURN(NULL); } +#define DB_FOREIGN_KEY_IS_PREFIX_INDEX 200 +#define DB_FOREIGN_KEY_COL_NOT_NULL 201 +#define DB_FOREIGN_KEY_COLS_NOT_EQUAL 202 +#define DB_FOREIGN_KEY_INDEX_NOT_FOUND 203 + /*********************************************************************//** Tries to find an index whose first fields are the columns in the array, in the same order and is not marked for deletion and is not the same @@ -2764,12 +2770,21 @@ dict_foreign_find_index( ibool check_charsets, /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where error happened */ + dict_index_t** err_index) + /*!< out: index where error happened */ { dict_index_t* index; + if (error) { + *error = DB_FOREIGN_KEY_INDEX_NOT_FOUND; + } + index = dict_table_get_first_index(table); while (index != NULL) { @@ -2795,6 +2810,12 @@ dict_foreign_find_index( /* We do not accept column prefix indexes here */ + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_IS_PREFIX_INDEX; + *err_col_no = i; + *err_index = index; + } + break; } @@ -2806,6 +2827,11 @@ dict_foreign_find_index( if (check_null && (field->col->prtype & DATA_NOT_NULL)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COL_NOT_NULL; + *err_col_no = i; + *err_index = index; + } return(NULL); } @@ -2815,6 +2841,12 @@ dict_foreign_find_index( i), check_charsets)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COLS_NOT_EQUAL; + *err_col_no = i; + *err_index = index; + } + break; } } @@ -2822,6 +2854,10 @@ dict_foreign_find_index( if (i == n_cols) { /* We found a matching index */ + if (error) { + *error = DB_SUCCESS; + } + return(index); } } @@ -2848,8 +2884,9 @@ wsrep_dict_foreign_find_index( /*!< in: nonzero if none of the columns must be declared NOT NULL */ { - return dict_foreign_find_index( - table, columns, n_cols, types_idx, check_charsets, check_null); + return dict_foreign_find_index(table, columns, n_cols, types_idx, + check_charsets, check_null, + NULL, NULL, NULL); } #endif /* WITH_WSREP */ /**********************************************************************//** @@ -2872,7 +2909,7 @@ dict_foreign_find_equiv_index( foreign->foreign_table, foreign->foreign_col_names, foreign->n_fields, foreign->foreign_index, TRUE, /* check types */ - FALSE/* allow columns to be NULL */)); + FALSE/* allow columns to be NULL */, NULL, NULL, NULL)); } #endif /* !UNIV_HOTBACKUP */ @@ -3035,11 +3072,15 @@ dict_foreign_add_to_cache( } if (for_in_cache->referenced_table == NULL && ref_table) { + ulint index_error; + ulint err_col; + dict_index_t *err_index=NULL; + index = dict_foreign_find_index( ref_table, for_in_cache->referenced_col_names, for_in_cache->n_fields, for_in_cache->foreign_index, - check_charsets, FALSE); + check_charsets, FALSE, &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -3071,6 +3112,9 @@ dict_foreign_add_to_cache( } if (for_in_cache->foreign_table == NULL && for_table) { + ulint index_error; + ulint err_col; + dict_index_t* err_index=NULL; index = dict_foreign_find_index( for_table, @@ -3079,7 +3123,8 @@ dict_foreign_add_to_cache( for_in_cache->referenced_index, check_charsets, for_in_cache->type & (DICT_FOREIGN_ON_DELETE_SET_NULL - | DICT_FOREIGN_ON_UPDATE_SET_NULL)); + | DICT_FOREIGN_ON_UPDATE_SET_NULL), + &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -3690,6 +3735,8 @@ static void dict_foreign_report_syntax_err( /*===========================*/ + const char* fmt, /*!< in: syntax err msg */ + const char* oper, /*!< in: operation */ const char* name, /*!< in: table name */ const char* start_of_latest_foreign, /*!< in: start of the foreign key clause @@ -3700,11 +3747,101 @@ dict_foreign_report_syntax_err( mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nSyntax error close to:\n%s\n", - start_of_latest_foreign, ptr); + fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); } +/*********************************************************************//** +Push warning message to SQL-layer based on foreign key constraint +index match error. */ +static +void +dict_foreign_push_index_error( +/*==========================*/ + trx_t* trx, /*!< in: trx */ + const char* operation, /*!< in: operation create or alter + */ + const char* create_name, /*!< in: table name in create or + alter table */ + const char* latest_foreign, /*!< in: start of latest foreign key + constraint name */ + const char** columns, /*!< in: foreign key columns */ + ulint index_error, /*!< in: error code */ + ulint err_col, /*!< in: column where error happened + */ + dict_index_t* err_index, /*!< in: index where error happened + */ + dict_table_t* table, /*!< in: table */ + FILE* ef) /*!< in: output stream */ +{ + switch (index_error) { + case DB_FOREIGN_KEY_INDEX_NOT_FOUND: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_IS_PREFIX_INDEX: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_COL_NOT_NULL: { + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s\n", + operation, create_name, columns[err_col], latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s", + operation, create_name, columns[err_col], latest_foreign); + break; + } + case DB_FOREIGN_KEY_COLS_NOT_EQUAL: { + dict_field_t* field; + const char* col_name; + field = dict_index_get_nth_field(err_index, err_col); + + col_name = dict_table_get_col_name( + table, dict_col_get_no(field->col)); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s\n", + operation, create_name, columns[err_col], col_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s", + operation, create_name, columns[err_col], col_name, latest_foreign); + break; + } + default: + ut_error; + } +} + /*********************************************************************//** 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 @@ -3733,15 +3870,20 @@ dict_create_foreign_constraints_low( DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ { - dict_table_t* table; - dict_table_t* referenced_table; - dict_table_t* table_to_alter; + dict_table_t* table = NULL; + dict_table_t* referenced_table = NULL; + dict_table_t* table_to_alter = NULL; + dict_table_t* table_to_create = NULL; ulint highest_id_so_far = 0; - dict_index_t* index; - dict_foreign_t* foreign; + dict_index_t* index = NULL; + dict_foreign_t* foreign = NULL; const char* ptr = sql_string; const char* start_of_latest_foreign = sql_string; + const char* start_of_latest_set = NULL; FILE* ef = dict_foreign_err_file; + ulint index_error = DB_SUCCESS; + dict_index_t* err_index = NULL; + ulint err_col; const char* constraint_name; ibool success; ulint error; @@ -3754,29 +3896,68 @@ dict_create_foreign_constraints_low( ulint n_on_updates; const dict_col_t*columns[500]; const char* column_names[500]; + const char* ref_column_names[500]; const char* referenced_table_name; + const char* create_table_name; + const char* orig; + char create_name[MAX_TABLE_NAME_LEN + 1]; + const char operation[8]; ut_ad(mutex_own(&(dict_sys->mutex))); table = dict_table_get_low(name, DICT_ERR_IGNORE_NONE); + /* First check if we are actually doing an ALTER TABLE, and in that + case look for the table being altered */ + ptr = dict_accept(cs, ptr, "ALTER", &success); + + strcpy((char *)operation, success ? "Alter " : "Create "); + + if (!success) { + orig = ptr; + ptr = dict_scan_to(ptr, "CREATE"); + ptr = dict_scan_to(ptr, "TABLE"); + ptr = dict_accept(cs, ptr, "TABLE", &success); + + if (success) { + ptr = dict_scan_table_name(cs, ptr, &table_to_create, name, + &success, heap, &create_table_name); + } + + if (success) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + create_table_name, strlen(create_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + ptr = orig; + } else { + char *bufend; + ptr = orig; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + name, strlen(name), trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } + + goto loop; + } if (table == NULL) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, - "Cannot find the table in the internal" - " data dictionary of InnoDB.\n" - "Create table statement:\n%s\n", sql_string); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); - + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, start_of_latest_foreign); return(DB_ERROR); } - /* First check if we are actually doing an ALTER TABLE, and in that - case look for the table being altered */ - - ptr = dict_accept(cs, ptr, "ALTER", &success); - + /* If not alter table jump to loop */ if (!success) { goto loop; @@ -3791,13 +3972,40 @@ dict_create_foreign_constraints_low( /* We are doing an ALTER TABLE: scan the table name we are altering */ + orig = ptr; ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, &success, heap, &referenced_table_name); + + if (table_to_alter) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + table_to_alter->name, strlen(table_to_alter->name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } else { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + + } + if (!success) { - fprintf(stderr, - "InnoDB: Error: could not find" - " the table being ALTERED in:\n%s\n", - sql_string); + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, orig); + mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, orig); return(DB_ERROR); } @@ -3863,7 +4071,19 @@ loop: if so, immediately reject the command if the table is a temporary one. For now, this kludge will work. */ if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.\n", + operation, create_name, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.", + operation, create_name, start_of_latest_foreign); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3899,11 +4119,21 @@ loop: if (!success) { /* MySQL allows also an index id before the '('; we skip it */ + orig = ptr; ptr = dict_skip_word(cs, ptr, &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3923,15 +4153,26 @@ loop: /* Scan the columns in the first list */ col_loop1: ut_a(i < (sizeof column_names) / sizeof *column_names); + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, table, columns + i, heap, column_names + i); if (!success) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3943,11 +4184,22 @@ col_loop1: goto col_loop1; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -3955,27 +4207,41 @@ col_loop1: as the first fields and in the right order */ index = dict_foreign_find_index(table, column_names, i, - NULL, TRUE, FALSE); + NULL, TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fputs("There is no index in table ", ef); - ut_print_name(ef, NULL, TRUE, name); + ut_print_name(ef, NULL, TRUE, create_name); fprintf(ef, " where the columns appear\n" "as the first columns. Constraint:\n%s\n" "See " REFMAN "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); - mutex_exit(&dict_foreign_err_mutex); - return(DB_CHILD_NO_INDEX); + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, table, ef); + + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } + + orig = ptr; ptr = dict_accept(cs, ptr, "REFERENCES", &success); if (!success || !my_isspace(cs, *ptr)) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4024,24 +4290,50 @@ col_loop1: checking of foreign key constraints! */ if (!success || (!referenced_table && trx->check_foreigns)) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char* bufend; + + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + buf[bufend - buf] = '\0'; + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.", + operation, create_name, buf, start_of_latest_foreign); + dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve table name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.\n", + operation, create_name, buf, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4049,20 +4341,29 @@ col_loop1: i = 0; col_loop2: + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, - heap, column_names + i); + heap, ref_column_names + i); i++; if (!success) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4072,13 +4373,23 @@ col_loop2: goto col_loop2; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success || foreign->n_fields != i) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns, you have %d when you should have %d.", + operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4088,6 +4399,7 @@ col_loop2: scan_on_conditions: /* Loop here as long as we can find ON ... conditions */ + start_of_latest_set = ptr; ptr = dict_accept(cs, ptr, "ON", &success); if (!success) { @@ -4098,13 +4410,24 @@ scan_on_conditions: ptr = dict_accept(cs, ptr, "DELETE", &success); if (!success) { + orig = ptr; ptr = dict_accept(cs, ptr, "UPDATE", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4136,12 +4459,22 @@ scan_on_conditions: ptr = dict_accept(cs, ptr, "NO", &success); if (success) { + orig = ptr; ptr = dict_accept(cs, ptr, "ACTION", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4155,42 +4488,73 @@ scan_on_conditions: goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "SET", &success); if (!success) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "NULL", &success); if (!success) { dict_foreign_free(foreign); - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } for (j = 0; j < foreign->n_fields; j++) { if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype) & DATA_NOT_NULL) { + const dict_col_t* col + = dict_index_get_nth_col(foreign->foreign_index, j); + const char* col_name = dict_table_get_col_name(foreign->foreign_index->table, + dict_col_get_no(col)); /* It is not sensible to define SET NULL if the column is not allowed to be NULL! */ - dict_foreign_free(foreign); - mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have defined a SET NULL condition" - " though some of the\n" - "columns are defined as NOT NULL.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.\n", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); + + dict_foreign_free(foreign); return(DB_CANNOT_ADD_CONSTRAINT); } } @@ -4207,16 +4571,22 @@ try_find_index: if (n_on_deletes > 1 || n_on_updates > 1) { /* It is an error to define more than 1 action */ - dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have twice an ON DELETE clause" - " or twice an ON UPDATE clause.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + dict_foreign_free(foreign); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4226,13 +4596,13 @@ try_find_index: if (referenced_table) { index = dict_foreign_find_index(referenced_table, - column_names, i, - foreign->foreign_index, - TRUE, FALSE); + ref_column_names, i, + foreign->foreign_index, + TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fprintf(ef, "%s:\n" "Cannot find an index in the" " referenced table where the\n" @@ -4250,9 +4620,13 @@ try_find_index: "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); + + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, referenced_table, ef); + mutex_exit(&dict_foreign_err_mutex); - return(DB_PARENT_NO_INDEX); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { ut_a(trx->check_foreigns == FALSE); @@ -4270,7 +4644,7 @@ try_find_index: i * sizeof(void*)); for (i = 0; i < foreign->n_fields; i++) { foreign->referenced_col_names[i] - = mem_heap_strdup(foreign->heap, column_names[i]); + = mem_heap_strdup(foreign->heap, ref_column_names[i]); } /* We found an ok constraint definition: add to the lists */ @@ -5815,7 +6189,8 @@ dict_table_replace_index_in_foreign_list( foreign->referenced_table, foreign->referenced_col_names, foreign->n_fields, index, - /*check_charsets=*/TRUE, /*check_null=*/FALSE); + /*check_charsets=*/TRUE, /*check_null=*/FALSE, + NULL, NULL, NULL); ut_ad(new_index || !trx->check_foreigns); ut_ad(!new_index || new_index->table == index->table); diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index e400ac60620..49c9790c435 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2000, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2000, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. @@ -1910,6 +1910,7 @@ check_trx_exists( if (trx == NULL) { trx = innobase_trx_allocate(thd); + thd_set_ha_data(thd, innodb_hton_ptr, trx); } else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) { mem_analyze_corruption(trx); ut_error; @@ -10169,6 +10170,13 @@ ha_innobase::estimate_rows_upper_bound(void) estimate = 2 * local_data_file_length / dict_index_calc_min_rec_len(index); + /* Set num_rows less than MERGEBUFF to simulate the case where we do + not have enough space to merge the externally sorted file blocks. */ + DBUG_EXECUTE_IF("set_num_rows_lt_MERGEBUFF", + estimate = 2; + DBUG_SET("-d,set_num_rows_lt_MERGEBUFF"); + ); + prebuilt->trx->op_info = (char*)""; DBUG_RETURN((ha_rows) estimate); @@ -10448,17 +10456,6 @@ ha_innobase::info_low( prebuilt->trx->op_info = "returning various info to MySQL"; } - my_snprintf(path, sizeof(path), "%s/%s%s", - mysql_data_home, ib_table->name, reg_ext); - - unpack_filename(path,path); - - /* Note that we do not know the access time of the table, - nor the CHECK TABLE time, nor the UPDATE or INSERT time. */ - - if (os_file_get_status(path,&stat_info)) { - stats.create_time = (ulong) stat_info.ctime; - } } if (flag & HA_STATUS_VARIABLE) { @@ -10702,6 +10699,20 @@ ha_innobase::info_low( } dict_table_stats_unlock(ib_table, RW_S_LATCH); + + my_snprintf(path, sizeof(path), "%s/%s%s", + mysql_data_home, + table->s->normalized_path.str, + reg_ext); + + unpack_filename(path,path); + + /* Note that we do not know the access time of the table, + nor the CHECK TABLE time, nor the UPDATE or INSERT time. */ + + if (os_file_get_status(path,&stat_info)) { + stats.create_time = (ulong) stat_info.ctime; + } } if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { @@ -15514,3 +15525,28 @@ ha_innobase::idx_cond_push( DBUG_RETURN(NULL); } +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +extern "C" UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)trx->mysql_thd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + convert_error_code_to_mysql(error, 0, thd), + buf); + my_free(buf); + va_end(args); +} diff --git a/storage/xtradb/include/dict0crea.h b/storage/xtradb/include/dict0crea.h index 762ab54a353..c67910f0d55 100644 --- a/storage/xtradb/include/dict0crea.h +++ b/storage/xtradb/include/dict0crea.h @@ -121,6 +121,17 @@ UNIV_INTERN ulint dict_create_or_check_foreign_constraint_tables(void); /*================================================*/ + +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx); /*!< in: trx */ + /********************************************************************//** Adds foreign key definitions to data dictionary tables in the database. We look at table->foreign_list, and also generate names to constraints that were diff --git a/storage/xtradb/include/ha_prototypes.h b/storage/xtradb/include/ha_prototypes.h index 60d792fc5f1..00d3892d663 100644 --- a/storage/xtradb/include/ha_prototypes.h +++ b/storage/xtradb/include/ha_prototypes.h @@ -366,5 +366,14 @@ innobase_convert_to_filename_charset( const char* from, /* in: identifier to convert */ ulint len); /* in: length of 'to', in bytes */ +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); #endif diff --git a/storage/xtradb/include/univ.i b/storage/xtradb/include/univ.i index c31a0f20862..701d3c14f1b 100644 --- a/storage/xtradb/include/univ.i +++ b/storage/xtradb/include/univ.i @@ -64,10 +64,10 @@ component, i.e. we show M.N.P as M.N */ (INNODB_VERSION_MAJOR << 8 | INNODB_VERSION_MINOR) #ifndef PERCONA_INNODB_VERSION -#define PERCONA_INNODB_VERSION 37.2 +#define PERCONA_INNODB_VERSION 37.3 #endif -#define INNODB_VERSION_STR "5.5.43-MariaDB-" IB_TO_STR(PERCONA_INNODB_VERSION) +#define INNODB_VERSION_STR "5.5.44-MariaDB-" IB_TO_STR(PERCONA_INNODB_VERSION) #define REFMAN "http://dev.mysql.com/doc/refman/" \ IB_TO_STR(MYSQL_MAJOR_VERSION) "." \ diff --git a/storage/xtradb/srv/srv0start.c b/storage/xtradb/srv/srv0start.c index 4318da10619..e946472e6ee 100644 --- a/storage/xtradb/srv/srv0start.c +++ b/storage/xtradb/srv/srv0start.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2009, Percona Inc. @@ -2511,9 +2511,9 @@ innobase_shutdown_for_mysql(void) ibuf_close(); log_shutdown(); - lock_sys_close(); trx_sys_file_format_close(); trx_sys_close(); + lock_sys_close(); mutex_free(&srv_monitor_file_mutex); mutex_free(&srv_dict_tmpfile_mutex); diff --git a/storage/xtradb/sync/sync0arr.c b/storage/xtradb/sync/sync0arr.c index 73cd6ea1739..3bf53696148 100644 --- a/storage/xtradb/sync/sync0arr.c +++ b/storage/xtradb/sync/sync0arr.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2013, 2014, MariaDB Corporation. All Rights Reserved. @@ -1114,8 +1114,9 @@ sync_array_output_info( os_thread_id_t r; fprintf(file, - "OS WAIT ARRAY INFO: reservation count %ld, signal count %ld\n", - (long) arr->res_count, (long) arr->sg_count); + "OS WAIT ARRAY INFO: reservation count " ULINTPF + ", signal count " ULINTPF "\n", + arr->res_count, arr->sg_count); i = 0; count = 0; diff --git a/storage/xtradb/trx/trx0trx.c b/storage/xtradb/trx/trx0trx.c index 27792e57e9c..d2417da55ec 100644 --- a/storage/xtradb/trx/trx0trx.c +++ b/storage/xtradb/trx/trx0trx.c @@ -771,9 +771,9 @@ trx_lists_init_at_db_start(void) " anyway.\n"); trx->state = TRX_ACTIVE; - trx_reserve_descriptor( - trx); } + + trx_reserve_descriptor(trx); } else { trx->state = TRX_COMMITTED_IN_MEMORY; diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index 78916f843e0..c889b90a2e2 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -21,31 +21,42 @@ static my_bool ssl_algorithms_added = FALSE; static my_bool ssl_error_strings_loaded= FALSE; -/* the function below was generated with "openssl dhparam -2 -C 1024" */ +/* the function below was generated with "openssl dhparam -2 -C 2048" */ static -DH *get_dh1024() +DH *get_dh2048() { - static unsigned char dh1024_p[]={ - 0xEC,0x46,0x7E,0xF9,0x4E,0x10,0x29,0xDC,0x44,0x97,0x71,0xFD, - 0x71,0xC6,0x9F,0x0D,0xD1,0x09,0xF6,0x58,0x6F,0xAD,0xCA,0xF4, - 0x37,0xD5,0xC3,0xBD,0xC3,0x9A,0x51,0x66,0x2C,0x58,0xBD,0x02, - 0xBD,0xBA,0xBA,0xFC,0xE7,0x0E,0x5A,0xE5,0x97,0x81,0xC3,0xF3, - 0x28,0x2D,0xAD,0x00,0x91,0xEF,0xF8,0xF0,0x5D,0xE9,0xE7,0x18, - 0xE2,0xAD,0xC4,0x70,0xC5,0x3C,0x12,0x8A,0x80,0x6A,0x9F,0x3B, - 0x00,0xA2,0x8F,0xA9,0x26,0xB0,0x0E,0x7F,0xED,0xF6,0xC2,0x03, - 0x81,0xB5,0xC5,0x41,0xD0,0x00,0x2B,0x21,0xD4,0x4B,0x74,0xA6, - 0xD7,0x1A,0x0E,0x82,0xC8,0xEE,0xD4,0xB1,0x6F,0xB4,0x79,0x01, - 0x8A,0xF1,0x12,0xD7,0x3C,0xFD,0xCB,0x9B,0xAE,0x1C,0xA9,0x0F, - 0x3D,0x0F,0xF8,0xD6,0x7D,0xDE,0xD6,0x0B, + static unsigned char dh2048_p[]={ + 0xA1,0xBB,0x7C,0x20,0xC5,0x5B,0xC0,0x7B,0x21,0x8B,0xD6,0xA8, + 0x15,0xFC,0x3B,0xBA,0xAB,0x9F,0xDF,0x68,0xC4,0x79,0x78,0x0D, + 0xC1,0x12,0x64,0xE4,0x15,0xC9,0x66,0xDB,0xF6,0xCB,0xB3,0x39, + 0x02,0x5B,0x78,0x62,0xFB,0x09,0xAE,0x09,0x6B,0xDD,0xD4,0x5D, + 0x97,0xBC,0xDC,0x7F,0xE6,0xD6,0xF1,0xCB,0xF5,0xEB,0xDA,0xA7, + 0x2E,0x5A,0x43,0x2B,0xE9,0x40,0xE2,0x85,0x00,0x1C,0xC0,0x0A, + 0x98,0x77,0xA9,0x31,0xDE,0x0B,0x75,0x4D,0x1E,0x1F,0x16,0x83, + 0xCA,0xDE,0xBD,0x21,0xFC,0xC1,0x82,0x37,0x36,0x33,0x0B,0x66, + 0x06,0x3C,0xF3,0xAF,0x21,0x57,0x57,0x80,0xF6,0x94,0x1B,0xA9, + 0xD4,0xF6,0x8F,0x18,0x62,0x0E,0xC4,0x22,0xF9,0x5B,0x62,0xCC, + 0x3F,0x19,0x95,0xCF,0x4B,0x00,0xA6,0x6C,0x0B,0xAF,0x9F,0xD5, + 0xFA,0x3D,0x6D,0xDA,0x30,0x83,0x07,0x91,0xAC,0x15,0xFF,0x8F, + 0x59,0x54,0xEA,0x25,0xBC,0x4E,0xEB,0x6A,0x54,0xDF,0x75,0x09, + 0x72,0x0F,0xEF,0x23,0x70,0xE0,0xA8,0x04,0xEA,0xFF,0x90,0x54, + 0xCD,0x84,0x18,0xC0,0x75,0x91,0x99,0x0F,0xA1,0x78,0x0C,0x07, + 0xB7,0xC5,0xDE,0x55,0x06,0x7B,0x95,0x68,0x2C,0x33,0x39,0xBC, + 0x2C,0xD0,0x6D,0xDD,0xFA,0xDC,0xB5,0x8F,0x82,0x39,0xF8,0x67, + 0x44,0xF1,0xD8,0xF7,0x78,0x11,0x9A,0x77,0x9B,0x53,0x47,0xD6, + 0x2B,0x5D,0x67,0xB8,0xB7,0xBC,0xC1,0xD7,0x79,0x62,0x15,0xC2, + 0xC5,0x83,0x97,0xA7,0xF8,0xB4,0x9C,0xF6,0x8F,0x9A,0xC7,0xDA, + 0x1B,0xBB,0x87,0x07,0xA7,0x71,0xAD,0xB2,0x8A,0x50,0xF8,0x26, + 0x12,0xB7,0x3E,0x0B, }; - static unsigned char dh1024_g[]={ + static unsigned char dh2048_g[]={ 0x02, }; DH *dh; if ((dh=DH_new()) == NULL) return(NULL); - dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL); - dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL); + dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); + dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); @@ -259,7 +270,7 @@ new_VioSSLFd(const char *key_file, const char *cert_file, } /* DH stuff */ - dh=get_dh1024(); + dh=get_dh2048(); SSL_CTX_set_tmp_dh(ssl_fd->ssl_context, dh); DH_free(dh);