diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index df5fa5fe99995e4c011ff98c4295cbfdf2f60c4b..0aba0c4672e503fa02941bdec97cc62f35ed00e9 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -336,3 +336,42 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 -	-	-	-	-	-	-	-	NULL	Impossible WHERE
 drop table t1;
 deallocate prepare stmt;
+create table t1 (a int);
+insert into t1 (a) values (1), (2), (3), (4);
+set @precision=10000000000;
+select rand(), 
+cast(rand(10)*@precision as unsigned integer),
+cast(rand(a)*@precision as unsigned integer) from t1;
+rand()	cast(rand(10)*@precision as unsigned integer)	cast(rand(a)*@precision as unsigned integer)
+-	6570515219	-
+-	1282061302	-
+-	6698761160	-
+-	9647622201	-
+prepare stmt from
+"select rand(), 
+        cast(rand(10)*@precision as unsigned integer),
+        cast(rand(a)*@precision as unsigned integer),
+        cast(rand(?)*@precision as unsigned integer) from t1";
+set @var=1;
+execute stmt using @var;
+rand()	cast(rand(10)*@precision as unsigned integer)	cast(rand(a)*@precision as unsigned integer)	cast(rand(?)*@precision as unsigned integer)
+-	6570515219	-	4054035371
+-	1282061302	-	8716141803
+-	6698761160	-	1418603212
+-	9647622201	-	944590960
+set @var=2;
+execute stmt using @var;
+rand()	cast(rand(10)*@precision as unsigned integer)	cast(rand(a)*@precision as unsigned integer)	cast(rand(?)*@precision as unsigned integer)
+-	6570515219	1559528654	6555866465
+-	1282061302	6238114970	1223466192
+-	6698761160	6511989195	6449731873
+-	9647622201	3845601374	8578261098
+set @var=3;
+execute stmt using @var;
+rand()	cast(rand(10)*@precision as unsigned integer)	cast(rand(a)*@precision as unsigned integer)	cast(rand(?)*@precision as unsigned integer)
+-	6570515219	1559528654	9057697559
+-	1282061302	6238114970	3730790581
+-	6698761160	6511989195	1480860534
+-	9647622201	3845601374	6211931236
+drop table t1;
+deallocate prepare stmt;
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 76c7fb7c2e721d9c56026fb136896e33f6451b81..7cbcd50245fceaef8e1e4e03404c3ec968718293 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -363,4 +363,30 @@ execute stmt using @v;
 drop table t1;
 deallocate prepare stmt;
 
-
+#
+# A test case for Bug#5985 prepare stmt from "select rand(?)" crashes
+# server. Check that Item_func_rand is prepared-statements friendly.
+#
+create table t1 (a int);
+insert into t1 (a) values (1), (2), (3), (4);
+set @precision=10000000000;
+--replace_column 1 - 3 -
+select rand(), 
+       cast(rand(10)*@precision as unsigned integer),
+       cast(rand(a)*@precision as unsigned integer) from t1;
+prepare stmt from
+"select rand(), 
+        cast(rand(10)*@precision as unsigned integer),
+        cast(rand(a)*@precision as unsigned integer),
+        cast(rand(?)*@precision as unsigned integer) from t1";
+set @var=1;
+--replace_column 1 - 3 -
+execute stmt using @var;
+set @var=2;
+--replace_column 1 -
+execute stmt using @var;
+set @var=3;
+--replace_column 1 -
+execute stmt using @var;
+drop table t1;
+deallocate prepare stmt;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index ccdde399e0ac7e346143939226c82755c164090b..f20d69bf2ad3a091632914f985341ef833b34f55 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1010,21 +1010,38 @@ double Item_func_round::val()
 }
 
 
-void Item_func_rand::fix_length_and_dec()
+bool Item_func_rand::fix_fields(THD *thd, struct st_table_list *tables,
+                                Item **ref)
 {
-  decimals=NOT_FIXED_DEC; 
-  max_length=float_length(decimals);
+  Item_real_func::fix_fields(thd, tables, ref);
   used_tables_cache|= RAND_TABLE_BIT;
   if (arg_count)
   {					// Only use argument once in query
-    uint32 tmp= (uint32) (args[0]->val_int());
-    if ((rand= (struct rand_struct*) sql_alloc(sizeof(*rand))))
-      randominit(rand,(uint32) (tmp*0x10001L+55555555L),
-		 (uint32) (tmp*0x10000001L));
+    /*
+      Allocate rand structure once: we must use thd->current_arena
+      to create rand in proper mem_root if it's a prepared statement or
+      stored procedure.
+    */
+    if (!rand && !(rand= (struct rand_struct*)
+                   thd->current_arena->alloc(sizeof(*rand))))
+      return TRUE;
+    /*
+      PARAM_ITEM is returned if we're in statement prepare and consequently
+      no placeholder value is set yet.
+    */
+    if (args[0]->type() != PARAM_ITEM)
+    {
+      /*
+        TODO: do not do reinit 'rand' for every execute of PS/SP if
+        args[0] is a constant.
+      */
+      uint32 tmp= (uint32) args[0]->val_int();
+      randominit(rand, (uint32) (tmp*0x10001L+55555555L),
+                 (uint32) (tmp*0x10000001L));
+    }
   }
   else
   {
-    THD *thd= current_thd;
     /*
       No need to send a Rand log event if seed was given eg: RAND(seed),
       as it will be replicated in the query as such.
@@ -1038,6 +1055,7 @@ void Item_func_rand::fix_length_and_dec()
     thd->rand_saved_seed2=thd->rand.seed2;
     rand= &thd->rand;
   }
+  return FALSE;
 }
 
 void Item_func_rand::update_used_tables()
diff --git a/sql/item_func.h b/sql/item_func.h
index 4889ca78a775bbfe1de032e4c699af0ef07322fc..e61459cfbd86503de57c4c53bfe8e0ad3ec4161f 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -512,13 +512,13 @@ class Item_func_rand :public Item_real_func
 {
   struct rand_struct *rand;
 public:
-  Item_func_rand(Item *a) :Item_real_func(a) {}
-  Item_func_rand()	  :Item_real_func()  {}
+  Item_func_rand(Item *a) :Item_real_func(a), rand(0) {}
+  Item_func_rand()	  :Item_real_func() {}
   double val();
   const char *func_name() const { return "rand"; }
   bool const_item() const { return 0; }
   void update_used_tables();
-  void fix_length_and_dec();
+  bool fix_fields(THD *thd, struct st_table_list *tables, Item **ref);
 };
 
 
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index e152cd9ea490f52b63674efb673d113c529dd01b..1a0879c6347a43cdf947a66782b61f3670b05f2a 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -447,6 +447,7 @@ bool is_update_query(enum enum_sql_command command);
 bool alloc_query(THD *thd, char *packet, ulong packet_length);
 void mysql_init_select(LEX *lex);
 void mysql_init_query(THD *thd, uchar *buf, uint length);
+void mysql_reset_thd_for_next_command(THD *thd);
 bool mysql_new_select(LEX *lex, bool move_down);
 void create_select_for_variable(const char *var_name);
 void mysql_init_multi_delete(LEX *lex);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 4ce655c78f8ad53b67bc5184ef06e7f0c7b4b69a..f1c75a3b365f1423cde61c5b01f660d5e14a1b8e 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1498,7 +1498,7 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup)
 }
 
 
-void Statement::end_statement()
+void THD::end_statement()
 {
   /* Cleanup SQL processing state to resuse this statement in next query. */
   lex_end(lex);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index aaa81fbe1658ff0ea2c15b5184366156947edabd..f25689aa5126a7f819c27e0f3d9fd30aa9c727e2 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -565,12 +565,6 @@ public:
   void restore_backup_statement(Statement *stmt, Statement *backup);
   /* return class type */
   virtual Type type() const;
-
-  /*
-    Cleanup statement parse state (parse tree, lex) after execution of
-    a non-prepared SQL statement.
-  */
-  void end_statement();
 };
 
 
@@ -1064,6 +1058,12 @@ public:
   void nocheck_register_item_tree_change(Item **place, Item *old_value,
                                          MEM_ROOT *runtime_memroot);
   void rollback_item_tree_changes();
+
+  /*
+    Cleanup statement parse state (parse tree, lex) and execution
+    state after execution of a non-prepared SQL statement.
+  */
+  void end_statement();
 };
 
 /* Flags for the THD::system_thread (bitmap) variable */
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 3deecccb4e181e7bfe94228d15b2943006c2f7d3..856742d13bcb79dd07dd560eae751808e225ec01 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -117,7 +117,30 @@ void lex_free(void)
 void lex_start(THD *thd, uchar *buf,uint length)
 {
   LEX *lex= thd->lex;
+  lex->unit.init_query();
+  lex->unit.init_select();
   lex->thd= thd;
+  lex->unit.thd= thd;
+  lex->select_lex.init_query();
+  lex->value_list.empty();
+  lex->param_list.empty();
+  lex->unit.next= lex->unit.master=
+    lex->unit.link_next= lex->unit.return_to= 0;
+  lex->unit.prev= lex->unit.link_prev= 0;
+  lex->unit.slave= lex->unit.global_parameters= lex->current_select=
+    lex->all_selects_list= &lex->select_lex;
+  lex->select_lex.master= &lex->unit;
+  lex->select_lex.prev= &lex->unit.slave;
+  lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
+  lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
+  lex->select_lex.options= 0;
+  lex->describe= 0;
+  lex->derived_tables= FALSE;
+  lex->lock_option= TL_READ;
+  lex->found_colon= 0;
+  lex->safe_to_cache_query= 1;
+  lex->time_zone_tables_used= 0;
+  lex->select_lex.select_number= 1;
   lex->next_state=MY_LEX_START;
   lex->end_of_query=(lex->ptr=buf)+length;
   lex->yylineno = 1;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index d198855a2d3958b775a6d110cd833b18a6ca32c8..38cdde019ffa099e9d3e2d241250ed8f7253703b 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -369,7 +369,7 @@ public:
   ulong init_prepare_fake_select_lex(THD *thd);
   int change_result(select_subselect *result, select_subselect *old_result);
 
-  friend void mysql_init_query(THD *thd, uchar *buf, uint length);
+  friend void lex_start(THD *thd, uchar *buf, uint length);
   friend int subselect_union_engine::exec();
 private:
   bool create_total_list_n_last_return(THD *thd, st_lex *lex,
@@ -508,7 +508,7 @@ public:
   
   bool test_limit();
 
-  friend void mysql_init_query(THD *thd, uchar *buf, uint length);
+  friend void lex_start(THD *thd, uchar *buf, uint length);
   st_select_lex() {}
   void make_empty_select()
   {
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index c5ca19ecba8f77c32b5c6bb94615b4ae12b86b46..c9c4ed35dc7c24e662de1ab0af9aacfe5ce17c08 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1001,16 +1001,15 @@ pthread_handler_decl(handle_one_connection,arg)
       net->compress=1;				// Use compression
 
     thd->version= refresh_version;
+    thd->proc_info= 0;
+    thd->set_time();
+    thd->init_for_queries();
     if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
     {
       execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
       if (thd->query_error)
 	thd->killed= 1;
     }
-
-    thd->proc_info=0;
-    thd->set_time();
-    thd->init_for_queries();
     while (!net->error && net->vio != 0 && !thd->killed)
     {
       if (do_command(thd))
@@ -3854,7 +3853,6 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
   return 0;
 }
 
-
 /****************************************************************************
   Initialize global thd variables needed for query
 ****************************************************************************/
@@ -3863,33 +3861,30 @@ void
 mysql_init_query(THD *thd, uchar *buf, uint length)
 {
   DBUG_ENTER("mysql_init_query");
-  LEX *lex= thd->lex;
-  lex->unit.init_query();
-  lex->unit.init_select();
-  lex->unit.thd= thd;
-  lex->select_lex.init_query();
-  lex->value_list.empty();
-  lex->param_list.empty();
-  lex->unit.next= lex->unit.master= 
-    lex->unit.link_next= lex->unit.return_to=0;
-  lex->unit.prev= lex->unit.link_prev= 0;
-  lex->unit.slave= lex->unit.global_parameters= lex->current_select=
-    lex->all_selects_list= &lex->select_lex;
-  lex->select_lex.master= &lex->unit;
-  lex->select_lex.prev= &lex->unit.slave;
-  lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
-  lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
-  lex->select_lex.options=0;
-  lex->describe= 0;
-  lex->derived_tables= FALSE;
-  lex->lock_option= TL_READ;
-  lex->found_colon= 0;
-  lex->safe_to_cache_query= 1;
-  lex->time_zone_tables_used= 0;
   lex_start(thd, buf, length);
-  thd->select_number= lex->select_lex.select_number= 1;
-  thd->free_list= 0;
-  thd->total_warn_count=0;			// Warnings for this query
+  mysql_reset_thd_for_next_command(thd);
+  DBUG_VOID_RETURN;
+}
+
+
+/*
+ Reset THD part responsible for command processing state.
+
+ DESCRIPTION
+   This needs to be called before execution of every statement
+   (prepared or conventional).
+
+ TODO
+   Make it a method of THD and align its name with the rest of
+   reset/end/start/init methods.
+   Call it after we use THD for queries, not before.
+*/
+
+void mysql_reset_thd_for_next_command(THD *thd)
+{
+  DBUG_ENTER("mysql_reset_thd_for_next_command");
+  thd->select_number= 1;
+  thd->total_warn_count= 0;                     // Warnings for this query
   thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
   thd->sent_row_count= thd->examined_row_count= 0;
   thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index a638e74dc2f8f39cf094c9bda8aa50c0fd43e398..27d98fdfebad225db18a49d40f49e19d54136012 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1760,6 +1760,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
     DBUG_VOID_RETURN;
   }
 
+  DBUG_ASSERT(thd->free_list == NULL);
+  mysql_reset_thd_for_next_command(thd);
 #ifndef EMBEDDED_LIBRARY
   if (stmt->param_count)
   {
@@ -1778,7 +1780,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
   if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
     goto set_params_data_err;
 #endif
-  DBUG_ASSERT(thd->free_list == NULL);
   thd->protocol= &thd->protocol_prep;           // Switch to binary protocol
   execute_stmt(thd, stmt, &expanded_query, true);
   thd->protocol= &thd->protocol_simple;         // Use normal protocol
@@ -1823,7 +1824,8 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
   }
 
   DBUG_ASSERT(thd->free_list == NULL);
-
+  /* Must go before setting variables, as it clears thd->user_var_events */
+  mysql_reset_thd_for_next_command(thd);
   thd->set_n_backup_statement(stmt, &thd->stmt_backup);
   if (stmt->set_params_from_vars(stmt,
                                  thd->stmt_backup.lex->prepared_stmt_params,
@@ -1932,6 +1934,7 @@ void mysql_stmt_reset(THD *thd, char *packet)
   */
   reset_stmt_params(stmt);
 
+  mysql_reset_thd_for_next_command(thd);
   send_ok(thd);
   
   DBUG_VOID_RETURN;