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 */