List:Commits« Previous MessageNext Message »
From:Elliot Murphy Date:April 18 2006 10:54pm
Subject:bk commit into 5.1 tree (elliot:1.2368)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of emurphy. When emurphy does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.2368 06/04/18 18:54:12 elliot@stripped +9 -0
  Merge mysql.com:/home/emurphy/src/mysql/bk-clean/tmp_merge
  into  mysql.com:/home/emurphy/src/mysql/bk-clean/mysql-5.1-new

  sql/sql_prepare.cc
    1.167 06/04/18 01:49:54 elliot@stripped +0 -0
    Auto merged

  sql/sql_class.h
    1.294 06/04/18 01:49:54 elliot@stripped +0 -0
    Auto merged

  sql/sql_class.cc
    1.254 06/04/18 01:49:54 elliot@stripped +0 -0
    Auto merged

  sql/mysqld.cc
    1.557 06/04/18 01:49:54 elliot@stripped +0 -0
    Auto merged

  sql/mysql_priv.h
    1.392 06/04/18 01:49:53 elliot@stripped +0 -0
    Auto merged

  sql/item_cmpfunc.cc
    1.202 06/04/18 01:49:53 elliot@stripped +0 -0
    Auto merged

  mysql-test/t/ps.test
    1.62 06/04/18 01:49:53 elliot@stripped +0 -0
    Auto merged

  mysql-test/r/ps.result
    1.64 06/04/18 01:49:53 elliot@stripped +0 -0
    Auto merged

  mysql-test/r/func_time.result
    1.50 06/04/18 01:49:53 elliot@stripped +0 -0
    Auto merged

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	elliot
# Host:	xan.(none)
# Root:	/home/emurphy/src/mysql/bk-clean/mysql-5.1-new/RESYNC

--- 1.201/sql/item_cmpfunc.cc	2006-04-10 17:25:19 -04:00
+++ 1.202/sql/item_cmpfunc.cc	2006-04-18 01:49:53 -04:00
@@ -52,7 +52,6 @@
 {
   uint i;
   Field *field= NULL;
-  bool all_constant= TRUE;
 
   /* If the first argument is a FIELD_ITEM, pull out the field. */
   if (items[0]->real_item()->type() == Item::FIELD_ITEM)
@@ -65,16 +64,9 @@
   for (i= 1; i < nitems; i++)
   {
     type[0]= item_cmp_type(type[0], items[i]->result_type());
-    if (field && !convert_constant_item(thd, field, &items[i]))
-      all_constant= FALSE;
+    if (field && convert_constant_item(thd, field, &items[i]))
+      type[0]= INT_RESULT;
   }
-
-  /*
-    If we had a field that can be compared as a longlong, and all constant
-    items, then the aggregate result will be an INT_RESULT.
-  */
-  if (field && all_constant)
-    type[0]= INT_RESULT;
 }
 
 

--- 1.391/sql/mysql_priv.h	2006-04-15 21:17:25 -04:00
+++ 1.392/sql/mysql_priv.h	2006-04-18 01:49:53 -04:00
@@ -1296,6 +1296,7 @@
 extern uint max_user_connections;
 extern ulong what_to_log,flush_time;
 extern ulong query_buff_size, thread_stack;
+extern ulong max_prepared_stmt_count, prepared_stmt_count;
 extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
 extern ulong max_binlog_size, max_relay_log_size;
 #ifdef HAVE_ROW_BASED_REPLICATION
@@ -1350,6 +1351,7 @@
        LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
        LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
        LOCK_global_system_variables, LOCK_user_conn,
+       LOCK_prepared_stmt_count,
        LOCK_bytes_sent, LOCK_bytes_received;
 #ifdef HAVE_OPENSSL
 extern pthread_mutex_t LOCK_des_key_file;

--- 1.556/sql/mysqld.cc	2006-04-17 13:06:12 -04:00
+++ 1.557/sql/mysqld.cc	2006-04-18 01:49:54 -04:00
@@ -500,6 +500,22 @@
 ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
 ulong max_connections, max_connect_errors;
 uint  max_user_connections= 0;
+/*
+  Limit of the total number of prepared statements in the server.
+  Is necessary to protect the server against out-of-memory attacks.
+*/
+ulong max_prepared_stmt_count;
+/*
+  Current total number of prepared statements in the server. This number
+  is exact, and therefore may not be equal to the difference between
+  `com_stmt_prepare' and `com_stmt_close' (global status variables), as
+  the latter ones account for all registered attempts to prepare
+  a statement (including unsuccessful ones).  Prepared statements are
+  currently connection-local: if the same SQL query text is prepared in
+  two different connections, this counts as two distinct prepared
+  statements.
+*/
+ulong prepared_stmt_count=0;
 ulong thread_id=1L,current_pid;
 ulong slow_launch_threads = 0, sync_binlog_period;
 ulong expire_logs_days = 0;
@@ -577,6 +593,14 @@
 		LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
 	        LOCK_global_system_variables,
 		LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
+/*
+  The below lock protects access to two global server variables:
+  max_prepared_stmt_count and prepared_stmt_count. These variables
+  set the limit and hold the current total number of prepared statements
+  in the server, respectively. As PREPARE/DEALLOCATE rate in a loaded
+  server may be fairly high, we need a dedicated lock.
+*/
+pthread_mutex_t LOCK_prepared_stmt_count;
 #ifdef HAVE_OPENSSL
 pthread_mutex_t LOCK_des_key_file;
 #endif
@@ -1288,6 +1312,7 @@
   (void) pthread_mutex_destroy(&LOCK_global_system_variables);
   (void) pthread_mutex_destroy(&LOCK_global_read_lock);
   (void) pthread_mutex_destroy(&LOCK_uuid_generator);
+  (void) pthread_mutex_destroy(&LOCK_prepared_stmt_count);
   (void) pthread_cond_destroy(&COND_thread_count);
   (void) pthread_cond_destroy(&COND_refresh);
   (void) pthread_cond_destroy(&COND_thread_cache);
@@ -2810,6 +2835,7 @@
   (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST);
   (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
   (void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
   (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST);
 #ifdef HAVE_OPENSSL
   (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
@@ -4634,7 +4660,8 @@
   OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
   OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS,
   OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE,
-  OPT_MAX_JOIN_SIZE, OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH, 
+  OPT_MAX_JOIN_SIZE, OPT_MAX_PREPARED_STMT_COUNT,
+  OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH,
   OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS,
   OPT_MAX_LENGTH_FOR_SORT_DATA,
   OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE,
@@ -5890,6 +5917,10 @@
     (gptr*) &global_system_variables.max_length_for_sort_data,
     (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG,
     REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0},
+  {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT,
+   "Maximum numbrer of prepared statements in the server.",
+   (gptr*) &max_prepared_stmt_count, (gptr*) &max_prepared_stmt_count,
+   0, GET_ULONG, REQUIRED_ARG, 16382, 0, 1*1024*1024, 0, 1, 0},
   {"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE,
    "If non-zero: relay log will be rotated automatically when the size exceeds this value; if zero (the default): when the size exceeds max_binlog_size. 0 excepted, the minimum value for this variable is 4096.",
    (gptr*) &max_relay_log_size, (gptr*) &max_relay_log_size, 0, GET_ULONG,

--- 1.253/sql/sql_class.cc	2006-04-09 09:48:43 -04:00
+++ 1.254/sql/sql_class.cc	2006-04-18 01:49:54 -04:00
@@ -447,7 +447,7 @@
     net_end(&net);
   }
 #endif
-  stmt_map.destroy();                     /* close all prepared statements */
+  stmt_map.reset();                     /* close all prepared statements */
   DBUG_ASSERT(lock_info.n_cursors == 0);
   if (!cleanup_done)
     cleanup();
@@ -1769,21 +1769,72 @@
 }
 
 
-int Statement_map::insert(Statement *statement)
+/*
+  Insert a new statement to the thread-local statement map.
+
+  DESCRIPTION
+    If there was an old statement with the same name, replace it with the
+    new one. Otherwise, check if max_prepared_stmt_count is not reached yet,
+    increase prepared_stmt_count, and insert the new statement. It's okay
+    to delete an old statement and fail to insert the new one.
+
+  POSTCONDITIONS
+    All named prepared statements are also present in names_hash.
+    Statement names in names_hash are unique.
+    The statement is added only if prepared_stmt_count < max_prepard_stmt_count
+    last_found_statement always points to a valid statement or is 0
+
+  RETURN VALUE
+    0  success
+    1  error: out of resources or max_prepared_stmt_count limit has been
+       reached. An error is sent to the client, the statement is deleted.
+*/
+
+int Statement_map::insert(THD *thd, Statement *statement)
 {
-  int res= my_hash_insert(&st_hash, (byte *) statement);
-  if (res)
-    return res;
-  if (statement->name.str)
+  if (my_hash_insert(&st_hash, (byte*) statement))
   {
-    if ((res= my_hash_insert(&names_hash, (byte*)statement)))
-    {
-      hash_delete(&st_hash, (byte*)statement);
-      return res;
-    }
+    /*
+      Delete is needed only in case of an insert failure. In all other
+      cases hash_delete will also delete the statement.
+    */
+    delete statement;
+    my_error(ER_OUT_OF_RESOURCES, MYF(0));
+    goto err_st_hash;
+  }
+  if (statement->name.str && my_hash_insert(&names_hash, (byte*) statement))
+  {
+    my_error(ER_OUT_OF_RESOURCES, MYF(0));
+    goto err_names_hash;
   }
+  pthread_mutex_lock(&LOCK_prepared_stmt_count);
+  /*
+    We don't check that prepared_stmt_count is <= max_prepared_stmt_count
+    because we would like to allow to lower the total limit
+    of prepared statements below the current count. In that case
+    no new statements can be added until prepared_stmt_count drops below
+    the limit.
+  */
+  if (prepared_stmt_count >= max_prepared_stmt_count)
+  {
+    pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+    my_error(ER_MAX_PREPARED_STMT_COUNT_REACHED, MYF(0),
+             max_prepared_stmt_count);
+    goto err_max;
+  }
+  prepared_stmt_count++;
+  pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+
   last_found_statement= statement;
-  return res;
+  return 0;
+
+err_max:
+  if (statement->name.str)
+    hash_delete(&names_hash, (byte*) statement);
+err_names_hash:
+  hash_delete(&st_hash, (byte*) statement);
+err_st_hash:
+  return 1;
 }
 
 
@@ -1796,6 +1847,47 @@
 #endif
 }
 
+
+void Statement_map::erase(Statement *statement)
+{
+  if (statement == last_found_statement)
+    last_found_statement= 0;
+  if (statement->name.str)
+    hash_delete(&names_hash, (byte *) statement);
+
+  hash_delete(&st_hash, (byte *) statement);
+  pthread_mutex_lock(&LOCK_prepared_stmt_count);
+  DBUG_ASSERT(prepared_stmt_count > 0);
+  prepared_stmt_count--;
+  pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+}
+
+
+void Statement_map::reset()
+{
+  /* Must be first, hash_free will reset st_hash.records */
+  pthread_mutex_lock(&LOCK_prepared_stmt_count);
+  DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
+  prepared_stmt_count-= st_hash.records;
+  pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+
+  my_hash_reset(&names_hash);
+  my_hash_reset(&st_hash);
+  last_found_statement= 0;
+}
+
+
+Statement_map::~Statement_map()
+{
+  /* Must go first, hash_free will reset st_hash.records */
+  pthread_mutex_lock(&LOCK_prepared_stmt_count);
+  DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
+  prepared_stmt_count-= st_hash.records;
+  pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+
+  hash_free(&names_hash);
+  hash_free(&st_hash);
+}
 
 bool select_dumpvar::send_data(List<Item> &items)
 {

--- 1.293/sql/sql_class.h	2006-04-01 02:38:28 -05:00
+++ 1.294/sql/sql_class.h	2006-04-18 01:49:54 -04:00
@@ -545,7 +545,7 @@
 public:
   Statement_map();
 
-  int insert(Statement *statement);
+  int insert(THD *thd, Statement *statement);
 
   Statement *find_by_name(LEX_STRING *name)
   {
@@ -567,36 +567,16 @@
     }
     return last_found_statement;
   }
-  void erase(Statement *statement)
-  {
-    if (statement == last_found_statement)
-      last_found_statement= 0;
-    if (statement->name.str)
-    {
-      hash_delete(&names_hash, (byte *) statement);  
-    }
-    hash_delete(&st_hash, (byte *) statement);
-  }
   /*
     Close all cursors of this connection that use tables of a storage
     engine that has transaction-specific state and therefore can not
     survive COMMIT or ROLLBACK. Currently all but MyISAM cursors are closed.
   */
   void close_transient_cursors();
+  void erase(Statement *statement);
   /* Erase all statements (calls Statement destructor) */
-  void reset()
-  {
-    my_hash_reset(&names_hash);
-    my_hash_reset(&st_hash);
-    transient_cursor_list.empty();
-    last_found_statement= 0;
-  }
-
-  void destroy()
-  {
-    hash_free(&names_hash);
-    hash_free(&st_hash);
-  }
+  void reset();
+  ~Statement_map();
 private:
   HASH st_hash;
   HASH names_hash;
@@ -1179,6 +1159,7 @@
   {
     my_bool my_bool_value;
     long    long_value;
+    ulong   ulong_value;
   } sys_var_tmp;
   
   struct {

--- 1.49/mysql-test/r/func_time.result	2006-04-07 05:23:48 -04:00
+++ 1.50/mysql-test/r/func_time.result	2006-04-18 01:49:53 -04:00
@@ -847,3 +847,28 @@
 select timestampdiff(year,'2004-02-29','2005-02-28');
 timestampdiff(year,'2004-02-29','2005-02-28')
 0
+CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, day date);
+CREATE TABLE t2 (id int NOT NULL PRIMARY KEY, day date);
+INSERT INTO t1 VALUES
+(1, '2005-06-01'), (2, '2005-02-01'), (3, '2005-07-01');
+INSERT INTO t2 VALUES
+(1, '2005-08-01'), (2, '2005-06-15'), (3, '2005-07-15');
+SELECT * FROM t1, t2 
+WHERE t1.day BETWEEN 
+'2005.09.01' - INTERVAL 6 MONTH AND t2.day;
+id	day	id	day
+1	2005-06-01	1	2005-08-01
+3	2005-07-01	1	2005-08-01
+1	2005-06-01	2	2005-06-15
+1	2005-06-01	3	2005-07-15
+3	2005-07-01	3	2005-07-15
+SELECT * FROM t1, t2 
+WHERE CAST(t1.day AS DATE) BETWEEN 
+'2005.09.01' - INTERVAL 6 MONTH AND t2.day;
+id	day	id	day
+1	2005-06-01	1	2005-08-01
+3	2005-07-01	1	2005-08-01
+1	2005-06-01	2	2005-06-15
+1	2005-06-01	3	2005-07-15
+3	2005-07-01	3	2005-07-15
+DROP TABLE t1,t2;

--- 1.63/mysql-test/r/ps.result	2006-03-30 10:07:56 -05:00
+++ 1.64/mysql-test/r/ps.result	2006-04-18 01:49:53 -04:00
@@ -108,6 +108,9 @@
 prepare stmt1 from @fvar;
 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '123.4567' at line 1
 drop table t1,t2;
+deallocate prepare stmt3;
+deallocate prepare stmt4;
+deallocate prepare stmt5;
 PREPARE stmt1 FROM "select _utf8 'A' collate utf8_bin = ?";
 set @var='A';
 EXECUTE stmt1 USING @var;
@@ -253,6 +256,7 @@
 1234
 1234
 set names default;
 create table t1 (a varchar(10)) charset=utf8;
 insert into t1 (a) values ('yahoo');
@@ -781,6 +785,7 @@
 1
 DROP VIEW b12651_V1;
 DROP TABLE b12651_T1, b12651_T2;
+DEALLOCATE PREPARE b12651;
 prepare stmt from "select @@time_zone";
 execute stmt;
 @@time_zone
@@ -873,6 +878,130 @@
 10
 drop table t1;
 deallocate prepare stmt;
+create table t1 (col1 integer, col2 integer);
+insert into t1 values(100,100),(101,101),(102,102),(103,103);
+prepare stmt from 'select col1, col2 from t1 where (col1, col2) in ((?,?))';
+set @a=100, @b=100;
+execute stmt using @a,@b;
+col1	col2
+100	100
+set @a=101, @b=101;
+execute stmt using @a,@b;
+col1	col2
+101	101
+set @a=102, @b=102;
+execute stmt using @a,@b;
+col1	col2
+102	102
+set @a=102, @b=103;
+execute stmt using @a,@b;
+col1	col2
+deallocate prepare stmt;
+drop table t1;
+set @old_max_prepared_stmt_count= @@max_prepared_stmt_count;
+show variables like 'max_prepared_stmt_count';
+Variable_name	Value
+max_prepared_stmt_count	16382
+show variables like 'prepared_stmt_count';
+Variable_name	Value
+prepared_stmt_count	0
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+@@max_prepared_stmt_count	@@prepared_stmt_count
+16382	0
+set global max_prepared_stmt_count=-1;
+select @@max_prepared_stmt_count;
+@@max_prepared_stmt_count
+0
+set global max_prepared_stmt_count=10000000000000000;
+select @@max_prepared_stmt_count;
+@@max_prepared_stmt_count
+1048576
+set global max_prepared_stmt_count=default;
+select @@max_prepared_stmt_count;
+@@max_prepared_stmt_count
+16382
+set @@max_prepared_stmt_count=1;
+ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL
+set max_prepared_stmt_count=1;
+ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL
+set local max_prepared_stmt_count=1;
+ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL
+set local prepared_stmt_count=0;
+ERROR HY000: Variable 'prepared_stmt_count' is a read only variable
+set @@prepared_stmt_count=0;
+ERROR HY000: Variable 'prepared_stmt_count' is a read only variable
+set global prepared_stmt_count=1;
+ERROR HY000: Variable 'prepared_stmt_count' is a read only variable
+set global max_prepared_stmt_count=1;
+select @@max_prepared_stmt_count;
+@@max_prepared_stmt_count
+1
+set global max_prepared_stmt_count=0;
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+@@max_prepared_stmt_count	@@prepared_stmt_count
+0	0
+prepare stmt from "select 1";
+ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0)
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+0
+set global max_prepared_stmt_count=1;
+prepare stmt from "select 1";
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+1
+prepare stmt1 from "select 1";
+ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 1)
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+1
+deallocate prepare stmt;
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+0
+prepare stmt from "select 1";
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+1
+prepare stmt from "select 2";
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+1
+select @@prepared_stmt_count, @@max_prepared_stmt_count;
+@@prepared_stmt_count	@@max_prepared_stmt_count
+1	1
+set global max_prepared_stmt_count=0;
+prepare stmt from "select 1";
+ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0)
+execute stmt;
+ERROR HY000: Unknown prepared statement handler (stmt) given to EXECUTE
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+0
+prepare stmt from "select 1";
+ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0)
+select @@prepared_stmt_count;
+@@prepared_stmt_count
+0
+set global max_prepared_stmt_count=3;
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+@@max_prepared_stmt_count	@@prepared_stmt_count
+3	0
+prepare stmt from "select 1";
+prepare stmt from "select 2";
+prepare stmt1 from "select 3";
+prepare stmt2 from "select 4";
+ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 3)
+prepare stmt2 from "select 4";
+ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 3)
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+@@max_prepared_stmt_count	@@prepared_stmt_count
+3	3
+deallocate prepare stmt;
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+@@max_prepared_stmt_count	@@prepared_stmt_count
+3	0
+set global max_prepared_stmt_count= @old_max_prepared_stmt_count;
 create table t1 (id int);
 prepare ins_call from "insert into t1 (id) values (1)";
 execute ins_call;
@@ -883,6 +1012,7 @@
 create table t1 (a int, b int);
 insert into t1 (a,b) values (2,8),(1,9),(3,7);
 prepare stmt from "select * from t1 order by ?";
+set @a=NULL;
 execute stmt using @a;
 a	b
 2	8

--- 1.61/mysql-test/t/ps.test	2006-03-30 10:07:57 -05:00
+++ 1.62/mysql-test/t/ps.test	2006-04-18 01:49:53 -04:00
@@ -114,6 +114,9 @@
 prepare stmt1 from @fvar;
 
 drop table t1,t2;
+deallocate prepare stmt3;
+deallocate prepare stmt4;
+deallocate prepare stmt5;
 
 #
 # Bug #4105: Server crash on attempt to prepare a statement with character
@@ -257,6 +260,7 @@
 execute `ü` ;
 set names latin1;
 set names default;
 
 
@@ -823,6 +827,7 @@
 
 DROP VIEW b12651_V1;
 DROP TABLE b12651_T1, b12651_T2;
+DEALLOCATE PREPARE b12651;
 
 #
 # Bug#9359 "Prepared statements take snapshot of system vars at PREPARE
@@ -921,6 +926,143 @@
 drop table t1;
 deallocate prepare stmt;
 
+#
+# Bug#16248 "WHERE (col1,col2) IN ((?,?)) gives wrong results":
+# check that ROW implementation is reexecution-friendly.
+#
+create table t1 (col1 integer, col2 integer);
+insert into t1 values(100,100),(101,101),(102,102),(103,103);
+prepare stmt from 'select col1, col2 from t1 where (col1, col2) in ((?,?))';
+set @a=100, @b=100;
+execute stmt using @a,@b;
+set @a=101, @b=101;
+execute stmt using @a,@b;
+set @a=102, @b=102;
+execute stmt using @a,@b;
+set @a=102, @b=103;
+execute stmt using @a,@b;
+deallocate prepare stmt;
+drop table t1;
+
+#
+# Bug#16365 Prepared Statements: DoS with too many open statements
+# Check that the limit @@max_prpeared_stmt_count works.
+#
+# Save the old value
+set @old_max_prepared_stmt_count= @@max_prepared_stmt_count;
+#
+# Disable prepared statement protocol: in this test we set
+# @@max_prepared_stmt_count to 0 or 1 and would like to test the limit
+# manually.
+#
+--disable_ps_protocol
+#
+# A. Check that the new variables are present in SHOW VARIABLES list.
+#
+show variables like 'max_prepared_stmt_count';
+show variables like 'prepared_stmt_count';
+#
+# B. Check that the new variables are selectable.
+#
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+#
+# C. Check that max_prepared_stmt_count is settable (global only),
+#    whereas prepared_stmt_count is readonly.
+#
+set global max_prepared_stmt_count=-1;
+select @@max_prepared_stmt_count;
+set global max_prepared_stmt_count=10000000000000000;
+select @@max_prepared_stmt_count;
+set global max_prepared_stmt_count=default;
+select @@max_prepared_stmt_count;
+--error ER_GLOBAL_VARIABLE
+set @@max_prepared_stmt_count=1;
+--error ER_GLOBAL_VARIABLE
+set max_prepared_stmt_count=1;
+--error ER_GLOBAL_VARIABLE
+set local max_prepared_stmt_count=1;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+set local prepared_stmt_count=0;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+set @@prepared_stmt_count=0;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR 
+set global prepared_stmt_count=1;
+# set to a reasonable limit works
+set global max_prepared_stmt_count=1;
+select @@max_prepared_stmt_count;
+#
+# D. Check that the variables actually work.
+#
+set global max_prepared_stmt_count=0;
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+--error ER_MAX_PREPARED_STMT_COUNT_REACHED
+prepare stmt from "select 1";
+select @@prepared_stmt_count;
+set global max_prepared_stmt_count=1;
+prepare stmt from "select 1";
+select @@prepared_stmt_count;
+--error ER_MAX_PREPARED_STMT_COUNT_REACHED
+prepare stmt1 from "select 1";
+select @@prepared_stmt_count;
+deallocate prepare stmt;
+select @@prepared_stmt_count;
+#
+# E. Check that we can prepare a statement with the same name 
+# successfully, without hitting the limit.
+#
+prepare stmt from "select 1";
+select @@prepared_stmt_count;
+prepare stmt from "select 2";
+select @@prepared_stmt_count;
+#
+# F. We can set the max below the current count. In this case no new 
+# statements should be allowed to prepare.
+#
+select @@prepared_stmt_count, @@max_prepared_stmt_count;
+set global max_prepared_stmt_count=0;
+--error ER_MAX_PREPARED_STMT_COUNT_REACHED
+prepare stmt from "select 1";
+# Result: the old statement is deallocated, the new is not created.
+--error 1243 # ER_UNKNOWN_STMT_HANDLER
+execute stmt;
+select @@prepared_stmt_count;
+--error ER_MAX_PREPARED_STMT_COUNT_REACHED
+prepare stmt from "select 1";
+select @@prepared_stmt_count;
+#
+# G. Show that the variables are up to date even after a connection with all
+# statements in it was terminated.
+#
+set global max_prepared_stmt_count=3;
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+prepare stmt from "select 1";
+connect (con1,localhost,root,,);
+connection con1;
+prepare stmt from "select 2";
+prepare stmt1 from "select 3";
+--error ER_MAX_PREPARED_STMT_COUNT_REACHED
+prepare stmt2 from "select 4";
+connection default;
+--error ER_MAX_PREPARED_STMT_COUNT_REACHED
+prepare stmt2 from "select 4";
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+disconnect con1;
+connection default;
+# Wait for the connection to die: deal with a possible race
+deallocate prepare stmt;
+let $count= `select @@prepared_stmt_count`;
+if ($count)
+{
+--sleep 2
+  let $count= `select @@prepared_stmt_count`;
+}
+select @@max_prepared_stmt_count, @@prepared_stmt_count;
+#
+# Restore the old value.
+#
+set global max_prepared_stmt_count= @old_max_prepared_stmt_count;
+--enable_ps_protocol
+
 # End of 4.1 tests
 
 #
@@ -946,6 +1088,7 @@
 
 # Will order by index
 prepare stmt from "select * from t1 order by ?";
+set @a=NULL;
 execute stmt using @a;
 set @a=1;
 execute stmt using @a;

--- 1.166/sql/sql_prepare.cc	2006-03-29 20:53:52 -05:00
+++ 1.167/sql/sql_prepare.cc	2006-04-18 01:49:54 -04:00
@@ -1848,10 +1848,13 @@
   if (! (stmt= new Prepared_statement(thd, &thd->protocol_prep)))
     DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
 
-  if (thd->stmt_map.insert(stmt))
+  if (thd->stmt_map.insert(thd, stmt))
   {
-    delete stmt;
-    DBUG_VOID_RETURN;                           /* out of memory */
+    /*
+      The error is set in the insert. The statement itself
+      will be also deleted there (this is how the hash works).
+    */
+    DBUG_VOID_RETURN;
   }
 
   /* Reset warnings from previous command */
@@ -2028,9 +2031,15 @@
     DBUG_VOID_RETURN;                           /* out of memory */
   }
 
-  if (stmt->set_name(name) || thd->stmt_map.insert(stmt))
+  /* Set the name first, insert should know that this statement has a name */
+  if (stmt->set_name(name))
   {
     delete stmt;
+    DBUG_VOID_RETURN;
+  }
+  if (thd->stmt_map.insert(thd, stmt))
+  {
+    /* The statement is deleted and an error is set if insert fails */
     DBUG_VOID_RETURN;
   }
 
Thread
bk commit into 5.1 tree (elliot:1.2368)Elliot Murphy19 Apr