diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index b23556887c1..e3aa87876ed 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -4899,3 +4899,92 @@ WHERE t1.a = d1.a; ERROR 42S22: Unknown column 'd1.a' in 'where clause' DROP TABLE t1; End of 5.1 tests. +Set up test tables. +CREATE TABLE t1 ( +t1_id INT UNSIGNED, +PRIMARY KEY(t1_id) +) Engine=MyISAM; +INSERT INTO t1 (t1_id) VALUES (1), (2), (3), (4), (5); +CREATE TABLE t2 SELECT * FROM t1; +CREATE TABLE t3 ( +t3_id INT UNSIGNED AUTO_INCREMENT, +t1_id INT UNSIGNED, +amount DECIMAL(16,2), +PRIMARY KEY(t3_id), +KEY(t1_id) +) Engine=MyISAM; +INSERT INTO t3 (t1_id, t3_id, amount) +VALUES (1, 1, 100.00), (2, 2, 200.00), (4, 4, 400.00); +This is the 'inner query' running by itself. +Produces correct results. +SELECT +t1.t1_id, +IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM +t1 +LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY +t1.t1_id +; +t1_id total_amount +1 100.00 +2 200.00 +3 0.00 +4 400.00 +5 0.00 +SELECT * FROM (the same inner query) +Produces correct results. +SELECT * FROM ( +SELECT +t1.t1_id, +IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM +t1 +LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY +t1.t1_id +) AS t; +t1_id total_amount +1 100.00 +2 200.00 +3 0.00 +4 400.00 +5 0.00 +Now make t2.t1_id part of a key. +ALTER TABLE t2 ADD PRIMARY KEY(t1_id); +Same inner query by itself. +Still correct results. +SELECT +t1.t1_id, +IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM +t1 +LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY +t1.t1_id; +t1_id total_amount +1 100.00 +2 200.00 +3 0 +4 400.00 +5 0 +SELECT * FROM (the same inner query), now with indexes on the LEFT JOIN +SELECT * FROM ( +SELECT +t1.t1_id, +IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM +t1 +LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY +t1.t1_id +) AS t; +t1_id total_amount +1 100.00 +2 200.00 +3 0.00 +4 400.00 +5 0.00 +DROP TABLE t3; +DROP TABLE t2; +DROP TABLE t1; diff --git a/mysql-test/r/symlink.result b/mysql-test/r/symlink.result index 245dcec04da..4dac7443135 100644 --- a/mysql-test/r/symlink.result +++ b/mysql-test/r/symlink.result @@ -168,4 +168,16 @@ Warning 1618 option ignored Warning 1618 option ignored DROP TABLE t1; SET @@SQL_MODE=@OLD_SQL_MODE; +# +# BUG#40980 - Drop table can remove another MyISAM table's +# data and index files +# +CREATE TABLE user(a INT) DATA DIRECTORY='MYSQL_TMP_DIR/mysql' + INDEX DIRECTORY='MYSQL_TMP_DIR/mysql'; +FLUSH TABLE user; +# Symlinking mysql database to tmpdir +FLUSH TABLE mysql.user; +DROP TABLE user; +FLUSH TABLE mysql.user; +SELECT * FROM mysql.user; End of 5.1 tests diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 50f2644ff9e..06aaf5dfb7f 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -3838,3 +3838,86 @@ DROP TABLE t1; --echo End of 5.1 tests. + +# +# Bug #47904 Incorrect results w/ table subquery, derived SQs, and LEFT JOIN on index +# + +--echo Set up test tables. +CREATE TABLE t1 ( + t1_id INT UNSIGNED, + + PRIMARY KEY(t1_id) +) Engine=MyISAM; + +INSERT INTO t1 (t1_id) VALUES (1), (2), (3), (4), (5); + +CREATE TABLE t2 SELECT * FROM t1; + +CREATE TABLE t3 ( + t3_id INT UNSIGNED AUTO_INCREMENT, + t1_id INT UNSIGNED, + amount DECIMAL(16,2), + + PRIMARY KEY(t3_id), + KEY(t1_id) +) Engine=MyISAM; + +INSERT INTO t3 (t1_id, t3_id, amount) + VALUES (1, 1, 100.00), (2, 2, 200.00), (4, 4, 400.00); + +--echo This is the 'inner query' running by itself. +--echo Produces correct results. +SELECT + t1.t1_id, + IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM + t1 + LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY + t1.t1_id +; + +--echo SELECT * FROM (the same inner query) +--echo Produces correct results. +SELECT * FROM ( +SELECT + t1.t1_id, + IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM + t1 + LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY + t1.t1_id +) AS t; + +--echo Now make t2.t1_id part of a key. +ALTER TABLE t2 ADD PRIMARY KEY(t1_id); + +--echo Same inner query by itself. +--echo Still correct results. +SELECT + t1.t1_id, + IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM + t1 + LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY + t1.t1_id; + +--echo SELECT * FROM (the same inner query), now with indexes on the LEFT JOIN +SELECT * FROM ( +SELECT + t1.t1_id, + IFNULL((SELECT SUM(amount) FROM t3 WHERE t3.t1_id=t1.t1_id), 0) AS total_amount +FROM + t1 + LEFT JOIN t2 ON t2.t1_id=t1.t1_id +GROUP BY + t1.t1_id +) AS t; + + +DROP TABLE t3; +DROP TABLE t2; +DROP TABLE t1; diff --git a/mysql-test/t/symlink.test b/mysql-test/t/symlink.test index c805dd40fe8..073fcd28246 100644 --- a/mysql-test/t/symlink.test +++ b/mysql-test/t/symlink.test @@ -249,4 +249,26 @@ eval CREATE TABLE t1(a INT) DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' INDEX DIRECTO DROP TABLE t1; SET @@SQL_MODE=@OLD_SQL_MODE; +--echo # +--echo # BUG#40980 - Drop table can remove another MyISAM table's +--echo # data and index files +--echo # +--mkdir $MYSQL_TMP_DIR/mysql +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval CREATE TABLE user(a INT) DATA DIRECTORY='$MYSQL_TMP_DIR/mysql' + INDEX DIRECTORY='$MYSQL_TMP_DIR/mysql'; +FLUSH TABLE user; +--echo # Symlinking mysql database to tmpdir +--remove_file $MYSQL_TMP_DIR/mysql/user.MYD +--remove_file $MYSQL_TMP_DIR/mysql/user.MYI +--rmdir $MYSQL_TMP_DIR/mysql +--exec ln -s $MYSQLD_DATADIR/mysql $MYSQL_TMP_DIR/mysql +FLUSH TABLE mysql.user; +DROP TABLE user; +FLUSH TABLE mysql.user; +--disable_result_log +SELECT * FROM mysql.user; +--enable_result_log +--remove_file $MYSQL_TMP_DIR/mysql + --echo End of 5.1 tests diff --git a/sql/item.cc b/sql/item.cc index 183868ea5c6..84da69adc39 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3750,7 +3750,7 @@ void Item_copy_decimal::copy() { my_decimal *nr= item->val_decimal(&cached_value); if (nr && nr != &cached_value) - memcpy (&cached_value, nr, sizeof (my_decimal)); + my_decimal2decimal (nr, &cached_value); null_value= item->null_value; } diff --git a/storage/myisam/mi_delete_table.c b/storage/myisam/mi_delete_table.c index 28aee848665..4fc9767251c 100644 --- a/storage/myisam/mi_delete_table.c +++ b/storage/myisam/mi_delete_table.c @@ -19,6 +19,41 @@ #include "fulltext.h" + +/** + Remove MyISAM data/index file safely + + @details + If name is a symlink and file it is pointing to is not in + data directory, file is also removed. + + @param name file to remove + + @returns + 0 on success or my_errno on failure +*/ + +static int _mi_safe_delete_file(const char *name) +{ + DBUG_ENTER("_mi_safe_delete_file"); + if (my_is_symlink(name) && (*myisam_test_invalid_symlink)(name)) + { + /* + Symlink is pointing to file in data directory. + Remove symlink, keep file. + */ + if (my_delete(name, MYF(MY_WME))) + DBUG_RETURN(my_errno); + } + else + { + if (my_delete_with_symlink(name, MYF(MY_WME))) + DBUG_RETURN(my_errno); + } + DBUG_RETURN(0); +} + + int mi_delete_table(const char *name) { char from[FN_REFLEN]; @@ -58,13 +93,39 @@ int mi_delete_table(const char *name) #endif /* USE_RAID */ fn_format(from,name,"",MI_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (mysql_file_delete_with_symlink(mi_key_file_kfile, from, MYF(MY_WME))) + if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from)) + { + /* + Symlink is pointing to file in data directory. + Remove symlink, keep file. + */ + if (mysql_file_delete(mi_key_file_kfile, from, MYF(MY_WME))) + DBUG_RETURN(my_errno); + } + else + { + if (mysql_file_delete_with_symlink(mi_key_file_kfile, from, MYF(MY_WME))) + DBUG_RETURN(my_errno); + } DBUG_RETURN(my_errno); fn_format(from,name,"",MI_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); #ifdef USE_RAID if (raid_type) DBUG_RETURN(my_raid_delete(from, raid_chunks, MYF(MY_WME)) ? my_errno : 0); #endif - DBUG_RETURN(mysql_file_delete_with_symlink(mi_key_file_dfile, - from, MYF(MY_WME)) ? my_errno : 0); + if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from)) + { + /* + Symlink is pointing to file in data directory. + Remove symlink, keep file. + */ + if (mysql_file_delete(mi_key_file_dfile, from, MYF(MY_WME))) + DBUG_RETURN(my_errno); + } + else + { + if (mysql_file_delete_with_symlink(mi_key_file_dfile, from, MYF(MY_WME))) + DBUG_RETURN(my_errno); + } + DBUG_RETURN(0); }