diff -r 9f28f5888574 innobase/dict/dict0boot.c
--- a/innobase/dict/dict0boot.c	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/dict/dict0boot.c	Mon Feb 02 10:27:17 2009 -0800
@@ -247,6 +247,7 @@
 	system tables */
 	/*-------------------------*/
 	table = dict_mem_table_create("SYS_TABLES", DICT_HDR_SPACE, 8, FALSE);
+	table->n_mysql_handles_opened = 1; /* for pin */
 
 	dict_mem_table_add_col(table, "NAME", DATA_BINARY, 0, 0, 0);
 	dict_mem_table_add_col(table, "ID", DATA_BINARY, 0, 0, 0);
@@ -283,6 +284,7 @@
 	ut_a(success);
 	/*-------------------------*/
 	table = dict_mem_table_create("SYS_COLUMNS", DICT_HDR_SPACE, 7, FALSE);
+	table->n_mysql_handles_opened = 1; /* for pin */
 
 	dict_mem_table_add_col(table, "TABLE_ID", DATA_BINARY,0,0,0);
 	dict_mem_table_add_col(table, "POS", DATA_INT, 0, 4, 0);
@@ -309,6 +311,7 @@
 	ut_a(success);
 	/*-------------------------*/
 	table = dict_mem_table_create("SYS_INDEXES", DICT_HDR_SPACE, 7, FALSE);
+	table->n_mysql_handles_opened = 1; /* for pin */
 
 	dict_mem_table_add_col(table, "TABLE_ID", DATA_BINARY, 0,0,0);
 	dict_mem_table_add_col(table, "ID", DATA_BINARY, 0, 0, 0);
@@ -345,6 +348,7 @@
 	ut_a(success);
 	/*-------------------------*/
 	table = dict_mem_table_create("SYS_FIELDS", DICT_HDR_SPACE, 3, FALSE);
+	table->n_mysql_handles_opened = 1; /* for pin */
 
 	dict_mem_table_add_col(table, "INDEX_ID", DATA_BINARY, 0,0,0);
 	dict_mem_table_add_col(table, "POS", DATA_INT, 0, 4, 0);
diff -r 9f28f5888574 innobase/dict/dict0crea.c
--- a/innobase/dict/dict0crea.c	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/dict/dict0crea.c	Mon Feb 02 10:27:17 2009 -0800
@@ -1178,6 +1178,9 @@
             	/* Foreign constraint system tables have already been
             	created, and they are ok */
 
+		table1->n_mysql_handles_opened = 1; /* for pin */
+		table2->n_mysql_handles_opened = 1; /* for pin */
+
 		mutex_exit(&(dict_sys->mutex));
 
             	return(DB_SUCCESS);
@@ -1265,6 +1268,11 @@
 	que_graph_free(graph);
 	
 	trx->op_info = "";
+
+	table1 = dict_table_get_low("SYS_FOREIGN");
+	table2 = dict_table_get_low("SYS_FOREIGN_COLS");
+	table1->n_mysql_handles_opened = 1; /* for pin */
+	table2->n_mysql_handles_opened = 1; /* for pin */
 
 	row_mysql_unlock_data_dictionary(trx);
 
diff -r 9f28f5888574 innobase/dict/dict0dict.c
--- a/innobase/dict/dict0dict.c	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/dict/dict0dict.c	Mon Feb 02 10:27:17 2009 -0800
@@ -638,6 +638,8 @@
 	mutex_enter(&(dict_sys->mutex));
 
 	table = dict_table_get_on_id_low(table_id, trx);
+
+	dict_table_LRU_trim(table);
 	
 	mutex_exit(&(dict_sys->mutex));
 
@@ -752,6 +754,8 @@
 	
 	table = dict_table_get_low(table_name);
 
+	dict_table_LRU_trim(table);
+
 	mutex_exit(&(dict_sys->mutex));
 
 	if (table != NULL) {
@@ -786,6 +790,8 @@
 
 	        table->n_mysql_handles_opened++;
 	}
+
+	dict_table_LRU_trim(table);
 
 	mutex_exit(&(dict_sys->mutex));
 
@@ -1267,20 +1273,64 @@
 too much space. Currently not used! */
 
 void
-dict_table_LRU_trim(void)
-/*=====================*/
+dict_table_LRU_trim(
+/*================*/
+	dict_table_t*	self)
 {
 	dict_table_t*	table;
 	dict_table_t*	prev_table;
-
-	ut_error;
-
-#ifdef UNIV_SYNC_DEBUG
-	ut_ad(mutex_own(&(dict_sys->mutex)));
-#endif /* UNIV_SYNC_DEBUG */
-
+	dict_foreign_t*	foreign;
+	ulint		n_removed;
+	ulint		n_have_parent;
+	ulint		cached_foreign_tables;
+
+	//ut_error;
+
+#ifdef UNIV_SYNC_DEBUG
+	ut_ad(mutex_own(&(dict_sys->mutex)));
+#endif /* UNIV_SYNC_DEBUG */
+
+retry:
+	n_removed = n_have_parent = 0;
 	table = UT_LIST_GET_LAST(dict_sys->table_LRU);
 
+	while ( srv_dict_size_limit && table
+		&& ((dict_sys->table_hash->n_cells
+		     + dict_sys->table_id_hash->n_cells
+		     + dict_sys->col_hash->n_cells) * sizeof(hash_cell_t)
+		    + dict_sys->size) > srv_dict_size_limit ) {
+		prev_table = UT_LIST_GET_PREV(table_LRU, table);
+
+		if (table == self || table->n_mysql_handles_opened)
+			goto next_loop;
+
+		cached_foreign_tables = 0;
+		foreign = UT_LIST_GET_FIRST(table->foreign_list);
+		while (foreign != NULL) {
+			if (foreign->referenced_table)
+				cached_foreign_tables++;
+			foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
+		}
+
+		/* TODO: use table->mem_fix also, if it becomes exact. */
+
+		if (cached_foreign_tables == 0) {
+			dict_table_remove_from_cache(table);
+			n_removed++;
+		} else {
+			n_have_parent++;
+		}
+next_loop:
+		table = prev_table;
+	}
+
+	if ( srv_dict_size_limit && n_have_parent && n_removed
+		&& ((dict_sys->table_hash->n_cells
+		     + dict_sys->table_id_hash->n_cells
+		     + dict_sys->col_hash->n_cells) * sizeof(hash_cell_t)
+		    + dict_sys->size) > srv_dict_size_limit )
+		goto retry;
+/*
 	while (table && (dict_sys->size >
 			 buf_pool_get_max_size() / DICT_POOL_PER_VARYING)) {
 
@@ -1292,6 +1342,7 @@
 
 		table = prev_table;
 	}
+*/
 }
 
 /**************************************************************************
diff -r 9f28f5888574 innobase/ibuf/ibuf0ibuf.c
--- a/innobase/ibuf/ibuf0ibuf.c	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/ibuf/ibuf0ibuf.c	Mon Feb 02 10:27:17 2009 -0800
@@ -534,6 +534,7 @@
 	sprintf(buf, "SYS_IBUF_TABLE_%lu", (ulong) space);
 	/* use old-style record format for the insert buffer */
 	table = dict_mem_table_create(buf, space, 2, FALSE);
+	table->n_mysql_handles_opened = 1; /* for pin */
 
 	dict_mem_table_add_col(table, "PAGE_NO", DATA_BINARY, 0, 0, 0);
 	dict_mem_table_add_col(table, "TYPES", DATA_BINARY, 0, 0, 0);
diff -r 9f28f5888574 innobase/include/dict0dict.h
--- a/innobase/include/dict0dict.h	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/include/dict0dict.h	Mon Feb 02 10:27:17 2009 -0800
@@ -938,6 +938,11 @@
 	const char*	ptr,	/* in: scan from */
 	const char*	string);/* in: look for this */
 
+void
+dict_table_LRU_trim(
+/*================*/
+	dict_table_t*	self);
+
 /* Buffers for storing detailed information about the latest foreign key
 and unique key errors */
 extern FILE*	dict_foreign_err_file;
diff -r 9f28f5888574 innobase/include/dict0dict.ic
--- a/innobase/include/dict0dict.ic	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/include/dict0dict.ic	Mon Feb 02 10:27:17 2009 -0800
@@ -533,6 +533,13 @@
 
 	HASH_SEARCH(name_hash, dict_sys->table_hash, table_fold, table,
 				ut_strcmp(table->name, table_name) == 0);
+
+	/* make young in table_LRU */
+	if (table) {
+		UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table);
+		UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table);
+	}
+
 	return(table);
 }
 
@@ -592,6 +599,10 @@
 	if (table != NULL) {
 		table->mem_fix++;
 
+		/* make young in table_LRU */
+		UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table);
+		UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table);
+
 		/* lock_push(trx, table, LOCK_DICT_MEM_FIX) */
 	}
 	
diff -r 9f28f5888574 innobase/include/srv0srv.h
--- a/innobase/include/srv0srv.h	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/include/srv0srv.h	Mon Feb 02 10:27:17 2009 -0800
@@ -143,6 +143,8 @@
 extern ibool    srv_print_innodb_tablespace_monitor;
 extern ibool    srv_print_verbose_log;
 extern ibool    srv_print_innodb_table_monitor;
+
+extern ulint	srv_dict_size_limit;
 
 extern ibool	srv_lock_timeout_and_monitor_active;
 extern ibool	srv_error_monitor_active; 
@@ -526,6 +528,7 @@
         ulint innodb_data_writes;
         ulint innodb_data_written;
         ulint innodb_data_reads;
+        ulint innodb_dict_tables;
         ulint innodb_buffer_pool_pages_total;
         ulint innodb_buffer_pool_pages_data;
         ulint innodb_buffer_pool_pages_dirty;
diff -r 9f28f5888574 innobase/srv/srv0srv.c
--- a/innobase/srv/srv0srv.c	Mon Feb 02 10:23:55 2009 -0800
+++ b/innobase/srv/srv0srv.c	Mon Feb 02 10:27:17 2009 -0800
@@ -343,6 +343,8 @@
 static ulint	srv_n_rows_updated_old		= 0;
 static ulint	srv_n_rows_deleted_old		= 0;
 static ulint	srv_n_rows_read_old		= 0;
+
+ulint	srv_dict_size_limit = 0;
 
 ulint		srv_n_lock_wait_count		= 0;
 ulint		srv_n_lock_wait_current_count	= 0;
@@ -1875,6 +1877,7 @@
         export_vars.innodb_data_reads= os_n_file_reads;
         export_vars.innodb_data_writes= os_n_file_writes;
         export_vars.innodb_data_written= srv_data_written;
+        export_vars.innodb_dict_tables= (dict_sys ? UT_LIST_GET_LEN(dict_sys->table_LRU) : 0);
         export_vars.innodb_buffer_pool_read_requests= buf_pool->n_page_gets;
         export_vars.innodb_buffer_pool_write_requests= srv_buf_pool_write_requests;
         export_vars.innodb_buffer_pool_wait_free= srv_buf_pool_wait_free;
diff -r 9f28f5888574 mysql-test/r/innodb_dict_size_limit.result
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mysql-test/r/innodb_dict_size_limit.result	Mon Feb 02 10:27:17 2009 -0800
@@ -0,0 +1,60 @@
+DROP TABLE IF EXISTS `test_5`;
+DROP TABLE IF EXISTS `test_4`;
+DROP TABLE IF EXISTS `test_3`;
+DROP TABLE IF EXISTS `test_2`;
+DROP TABLE IF EXISTS `test_1`;
+SET storage_engine=InnoDB;
+SET GLOBAL innodb_dict_size_limit=1;
+FLUSH TABLES;
+CREATE TABLE `test_1` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_2` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_3` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_4` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_5` (`a` int, `b` int, PRIMARY KEY  (`a`));
+ALTER TABLE `test_5` ADD CONSTRAINT FOREIGN KEY(`b`) REFERENCES `test_4`(`a`);
+ALTER TABLE `test_4` ADD CONSTRAINT FOREIGN KEY(`b`) REFERENCES `test_3`(`a`);
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	9
+FLUSH TABLES;
+SELECT * FROM `test_1`;
+a	b
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	8
+SELECT * FROM `test_3`;
+a	b
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	11
+FLUSH TABLES;
+SELECT * FROM `test_2`;
+a	b
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	8
+SELECT * FROM `test_1`;
+a	b
+FLUSH TABLES;
+SELECT * FROM `test_4`;
+a	b
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	9
+SELECT * FROM `test_3`;
+a	b
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	10
+SET GLOBAL innodb_dict_size_limit=0;
+FLUSH TABLES;
+SELECT * FROM `test_2`;
+a	b
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+Variable_name	Value
+Innodb_dict_tables	11
+DROP TABLE `test_5`;
+DROP TABLE `test_4`;
+DROP TABLE `test_3`;
+DROP TABLE `test_2`;
+DROP TABLE `test_1`;
diff -r 9f28f5888574 mysql-test/t/innodb_dict_size_limit.test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mysql-test/t/innodb_dict_size_limit.test	Mon Feb 02 10:27:17 2009 -0800
@@ -0,0 +1,63 @@
+#
+# Test for new variable innodb_dict_size_limit;
+#
+-- source include/have_innodb.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS `test_5`;
+DROP TABLE IF EXISTS `test_4`;
+DROP TABLE IF EXISTS `test_3`;
+DROP TABLE IF EXISTS `test_2`;
+DROP TABLE IF EXISTS `test_1`;
+--enable_warnings
+
+SET storage_engine=InnoDB;
+SET GLOBAL innodb_dict_size_limit=1;
+
+FLUSH TABLES;
+
+CREATE TABLE `test_1` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_2` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_3` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_4` (`a` int, `b` int, PRIMARY KEY  (`a`));
+CREATE TABLE `test_5` (`a` int, `b` int, PRIMARY KEY  (`a`));
+
+ALTER TABLE `test_5` ADD CONSTRAINT FOREIGN KEY(`b`) REFERENCES `test_4`(`a`);
+ALTER TABLE `test_4` ADD CONSTRAINT FOREIGN KEY(`b`) REFERENCES `test_3`(`a`);
+
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+FLUSH TABLES;
+SELECT * FROM `test_1`;
+
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+SELECT * FROM `test_3`;
+
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+FLUSH TABLES;
+SELECT * FROM `test_2`;
+
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+SELECT * FROM `test_1`;
+FLUSH TABLES;
+SELECT * FROM `test_4`;
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+SELECT * FROM `test_3`;
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+SET GLOBAL innodb_dict_size_limit=0;
+FLUSH TABLES;
+SELECT * FROM `test_2`;
+
+SHOW GLOBAL STATUS LIKE 'Innodb_dict_tables';
+
+DROP TABLE `test_5`;
+DROP TABLE `test_4`;
+DROP TABLE `test_3`;
+DROP TABLE `test_2`;
+DROP TABLE `test_1`;
+
diff -r 9f28f5888574 patch_info/innodb_dict_size_limit.info
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patch_info/innodb_dict_size_limit.info	Mon Feb 02 10:27:17 2009 -0800
@@ -0,0 +1,9 @@
+File=innodb_dict_size_limit.patch
+Name=Limit dictionary cache size
+Version=1.0
+Author=Percona
+License=GPL
+Comment=Variable innodb_dict_size_limit in bytes
+ChangeLog=
+2009-01-26
+YK: Initial release
diff -r 9f28f5888574 sql/ha_innodb.cc
--- a/sql/ha_innodb.cc	Mon Feb 02 10:23:55 2009 -0800
+++ b/sql/ha_innodb.cc	Mon Feb 02 10:27:17 2009 -0800
@@ -284,6 +284,8 @@
   (char*) &export_vars.innodb_dblwr_pages_written,        SHOW_LONG},
   {"dblwr_writes",
   (char*) &export_vars.innodb_dblwr_writes,               SHOW_LONG},
+  {"dict_tables",
+  (char*) &export_vars.innodb_dict_tables,                SHOW_LONG},
   {"log_waits",
   (char*) &export_vars.innodb_log_waits,                  SHOW_LONG},
   {"log_write_requests",
diff -r 9f28f5888574 sql/ha_innodb.h
--- a/sql/ha_innodb.h	Mon Feb 02 10:23:55 2009 -0800
+++ b/sql/ha_innodb.h	Mon Feb 02 10:27:17 2009 -0800
@@ -234,6 +234,7 @@
 extern ulong srv_thread_concurrency;
 extern ulong srv_commit_concurrency;
 extern ulong srv_flush_log_at_trx_commit;
+extern ulong srv_dict_size_limit;
 }
 
 bool innobase_init(void);
diff -r 9f28f5888574 sql/mysqld.cc
--- a/sql/mysqld.cc	Mon Feb 02 10:23:55 2009 -0800
+++ b/sql/mysqld.cc	Mon Feb 02 10:27:17 2009 -0800
@@ -5001,6 +5001,7 @@
   OPT_SECURE_FILE_PRIV,
   OPT_KEEP_FILES_ON_CREATE,
   OPT_INNODB_ADAPTIVE_HASH_INDEX,
+  OPT_INNODB_DICT_SIZE_LIMIT,
   OPT_FEDERATED
 };
 
@@ -5308,6 +5309,10 @@
    (gptr*) &global_system_variables.innodb_table_locks,
    (gptr*) &global_system_variables.innodb_table_locks,
    0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+  {"innodb_dict_size_limit", OPT_INNODB_DICT_SIZE_LIMIT,
+   "Limit the allocated memory for dictionary cache. (0: unlimited)",
+   (gptr*) &srv_dict_size_limit, (gptr*) &srv_dict_size_limit, 0,
+   GET_ULONG, REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 0 ,0},
 #endif /* End HAVE_INNOBASE_DB */
   {"isam", OPT_ISAM, "Obsolete. ISAM storage engine is no longer supported.",
    (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, 0, 0, 0,
diff -r 9f28f5888574 sql/set_var.cc
--- a/sql/set_var.cc	Mon Feb 02 10:23:55 2009 -0800
+++ b/sql/set_var.cc	Mon Feb 02 10:27:17 2009 -0800
@@ -454,6 +454,8 @@
 							&srv_auto_extend_increment);
 sys_var_long_ptr	sys_innodb_sync_spin_loops("innodb_sync_spin_loops",
                                              &srv_n_spin_wait_rounds);
+sys_var_long_ptr	sys_innodb_dict_size_limit("innodb_dict_size_limit",
+                                                   &srv_dict_size_limit);
 sys_var_long_ptr  sys_innodb_concurrency_tickets("innodb_concurrency_tickets",
                                              &srv_n_free_tickets_to_enter);
 sys_var_long_ptr  sys_innodb_thread_sleep_delay("innodb_thread_sleep_delay",
@@ -817,6 +819,7 @@
   &sys_innodb_autoextend_increment,
   &sys_innodb_sync_spin_loops,
   &sys_innodb_concurrency_tickets,
+  &sys_innodb_dict_size_limit,
   &sys_innodb_thread_sleep_delay,
   &sys_innodb_thread_concurrency,
   &sys_innodb_commit_concurrency,
@@ -932,6 +935,7 @@
   {sys_innodb_data_home_dir.name, (char*) &sys_innodb_data_home_dir, SHOW_SYS},
   {"innodb_adaptive_hash_index", (char*) &innobase_adaptive_hash_index, SHOW_MY_BOOL},
   {"innodb_doublewrite", (char*) &innobase_use_doublewrite, SHOW_MY_BOOL},
+  {sys_innodb_dict_size_limit.name, (char*) &sys_innodb_dict_size_limit, SHOW_SYS},
   {sys_innodb_fast_shutdown.name,(char*) &sys_innodb_fast_shutdown, SHOW_SYS},
   {"innodb_file_io_threads", (char*) &innobase_file_io_threads, SHOW_LONG },
   {"innodb_file_per_table", (char*) &innobase_file_per_table, SHOW_MY_BOOL},
