diff --git a/configure.in b/configure.in index 06a0c1bd52f9b37a4d6f8c0920e9cb80c7f63d79..65af867cbc08d773e00cdd56f6bcecb1ce48813f 100644 --- a/configure.in +++ b/configure.in @@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! -AM_INIT_AUTOMAKE(mysql, 4.0.23) +AM_INIT_AUTOMAKE(mysql, 4.0.24) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 6ed3720cc841734fc8f97aeb8af1e324c492eea0..b8339134fb1012b30ad5feaa9ecfba92d41e1136 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -1195,6 +1195,7 @@ os_file_pread( return(n_bytes); #else { + off_t ret_offset; ssize_t ret; ulint i; @@ -1203,12 +1204,12 @@ os_file_pread( os_mutex_enter(os_file_seek_mutexes[i]); - ret = lseek(file, offs, 0); + ret_offset = lseek(file, offs, SEEK_SET); - if (ret < 0) { + if (ret_offset < 0) { os_mutex_exit(os_file_seek_mutexes[i]); - return(ret); + return(-1); } ret = read(file, buf, (ssize_t)n); @@ -1281,6 +1282,7 @@ os_file_pwrite( return(ret); #else { + off_t ret_offset; ulint i; /* Protect the seek / write operation with a mutex */ @@ -1288,12 +1290,12 @@ os_file_pwrite( os_mutex_enter(os_file_seek_mutexes[i]); - ret = lseek(file, offs, 0); + ret_offset = lseek(file, offs, SEEK_SET); - if (ret < 0) { + if (ret_offset < 0) { os_mutex_exit(os_file_seek_mutexes[i]); - return(ret); + return(-1); } ret = write(file, buf, (ssize_t)n); diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index b9f860903cbe5d49dbd72dc369311318e5ed255c..60dbf0596735f9b5c75e99f4d3db0da77fcee447 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -1373,8 +1373,34 @@ row_ins_check_foreign_constraints( row_mysql_freeze_data_dictionary(trx); } + if (foreign->referenced_table) { + mutex_enter(&(dict_sys->mutex)); + + (foreign->referenced_table + ->n_foreign_key_checks_running)++; + + mutex_exit(&(dict_sys->mutex)); + } + + /* NOTE that if the thread ends up waiting for a lock + we will release dict_operation_lock temporarily! + But the counter on the table protects the referenced + table from being dropped while the check is running. */ + err = row_ins_check_foreign_constraint(TRUE, foreign, table, entry, thr); + + if (foreign->referenced_table) { + mutex_enter(&(dict_sys->mutex)); + + ut_a(foreign->referenced_table + ->n_foreign_key_checks_running > 0); + (foreign->referenced_table + ->n_foreign_key_checks_running)--; + + mutex_exit(&(dict_sys->mutex)); + } + if (got_s_lock) { row_mysql_unfreeze_data_dictionary(trx); } diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 2e8f7121d2c78d7fc8cb9401bfe21d8b176a6ef2..1ab7bb1deb08169fe7c7e606ea79a5d9e52f80ce 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1761,20 +1761,19 @@ row_drop_table_for_mysql_in_background( trx = trx_allocate_for_background(); + /* If the original transaction was dropping a table referenced by + foreign keys, we must set the following to be able to drop the + table: */ + + trx->check_foreigns = FALSE; + /* fputs("InnoDB: Error: Dropping table ", stderr); ut_print_name(stderr, name); fputs(" in background drop list\n", stderr); */ - /* Drop the table in InnoDB */ + /* Try to drop the table in InnoDB */ error = row_drop_table_for_mysql(name, trx, FALSE); - - if (error != DB_SUCCESS) { - ut_print_timestamp(stderr); - fputs(" InnoDB: Error: Dropping table ", stderr); - ut_print_name(stderr, name); - fputs(" in background drop list failed\n", stderr); - } /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs @@ -1786,7 +1785,7 @@ row_drop_table_for_mysql_in_background( trx_free_for_background(trx); - return(DB_SUCCESS); + return(error); } /************************************************************************* @@ -1820,6 +1819,7 @@ loop: mutex_exit(&kernel_mutex); if (drop == NULL) { + /* All tables dropped */ return(n_tables + n_tables_dropped); } @@ -1834,16 +1834,16 @@ loop: goto already_dropped; } - - if (table->n_mysql_handles_opened > 0 - || table->n_foreign_key_checks_running > 0) { + + if (DB_SUCCESS != row_drop_table_for_mysql_in_background( + drop->table_name)) { + /* If the DROP fails for some table, we return, and let the + main thread retry later */ return(n_tables + n_tables_dropped); } n_tables_dropped++; - - row_drop_table_for_mysql_in_background(drop->table_name); already_dropped: mutex_enter(&kernel_mutex); @@ -1887,21 +1887,21 @@ row_get_background_drop_list_len_low(void) } /************************************************************************* -Adds a table to the list of tables which the master thread drops in -background. We need this on Unix because in ALTER TABLE MySQL may call -drop table even if the table has running queries on it. */ +If a table is not yet in the drop list, adds the table to the list of tables +which the master thread drops in background. We need this on Unix because in +ALTER TABLE MySQL may call drop table even if the table has running queries on +it. Also, if there are running foreign key checks on the table, we drop the +table lazily. */ static -void +ibool row_add_table_to_background_drop_list( /*==================================*/ + /* out: TRUE if the table was not yet in the + drop list, and was added there */ dict_table_t* table) /* in: table */ { row_mysql_drop_t* drop; - drop = mem_alloc(sizeof(row_mysql_drop_t)); - - drop->table_name = mem_strdup(table->name); - mutex_enter(&kernel_mutex); if (!row_mysql_drop_list_inited) { @@ -1909,6 +1909,25 @@ row_add_table_to_background_drop_list( UT_LIST_INIT(row_mysql_drop_list); row_mysql_drop_list_inited = TRUE; } + + /* Look if the table already is in the drop list */ + drop = UT_LIST_GET_FIRST(row_mysql_drop_list); + + while (drop != NULL) { + if (strcmp(drop->table_name, table->name) == 0) { + /* Already in the list */ + + mutex_exit(&kernel_mutex); + + return(FALSE); + } + + drop = UT_LIST_GET_NEXT(row_mysql_drop_list, drop); + } + + drop = mem_alloc(sizeof(row_mysql_drop_t)); + + drop->table_name = mem_strdup(table->name); UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop); @@ -1917,6 +1936,8 @@ row_add_table_to_background_drop_list( fputs(" to background drop list\n", stderr); */ mutex_exit(&kernel_mutex); + + return(TRUE); } /************************************************************************* @@ -2151,36 +2172,53 @@ row_drop_table_for_mysql( } if (table->n_mysql_handles_opened > 0) { + ibool added; - ut_print_timestamp(stderr); - fputs(" InnoDB: Warning: MySQL is trying to drop table ", - stderr); - ut_print_name(stderr, table->name); - fputs("\n" - "InnoDB: though there are still open handles to it.\n" - "InnoDB: Adding the table to the background drop queue.\n", - stderr); + added = row_add_table_to_background_drop_list(table); - row_add_table_to_background_drop_list(table); + if (added) { + ut_print_timestamp(stderr); +fputs(" InnoDB: Warning: MySQL is trying to drop table ", stderr); + ut_print_name(stderr, table->name); + fputs("\n" +"InnoDB: though there are still open handles to it.\n" +"InnoDB: Adding the table to the background drop queue.\n", + stderr); + + /* We return DB_SUCCESS to MySQL though the drop will + happen lazily later */ - err = DB_SUCCESS; + err = DB_SUCCESS; + } else { + /* The table is already in the background drop list */ + err = DB_ERROR; + } goto funct_exit; } if (table->n_foreign_key_checks_running > 0) { + ibool added; + + added = row_add_table_to_background_drop_list(table); - ut_print_timestamp(stderr); - fputs(" InnoDB: You are trying to drop table ", stderr); + if (added) { + ut_print_timestamp(stderr); +fputs(" InnoDB: You are trying to drop table ", stderr); ut_print_name(stderr, table->name); - fputs("\n" - "InnoDB: though there is a foreign key check running on it.\n" - "InnoDB: Adding the table to the background drop queue.\n", + fputs("\n" +"InnoDB: though there is a foreign key check running on it.\n" +"InnoDB: Adding the table to the background drop queue.\n", stderr); - row_add_table_to_background_drop_list(table); + /* We return DB_SUCCESS to MySQL though the drop will + happen lazily later */ - err = DB_SUCCESS; + err = DB_SUCCESS; + } else { + /* The table is already in the background drop list */ + err = DB_ERROR; + } goto funct_exit; } diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index d47560fe7a6c11762974a383455315839627d804..b760309bb5b29089bb1e6eb8dece9bd40f0227d6 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -4,6 +4,7 @@ # Sligtly updated by Monty # Cleaned up again by Matt # Fixed by Sergei +# List of failed cases (--force) backported from 4.1 by Joerg # :-) #++ @@ -202,6 +203,7 @@ MYSQL_MANAGER_LOG=$MYSQL_TEST_DIR/var/log/manager.log MYSQL_MANAGER_USER=root NO_SLAVE=0 USER_TEST= +FAILED_CASES= EXTRA_MASTER_OPT="" EXTRA_MYSQL_TEST_OPT="" @@ -1333,7 +1335,7 @@ run_testcase () show_failed_diff $result_file $ECHO if [ x$FORCE != x1 ] ; then - $ECHO "Aborting. To continue, re-run with '--force'." + $ECHO "Aborting: $tname failed. To continue, re-run with '--force'." $ECHO if [ -z "$DO_GDB" ] && [ -z "$USE_RUNNING_SERVER" ] && [ -z "$DO_DDD" ] then @@ -1342,7 +1344,7 @@ run_testcase () fi exit 1 fi - + FAILED_CASES="$FAILED_CASES $tname" if [ -z "$DO_GDB" ] && [ -z "$USE_RUNNING_SERVER" ] && [ -z "$DO_DDD" ] then mysql_restart @@ -1485,4 +1487,10 @@ $ECHO [ "$DO_GCOV" ] && gcov_collect # collect coverage information [ "$DO_GPROF" ] && gprof_collect # collect coverage information -exit 0 +if [ $TOT_FAIL -ne 0 ]; then + $ECHO "mysql-test-run: *** Failing the test(s):$FAILED_CASES" + $ECHO + exit 1 +else + exit 0 +fi diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index d38a2edfa1ab6f1a9fbdf76a4f7171e784bcd405..278cd4dd935f95b1b8e07510880284f04ecfcaee 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -291,3 +291,9 @@ trim(trailing 'foo' from 'foo') select trim(leading 'foo' from 'foo'); trim(leading 'foo' from 'foo') +select quote(ltrim(concat(' ', 'a'))); +quote(ltrim(concat(' ', 'a'))) +'a' +select quote(trim(concat(' ', 'a'))); +quote(trim(concat(' ', 'a'))) +'a' diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index f0e5d16e916c38b0d1aeed8848416699a7d0cd46..19d83a95c5e2e09887798f1d809e179fc11a2b5f 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -36,6 +36,28 @@ Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE ISSUER 'MySQL AB' SUBJECT 'testsubject' CIPHER 'EDH-RSA-DES-CBC3-SHA' delete from mysql.user where user='mysqltest_1'; flush privileges; +delete from mysql.user where user='mysqltest_1'; +flush privileges; +grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10; +select * from mysql.user where user="mysqltest_1"; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 10 0 0 +show grants for mysqltest_1@localhost; +Grants for mysqltest_1@localhost +GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 +grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30; +select * from mysql.user where user="mysqltest_1"; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 10 20 30 +show grants for mysqltest_1@localhost; +Grants for mysqltest_1@localhost +GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30 +flush privileges; +show grants for mysqltest_1@localhost; +Grants for mysqltest_1@localhost +GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30 +delete from mysql.user where user='mysqltest_1'; +flush privileges; grant CREATE TEMPORARY TABLES, LOCK TABLES on mysqltest.* to mysqltest_1@localhost; show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost @@ -156,3 +178,67 @@ select host,db,user,select_priv,insert_priv from mysql.db where db="db6123"; host db user select_priv insert_priv delete from mysql.user where user='test6123'; drop database db6123; +create database mysqltest_1; +create database mysqltest_2; +create table mysqltest_1.t1 select 1 a, 2 q; +create table mysqltest_1.t2 select 1 b, 2 r; +create table mysqltest_2.t1 select 1 c, 2 s; +create table mysqltest_2.t2 select 1 d, 2 t; +grant update (a) on mysqltest_1.t1 to mysqltest_3@localhost; +grant select (b) on mysqltest_1.t2 to mysqltest_3@localhost; +grant select (c) on mysqltest_2.t1 to mysqltest_3@localhost; +grant update (d) on mysqltest_2.t2 to mysqltest_3@localhost; +show grants for mysqltest_3@localhost; +Grants for mysqltest_3@localhost +GRANT USAGE ON *.* TO 'mysqltest_3'@'localhost' +GRANT SELECT (b) ON `mysqltest_1`.`t2` TO 'mysqltest_3'@'localhost' +GRANT SELECT (c) ON `mysqltest_2`.`t1` TO 'mysqltest_3'@'localhost' +GRANT UPDATE (a) ON `mysqltest_1`.`t1` TO 'mysqltest_3'@'localhost' +GRANT UPDATE (d) ON `mysqltest_2`.`t2` TO 'mysqltest_3'@'localhost' +update mysqltest_1.t1, mysqltest_1.t2 set q=10 where b=1; +UPDATE command denied to user: 'mysqltest_3@localhost' for column 'q' in table 't1' +update mysqltest_1.t1, mysqltest_2.t2 set d=20 where d=1; +select command denied to user: 'mysqltest_3@localhost' for table 't1' +update mysqltest_2.t1, mysqltest_1.t2 set c=20 where b=1; +UPDATE command denied to user: 'mysqltest_3@localhost' for column 'c' in table 't1' +update mysqltest_2.t1, mysqltest_2.t2 set d=10 where s=2; +SELECT command denied to user: 'mysqltest_3@localhost' for column 's' in table 't1' +update mysqltest_1.t1, mysqltest_2.t2 set a=10,d=10; +update mysqltest_1.t1, mysqltest_2.t1 set a=20 where c=20; +select t1.*,t2.* from mysqltest_1.t1,mysqltest_1.t2; +a q b r +10 2 1 2 +select t1.*,t2.* from mysqltest_2.t1,mysqltest_2.t2; +c s d t +1 2 10 2 +revoke all on mysqltest_1.t1 from mysqltest_3@localhost; +revoke all on mysqltest_1.t2 from mysqltest_3@localhost; +revoke all on mysqltest_2.t1 from mysqltest_3@localhost; +revoke all on mysqltest_2.t2 from mysqltest_3@localhost; +grant all on mysqltest_2.* to mysqltest_3@localhost; +grant select on *.* to mysqltest_3@localhost; +flush privileges; +use mysqltest_1; +update mysqltest_2.t1, mysqltest_2.t2 set c=500,d=600; +update mysqltest_1.t1, mysqltest_1.t2 set a=100,b=200; +UPDATE command denied to user: 'mysqltest_3@localhost' for column 'a' in table 't1' +use mysqltest_2; +update mysqltest_1.t1, mysqltest_1.t2 set a=100,b=200; +Access denied for user: 'mysqltest_3@localhost' to database 'mysqltest_1' +update mysqltest_2.t1, mysqltest_1.t2 set c=100,b=200; +Access denied for user: 'mysqltest_3@localhost' to database 'mysqltest_1' +update mysqltest_1.t1, mysqltest_2.t2 set a=100,d=200; +Access denied for user: 'mysqltest_3@localhost' to database 'mysqltest_1' +select t1.*,t2.* from mysqltest_1.t1,mysqltest_1.t2; +a q b r +10 2 1 2 +select t1.*,t2.* from mysqltest_2.t1,mysqltest_2.t2; +c s d t +500 2 600 2 +delete from mysql.user where user='mysqltest_3'; +delete from mysql.db where user="mysqltest_3"; +delete from mysql.tables_priv where user="mysqltest_3"; +delete from mysql.columns_priv where user="mysqltest_3"; +flush privileges; +drop database mysqltest_1; +drop database mysqltest_2; diff --git a/mysql-test/r/update.result b/mysql-test/r/update.result index c9405d71237826c70619aa3c36c038c57572899c..7810d52d15618cfb32f8e63d14ef782ca0b1b441 100644 --- a/mysql-test/r/update.result +++ b/mysql-test/r/update.result @@ -203,3 +203,9 @@ colC colA colD colE colF 3 4433 10005 492 500 DROP TABLE t1; DROP TABLE t2; +create table t1 (c1 int, c2 char(6), c3 int); +create table t2 (c1 int, c2 char(6)); +insert into t1 values (1, "t1c2-1", 10), (2, "t1c2-2", 20); +update t1 left join t2 on t1.c1 = t2.c1 set t2.c2 = "t2c2-1"; +update t1 left join t2 on t1.c1 = t2.c1 set t2.c2 = "t2c2-1" where t1.c3 = 10; +drop table t1, t2; diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 1ae4db3a42a3688cd9395a56db5b7322a65ab546..79a996e7e78ee40e2365461ce237840aeb31a076 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -185,3 +185,11 @@ drop table t1; select trim(trailing 'foo' from 'foo'); select trim(leading 'foo' from 'foo'); + +# +# crashing bug with QUOTE() and LTRIM() or TRIM() fixed +# Bug #7495 +# + +select quote(ltrim(concat(' ', 'a'))); +select quote(trim(concat(' ', 'a'))); diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index 21173a356ce4efca328ff64472dc1ac5130daf50..960c55065fe508b9fa303c45b77b279b7254f384 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -2,6 +2,8 @@ drop table if exists t1; --enable_warnings +connect (master,localhost,root,,); +connection master; # # Test that SSL options works properly # @@ -25,6 +27,23 @@ show grants for mysqltest_1@localhost; delete from mysql.user where user='mysqltest_1'; flush privileges; +# +# Test of GRANTS specifying user limits +# +delete from mysql.user where user='mysqltest_1'; +flush privileges; +grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10; +select * from mysql.user where user="mysqltest_1"; +show grants for mysqltest_1@localhost; +grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30; +select * from mysql.user where user="mysqltest_1"; +show grants for mysqltest_1@localhost; +# This is just to double check that one won't ignore results of selects +flush privileges; +show grants for mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +flush privileges; + # # Test that the new db privileges are stored/retrieved correctly # @@ -114,3 +133,73 @@ grant usage on db6123.* to test6123 identified by 'magic123'; select host,db,user,select_priv,insert_priv from mysql.db where db="db6123"; delete from mysql.user where user='test6123'; drop database db6123; + +# +# Bug#7391: Cross-database multi-table UPDATE security problem +# +create database mysqltest_1; +create database mysqltest_2; +create table mysqltest_1.t1 select 1 a, 2 q; +create table mysqltest_1.t2 select 1 b, 2 r; +create table mysqltest_2.t1 select 1 c, 2 s; +create table mysqltest_2.t2 select 1 d, 2 t; + +#test the column privileges +grant update (a) on mysqltest_1.t1 to mysqltest_3@localhost; +grant select (b) on mysqltest_1.t2 to mysqltest_3@localhost; +grant select (c) on mysqltest_2.t1 to mysqltest_3@localhost; +grant update (d) on mysqltest_2.t2 to mysqltest_3@localhost; +connect (conn1,localhost,mysqltest_3,,); +connection conn1; +show grants for mysqltest_3@localhost; +--error 1143 +update mysqltest_1.t1, mysqltest_1.t2 set q=10 where b=1; +--error 1142 +update mysqltest_1.t1, mysqltest_2.t2 set d=20 where d=1; +--error 1143 +update mysqltest_2.t1, mysqltest_1.t2 set c=20 where b=1; +--error 1143 +update mysqltest_2.t1, mysqltest_2.t2 set d=10 where s=2; +#the following two should work +update mysqltest_1.t1, mysqltest_2.t2 set a=10,d=10; +update mysqltest_1.t1, mysqltest_2.t1 set a=20 where c=20; +connection master; +select t1.*,t2.* from mysqltest_1.t1,mysqltest_1.t2; +select t1.*,t2.* from mysqltest_2.t1,mysqltest_2.t2; +revoke all on mysqltest_1.t1 from mysqltest_3@localhost; +revoke all on mysqltest_1.t2 from mysqltest_3@localhost; +revoke all on mysqltest_2.t1 from mysqltest_3@localhost; +revoke all on mysqltest_2.t2 from mysqltest_3@localhost; + +#test the db/table level privileges +grant all on mysqltest_2.* to mysqltest_3@localhost; +grant select on *.* to mysqltest_3@localhost; +flush privileges; +disconnect conn1; +connect (conn2,localhost,mysqltest_3,,); +connection conn2; +use mysqltest_1; +update mysqltest_2.t1, mysqltest_2.t2 set c=500,d=600; +# the following failed before, should fail now. +--error 1143 +update mysqltest_1.t1, mysqltest_1.t2 set a=100,b=200; +use mysqltest_2; +#the following used to succeed, it must fail now. +--error 1044 +update mysqltest_1.t1, mysqltest_1.t2 set a=100,b=200; +--error 1044 +update mysqltest_2.t1, mysqltest_1.t2 set c=100,b=200; +--error 1044 +update mysqltest_1.t1, mysqltest_2.t2 set a=100,d=200; +#lets see the result +connection master; +select t1.*,t2.* from mysqltest_1.t1,mysqltest_1.t2; +select t1.*,t2.* from mysqltest_2.t1,mysqltest_2.t2; + +delete from mysql.user where user='mysqltest_3'; +delete from mysql.db where user="mysqltest_3"; +delete from mysql.tables_priv where user="mysqltest_3"; +delete from mysql.columns_priv where user="mysqltest_3"; +flush privileges; +drop database mysqltest_1; +drop database mysqltest_2; diff --git a/mysql-test/t/update.test b/mysql-test/t/update.test index 1850564418c37816776980e573121532a9f2676e..62439dcc51bf267d41e3062fa2873be881e531ae 100644 --- a/mysql-test/t/update.test +++ b/mysql-test/t/update.test @@ -155,3 +155,12 @@ SELECT * FROM t2; DROP TABLE t1; DROP TABLE t2; +# +# Bug #6054 +# +create table t1 (c1 int, c2 char(6), c3 int); +create table t2 (c1 int, c2 char(6)); +insert into t1 values (1, "t1c2-1", 10), (2, "t1c2-2", 20); +update t1 left join t2 on t1.c1 = t2.c1 set t2.c2 = "t2c2-1"; +update t1 left join t2 on t1.c1 = t2.c1 set t2.c2 = "t2c2-1" where t1.c3 = 10; +drop table t1, t2; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index c36075207ed580d4babd7c2f5d35fde9f1038cc6..69d9d885b8efc139224de412e786df0fe7b49abf 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4702,7 +4702,8 @@ ha_innobase::external_lock( if (prebuilt->select_lock_type != LOCK_NONE) { if (thd->in_lock_tables && - thd->variables.innodb_table_locks) { + thd->variables.innodb_table_locks && + (thd->options & OPTION_NOT_AUTOCOMMIT)) { ulint error; error = row_lock_table_for_mysql(prebuilt); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 53a9d3fe2199109ebc70b9fb21969f98e04ef6b2..a852906ee2c559ae2d99eeda305b8650d7bd23fb 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2185,16 +2185,16 @@ String *Item_func_quote::val_str(String *str) /* We have to use realloc() instead of alloc() as we want to keep the - old result in str + old result in arg */ - if (str->realloc(new_length)) + if (arg->realloc(new_length)) goto null; /* As 'arg' and 'str' may be the same string, we must replace characters from the end to the beginning */ - to= (char*) str->ptr() + new_length - 1; + to= (char*) arg->ptr() + new_length - 1; *to--= '\''; for (start= (char*) arg->ptr(),end= start + arg_length; end-- != start; to--) { @@ -2222,9 +2222,9 @@ String *Item_func_quote::val_str(String *str) } } *to= '\''; - str->length(new_length); + arg->length(new_length); null_value= 0; - return str; + return arg; null: null_value= 1; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 768acd77c27f26b01705a62ba63902b0206b33e5..eae63c5deb3d722af543ee697a7e2e8c817f4ca0 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -107,15 +107,6 @@ extern "C" { // Because of SCO 3.2V4.2 int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; -#ifdef __STDC__ -#define my_fromhost(A) fromhost(A) -#define my_hosts_access(A) hosts_access(A) -#define my_eval_client(A) eval_client(A) -#else -#define my_fromhost(A) fromhost() -#define my_hosts_access(A) hosts_access() -#define my_eval_client(A) eval_client() -#endif #endif /* HAVE_LIBWRAP */ #ifdef HAVE_SYS_MMAN_H @@ -3240,8 +3231,8 @@ extern "C" pthread_handler_decl(handle_connections_sockets, struct request_info req; signal(SIGCHLD, SIG_DFL); request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL); - my_fromhost(&req); - if (!my_hosts_access(&req)) + fromhost(&req); + if (!hosts_access(&req)) { /* This may be stupid but refuse() includes an exit(0) @@ -3249,7 +3240,7 @@ extern "C" pthread_handler_decl(handle_connections_sockets, clean_exit() - same stupid thing ... */ syslog(deny_severity, "refused connect from %s", - my_eval_client(&req)); + eval_client(&req)); /* C++ sucks (the gibberish in front just translates the supplied diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 67ca62357ecc485d65d8e61e53035a33329f0699..1b55168695b17f36d94f785a5a83ed768ba2796a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1403,6 +1403,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, Field **tmp_field; ulong priv; + uint next_field; for (tmp_field= table->field+3, priv = SELECT_ACL; *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM && ((Field_enum*) (*tmp_field))->typelib->count == 2 ; @@ -1411,56 +1412,59 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, if (priv & rights) // set requested privileges (*tmp_field)->store(&what,1); } - rights=get_access(table,3,0); + rights= get_access(table, 3, &next_field); DBUG_PRINT("info",("table->fields: %d",table->fields)); if (table->fields >= 31) /* From 4.0.0 we have more fields */ { /* We write down SSL related ACL stuff */ switch (thd->lex.ssl_type) { case SSL_TYPE_ANY: - table->field[24]->store("ANY",3); - table->field[25]->store("",0); - table->field[26]->store("",0); - table->field[27]->store("",0); + table->field[next_field]->store("ANY", 3); + table->field[next_field+1]->store("", 0); + table->field[next_field+2]->store("", 0); + table->field[next_field+3]->store("", 0); break; case SSL_TYPE_X509: - table->field[24]->store("X509",4); - table->field[25]->store("",0); - table->field[26]->store("",0); - table->field[27]->store("",0); + table->field[next_field]->store("X509", 4); + table->field[next_field+1]->store("", 0); + table->field[next_field+2]->store("", 0); + table->field[next_field+3]->store("", 0); break; case SSL_TYPE_SPECIFIED: - table->field[24]->store("SPECIFIED",9); - table->field[25]->store("",0); - table->field[26]->store("",0); - table->field[27]->store("",0); + table->field[next_field]->store("SPECIFIED", 9); + table->field[next_field+1]->store("", 0); + table->field[next_field+2]->store("", 0); + table->field[next_field+3]->store("", 0); if (thd->lex.ssl_cipher) - table->field[25]->store(thd->lex.ssl_cipher, - strlen(thd->lex.ssl_cipher)); + table->field[next_field+1]->store(thd->lex.ssl_cipher, + strlen(thd->lex.ssl_cipher)); if (thd->lex.x509_issuer) - table->field[26]->store(thd->lex.x509_issuer, - strlen(thd->lex.x509_issuer)); + table->field[next_field+2]->store(thd->lex.x509_issuer, + strlen(thd->lex.x509_issuer)); if (thd->lex.x509_subject) - table->field[27]->store(thd->lex.x509_subject, - strlen(thd->lex.x509_subject)); + table->field[next_field+3]->store(thd->lex.x509_subject, + strlen(thd->lex.x509_subject)); break; case SSL_TYPE_NOT_SPECIFIED: break; case SSL_TYPE_NONE: - table->field[24]->store("",0); - table->field[25]->store("",0); - table->field[26]->store("",0); - table->field[27]->store("",0); + table->field[next_field]->store("", 0); + table->field[next_field+1]->store("", 0); + table->field[next_field+2]->store("", 0); + table->field[next_field+3]->store("", 0); break; } + /* Skip over SSL related fields to first user limits related field */ + next_field+= 4; + USER_RESOURCES mqh = thd->lex.mqh; if (mqh.bits & 1) - table->field[28]->store((longlong) mqh.questions); + table->field[next_field]->store((longlong) mqh.questions); if (mqh.bits & 2) - table->field[29]->store((longlong) mqh.updates); + table->field[next_field+1]->store((longlong) mqh.updates); if (mqh.bits & 4) - table->field[30]->store((longlong) mqh.connections); + table->field[next_field+2]->store((longlong) mqh.connections); mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections; } if (old_row_exists) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2df0d45f8ed1b33d155a59a12a6136642d6b73fc..eda4ce7318679434feb409ffa4d5a2c178c6e9ad 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4951,10 +4951,7 @@ join_read_system(JOIN_TAB *tab) table->file->print_error(error,MYF(0)); return 1; } - if (tab->on_expr) - mark_as_null_row(tab->table); - else - table->null_row=1; // Why do this for inner join? + mark_as_null_row(tab->table); empty_record(table); // Make empty record return -1; } @@ -4984,10 +4981,7 @@ join_read_const(JOIN_TAB *tab) } if (error) { - if (tab->on_expr) - mark_as_null_row(tab->table); - else - table->null_row=1; + mark_as_null_row(tab->table); empty_record(table); if (error != HA_ERR_KEY_NOT_FOUND) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index cdcc90e8651ef36793516b405b9e0bdf3c0ae8d2..4f7e34ec74ff34b427b9b65f716a7d713b215dba 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -465,21 +465,34 @@ int mysql_multi_update(THD *thd, */ for (tl= table_list ; tl ; tl=tl->next) { + TABLE_LIST *save= tl->next; TABLE *table= tl->table; + uint wants; + tl->next= 0; if (update_map & table->map) { DBUG_PRINT("info",("setting table `%s` for update", tl->alias)); tl->lock_type= thd->lex.lock_option; tl->updating= 1; + wants= UPDATE_ACL; } else { DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias)); tl->lock_type= TL_READ; tl->updating= 0; + wants= SELECT_ACL; } if (!using_lock_tables) tl->table->reginfo.lock_type= tl->lock_type; + + if (check_access(thd, wants, tl->db, &tl->grant.privilege, 0, 0) || + (grant_option && check_grant(thd, wants, tl, 0, 0))) + { + tl->next= save; + DBUG_RETURN(0); + } + tl->next= save; } /* Relock the tables with the correct modes */