diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c
index 31a601e68b083e9c19d711d51f006e2f865ef875..e8261ab1e910d56176c6251575e80225ad0fbf04 100644
--- a/innobase/dict/dict0crea.c
+++ b/innobase/dict/dict0crea.c
@@ -1011,6 +1011,12 @@ dict_create_or_check_foreign_constraint_tables(void)
 	there are 2 secondary indexes on SYS_FOREIGN, and they
 	are defined just like below */
 	
+	/* NOTE: when designing InnoDB's foreign key support in 2001, we made
+	an error and made the table names and the foreign key id of type
+	'CHAR' (internally, really a VARCHAR). We should have made the type
+	VARBINARY, like in other InnoDB system tables, to get a clean
+	design. */
+
 	str = (char *)
 	"PROCEDURE CREATE_FOREIGN_SYS_TABLES_PROC () IS\n"
 	"BEGIN\n"
@@ -1227,9 +1233,17 @@ loop:
 		fputs(".\nA foreign key constraint of name ", ef);
 		ut_print_name(ef, foreign->id);
 		fputs("\nalready exists."
-			"  (Note that internally InnoDB adds 'databasename/'\n"
+			" (Note that internally InnoDB adds 'databasename/'\n"
 			"in front of the user-defined constraint name).\n",
 			ef);
+		fputs("Note that InnoDB's FOREIGN KEY system tables store\n"
+		      "constraint names as case-insensitive, with the\n"
+		      "MySQL standard latin1_swedish_ci collation. If you\n"
+		      "create tables or databases whose names differ only in\n"
+		      "the character case, then collisions in constraint\n"
+		      "names can occur. Workaround: name your constraints\n"
+		      "explicitly with unique names.\n",
+			ef);
 
 		mutex_exit(&dict_foreign_err_mutex);
 
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c
index 4340934ab3da7f1ae2f842acd9347cc0fc10c567..5ca31ecd422e6954c6e3338c43bd9d0a8bf855ff 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -132,7 +132,7 @@ dict_index_build_internal_non_clust(
 	dict_index_t*	index);	/* in: user representation of a non-clustered
 				index */	
 /**************************************************************************
-Removes a foreign constraint struct from the dictionet cache. */
+Removes a foreign constraint struct from the dictionary cache. */
 static
 void
 dict_foreign_remove_from_cache(
@@ -581,7 +581,7 @@ dict_table_get_on_id(
 	dict_table_t*	table;
 	
 	if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0
-	   || trx->dict_operation) {
+	   || trx->dict_operation_lock_mode == RW_X_LATCH) {
 		/* It is a system table which will always exist in the table
 		cache: we avoid acquiring the dictionary mutex, because
 		if we are doing a rollback to handle an error in TABLE
diff --git a/innobase/dict/dict0load.c b/innobase/dict/dict0load.c
index 6a4d4c868245335281dc41ba2cfef80fa69d0b8d..a4637e09d07a281641b0c847c79c2575bd912dd1 100644
--- a/innobase/dict/dict0load.c
+++ b/innobase/dict/dict0load.c
@@ -19,6 +19,7 @@ Created 4/24/1996 Heikki Tuuri
 #include "mach0data.h"
 #include "dict0dict.h"
 #include "dict0boot.h"
+#include "rem0cmp.h"
 
 /************************************************************************
 Finds the first table name in the given database. */
@@ -1121,12 +1122,26 @@ loop:
 	rec = btr_pcur_get_rec(&pcur);
 	field = rec_get_nth_field(rec, 0, &len);
 
-	/* Check if the table name in record is the one searched for */
-	if (len != ut_strlen(table_name)
-	    || 0 != ut_memcmp(field, table_name, len)) {
+	/* Check if the table name in the record is the one searched for; the
+	following call does the comparison in the latin1_swedish_ci
+	charset-collation, in a case-insensitive way. */
 
+	if (0 != cmp_data_data(dfield_get_type(dfield),
+			dfield_get_data(dfield), dfield_get_len(dfield),
+			field, len)) {
+		
 		goto load_next_index;
 	}
+
+	/* Since table names in SYS_FOREIGN are stored in a case-insensitive
+	order, we have to check that the table name matches also in a binary
+	string comparison. On Unix, MySQL allows table names that only differ
+	in character case. */
+
+	if (0 != ut_memcmp(field, table_name, len)) {
+
+		goto next_rec;
+	}
 		
 	if (rec_get_deleted_flag(rec)) {
 
diff --git a/innobase/eval/eval0eval.c b/innobase/eval/eval0eval.c
index ebb6cb1b7d9ff497979ed3101d4bd3ca25bfe18b..5b2d1f857b18c6636e25ea436dc91da623e03263 100644
--- a/innobase/eval/eval0eval.c
+++ b/innobase/eval/eval0eval.c
@@ -627,7 +627,11 @@ eval_concat(
 }
 
 /*********************************************************************
-Evaluates a predefined function node. */
+Evaluates a predefined function node. If the first argument is an integer,
+this function looks at the second argument which is the integer length in
+bytes, and converts the integer to a VARCHAR.
+If the first argument is of some other type, this function converts it to
+BINARY. */
 UNIV_INLINE
 void
 eval_to_binary(
@@ -638,12 +642,24 @@ eval_to_binary(
 	que_node_t*	arg2;
 	dfield_t*	dfield;
 	byte*		str1;
+	ulint		len;
 	ulint		len1;
 
 	arg1 = func_node->args;
 
 	str1 = dfield_get_data(que_node_get_val(arg1));
 
+	if (dtype_get_mtype(que_node_get_data_type(arg1)) != DATA_INT) {
+
+		len = dfield_get_len(que_node_get_val(arg1));
+
+		dfield = que_node_get_val(func_node);
+
+		dfield_set_data(dfield, str1, len);
+
+		return;
+	}
+
 	arg2 = que_node_get_next(arg1);
 	
 	len1 = (ulint)eval_node_get_int_val(arg2);
diff --git a/innobase/pars/pars0pars.c b/innobase/pars/pars0pars.c
index a4124672df01dacd2fa4cf4196c4404a420084de..5be0e52d0c8f8b510499b7180d2f43623e34bc75 100644
--- a/innobase/pars/pars0pars.c
+++ b/innobase/pars/pars0pars.c
@@ -259,9 +259,13 @@ pars_resolve_func_data_type(
 		dtype_set(que_node_get_data_type(node), DATA_VARCHAR,
 							DATA_ENGLISH, 0, 0);
 	} else if (func == PARS_TO_BINARY_TOKEN) {
-		ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT);
-		dtype_set(que_node_get_data_type(node), DATA_VARCHAR,
+		if (dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT) {
+			dtype_set(que_node_get_data_type(node), DATA_VARCHAR,
 							DATA_ENGLISH, 0, 0);
+		} else {
+			dtype_set(que_node_get_data_type(node), DATA_BINARY,
+								0, 0, 0);
+		}
 	} else if (func == PARS_TO_NUMBER_TOKEN) {
 		ut_a(dtype_get_mtype(que_node_get_data_type(arg))
 							== DATA_VARCHAR);
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 78b2aa8e28faf43a14711a6acf2ad4a450938f1c..2e8f7121d2c78d7fc8cb9401bfe21d8b176a6ef2 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -1981,7 +1981,8 @@ row_drop_table_for_mysql(
 	"WHILE found = 1 LOOP\n"
 	"	SELECT ID INTO foreign_id\n"
 	"	FROM SYS_FOREIGN\n"
-	"	WHERE FOR_NAME = table_name;\n"	
+	"	WHERE FOR_NAME = table_name\n"
+        "             AND TO_BINARY(FOR_NAME) = TO_BINARY(table_name);\n"
 	"	IF (SQL % NOTFOUND) THEN\n"
 	"		found := 0;\n"
 	"	ELSE"
@@ -2381,7 +2382,8 @@ row_rename_table_for_mysql(
 	"WHILE found = 1 LOOP\n"
 	"	SELECT ID INTO foreign_id\n"
 	"	FROM SYS_FOREIGN\n"
-	"	WHERE FOR_NAME = old_table_name;\n"	
+	"	WHERE FOR_NAME = old_table_name\n"
+	"	      AND TO_BINARY(FOR_NAME) = TO_BINARY(old_table_name);\n"
 	"	IF (SQL % NOTFOUND) THEN\n"
 	"	 found := 0;\n"
 	"	ELSE\n"
@@ -2414,7 +2416,8 @@ row_rename_table_for_mysql(
 	"	END IF;\n"
 	"END LOOP;\n"
 	"UPDATE SYS_FOREIGN SET REF_NAME = new_table_name\n"
-	"WHERE REF_NAME = old_table_name;\n";
+	"WHERE REF_NAME = old_table_name\n"
+	"      AND TO_BINARY(REF_NAME) = TO_BINARY(old_table_name);\n";
 	static const char str5[] =
 	"END;\n";
 
@@ -2602,7 +2605,11 @@ row_rename_table_for_mysql(
 		if (err == DB_DUPLICATE_KEY) {
 	    		ut_print_timestamp(stderr);
 
-			fputs("  InnoDB: Error: table ", stderr);
+			fputs(
+     "  InnoDB: Error; possible reasons:\n"
+     "InnoDB: 1) Table rename would cause two FOREIGN KEY constraints\n"
+     "InnoDB: to have the same internal name in case-insensitive comparison.\n"
+     "InnoDB: 2) table ", stderr);
 			ut_print_name(stderr, new_name);
 			fputs(" exists in the InnoDB internal data\n"
      "InnoDB: dictionary though MySQL is trying rename table ", stderr);