diff --git a/sql/sql_class.h b/sql/sql_class.h
index aea31f7db54881b2061c0f3b8d8719e59ec740c1..58514abb7b0f692f8448d40f789321db0b174104 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1109,6 +1109,13 @@ class select_result :public Sql_alloc {
     unit= u;
     return 0;
   }
+  /*
+    Because of peculiarities of prepared statements protocol
+    we need to know number of columns in the result set (if
+    there is a result set) apart from sending columns metadata.
+  */
+  virtual uint field_count(List<Item> &fields) const
+  { return fields.elements; }
   virtual bool send_fields(List<Item> &list,uint flag)=0;
   virtual bool send_data(List<Item> &items)=0;
   virtual bool initialize_tables (JOIN *join=0) { return 0; }
@@ -1123,6 +1130,20 @@ class select_result :public Sql_alloc {
 };
 
 
+/*
+  Base class for select_result descendands which intercept and
+  transform result set rows. As the rows are not sent to the client,
+  sending of result set metadata should be suppressed as well.
+*/
+
+class select_result_interceptor: public select_result
+{
+public:
+  uint field_count(List<Item> &fields) const { return 0; }
+  bool send_fields(List<Item> &fields, uint flag) { return FALSE; }
+};
+
+
 class select_send :public select_result {
 public:
   select_send() {}
@@ -1132,7 +1153,7 @@ class select_send :public select_result {
 };
 
 
-class select_to_file :public select_result {
+class select_to_file :public select_result_interceptor {
 protected:
   sql_exchange *exchange;
   File file;
@@ -1144,7 +1165,6 @@ class select_to_file :public select_result {
   select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
   { path[0]=0; }
   ~select_to_file();
-  bool send_fields(List<Item> &list, uint flag) { return 0; }
   void send_error(uint errcode,const char *err);
   bool send_eof();
   void cleanup();
@@ -1171,7 +1191,7 @@ class select_dump :public select_to_file {
 };
 
 
-class select_insert :public select_result {
+class select_insert :public select_result_interceptor {
  public:
   TABLE *table;
   List<Item> *fields;
@@ -1187,8 +1207,6 @@ class select_insert :public select_result {
   }
   ~select_insert();
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-  bool send_fields(List<Item> &list, uint flag)
-  { return 0; }
   bool send_data(List<Item> &items);
   void send_error(uint errcode,const char *err);
   bool send_eof();
@@ -1271,7 +1289,7 @@ class TMP_TABLE_PARAM :public Sql_alloc
   }
 };
 
-class select_union :public select_result {
+class select_union :public select_result_interceptor {
  public:
   TABLE *table;
   COPY_INFO info;
@@ -1280,8 +1298,6 @@ class select_union :public select_result {
   select_union(TABLE *table_par);
   ~select_union();
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-  bool send_fields(List<Item> &list, uint flag)
-  { return 0; }
   bool send_data(List<Item> &items);
   bool send_eof();
   bool flush();
@@ -1289,13 +1305,12 @@ class select_union :public select_result {
 };
 
 /* Base subselect interface class */
-class select_subselect :public select_result
+class select_subselect :public select_result_interceptor
 {
 protected:
   Item_subselect *item;
 public:
   select_subselect(Item_subselect *item);
-  bool send_fields(List<Item> &list, uint flag) { return 0; };
   bool send_data(List<Item> &items)=0;
   bool send_eof() { return 0; };
 };
@@ -1432,7 +1447,7 @@ class Unique :public Sql_alloc
 };
 
 
-class multi_delete :public select_result
+class multi_delete :public select_result_interceptor
 {
   TABLE_LIST *delete_tables, *table_being_deleted;
   Unique **tempfiles;
@@ -1445,8 +1460,6 @@ class multi_delete :public select_result
   multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
   ~multi_delete();
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-  bool send_fields(List<Item> &list,
- 		   uint flag) { return 0; }
   bool send_data(List<Item> &items);
   bool initialize_tables (JOIN *join);
   void send_error(uint errcode,const char *err);
@@ -1455,7 +1468,7 @@ class multi_delete :public select_result
 };
 
 
-class multi_update :public select_result
+class multi_update :public select_result_interceptor
 {
   TABLE_LIST *all_tables, *update_tables, *table_being_updated;
   THD *thd;
@@ -1474,7 +1487,6 @@ class multi_update :public select_result
 	       List<Item> *values, enum_duplicates handle_duplicates);
   ~multi_update();
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-  bool send_fields(List<Item> &list, uint flag) { return 0; }
   bool send_data(List<Item> &items);
   bool initialize_tables (JOIN *join);
   void send_error(uint errcode,const char *err);
@@ -1483,7 +1495,7 @@ class multi_update :public select_result
 };
 
 
-class select_dumpvar :public select_result {
+class select_dumpvar :public select_result_interceptor {
   ha_rows row_count;
 public:
   List<LEX_STRING> var_list;
@@ -1491,7 +1503,6 @@ class select_dumpvar :public select_result {
   select_dumpvar(void)  { var_list.empty(); vars.empty(); row_count=0;}
   ~select_dumpvar() {}
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-  bool send_fields(List<Item> &list, uint flag) {return 0;}
   bool send_data(List<Item> &items);
   bool send_eof();
   void cleanup();
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 27d98fdfebad225db18a49d40f49e19d54136012..545bc7bcc215ee6dc30d7b2fd0713b7659e37dc3 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1065,6 +1065,12 @@ static int mysql_test_select(Prepared_statement *stmt,
     DBUG_RETURN(1);
 #endif
 
+  if (!lex->result && !(lex->result= new (&stmt->mem_root) select_send))
+  {
+    send_error(thd);
+    goto err;
+  }
+
   if (open_and_lock_tables(thd, tables))
   {
     send_error(thd);
@@ -1088,8 +1094,13 @@ static int mysql_test_select(Prepared_statement *stmt,
     }
     else
     {
-      if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
-          thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
+      List<Item> &fields= lex->select_lex.item_list;
+      /*
+        We can use lex->result as it should've been
+        prepared in unit->prepare call above.
+      */
+      if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
+          lex->result->send_fields(fields, 0)
 #ifndef EMBEDDED_LIBRARY
           || net_flush(&thd->net)
 #endif
diff --git a/tests/client_test.c b/tests/client_test.c
index 0b30cc3386d00a583047a9806643ee1147a07030..1d2a85e54f4c52b9a2704cd230b939bb362de83c 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -10541,6 +10541,22 @@ static void test_bug5315()
 }
 
 
+static void test_bug6059()
+{
+  MYSQL_STMT *stmt;
+  const char *stmt_text;
+  int rc;
+
+  myheader("test_bug6059");
+
+  stmt_text= "SELECT 'foo' INTO OUTFILE 'x.3'";
+
+  stmt= mysql_stmt_init(mysql);
+  rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+  DIE_UNLESS(mysql_stmt_field_count(stmt) == 0);
+  mysql_stmt_close(stmt);
+}
+
 /*
   Read and parse arguments and MySQL options from my.cnf
 */
@@ -10851,6 +10867,7 @@ int main(int argc, char **argv)
     test_bug5194();         /* bulk inserts in prepared mode */
     test_bug5315();         /* check that mysql_change_user closes all
                                prepared statements */
+    test_bug6059();         /* correct metadata for SELECT ... INTO OUTFILE */
     /*
       XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
       DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.