List:Internals« Previous MessageNext Message »
From:konstantin Date:March 22 2004 3:11pm
Subject:bk commit into 4.1 tree (konstantin:1.1713)
View as plain text  
Below is the list of changes that have just been committed into a local
4.1 repository of kostja. When kostja 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://www.mysql.com/doc/I/n/Installing_source_tree.html

ChangeSet
  1.1713 04/03/22 18:11:35 konstantin@stripped +10 -0
  Intermediate commit (ro cursors) to perform bk export: ignore unless you're
  curious.
  This is the first working version.

  tests/c_api/sap.cc
    1.1 04/03/22 18:11:32 konstantin@stripped +301 -0

  tests/c_api/Makefile.am
    1.1 04/03/22 18:11:32 konstantin@stripped +33 -0

  tests/c_api/sap.cc
    1.0 04/03/22 18:11:32 konstantin@stripped +0 -0
    BitKeeper file /home/kostja/mysql/mysql-4.1-sap/tests/c_api/sap.cc

  tests/c_api/Makefile.am
    1.0 04/03/22 18:11:32 konstantin@stripped +0 -0
    BitKeeper file /home/kostja/mysql/mysql-4.1-sap/tests/c_api/Makefile.am

  sql/sql_union.cc
    1.106 04/03/22 18:11:32 konstantin@stripped +5 -1
    - added support for opening cursors

  sql/sql_select.h
    1.69 04/03/22 18:11:32 konstantin@stripped +2 -7
    - cleanup of Cursor class

  sql/sql_select.cc
    1.311 04/03/22 18:11:31 konstantin@stripped +84 -13
    - added support for simple cursors

  sql/sql_prepare.cc
    1.86 04/03/22 18:11:31 konstantin@stripped +85 -2
    COM_FETCH handling implemented

  sql/sql_parse.cc
    1.328 04/03/22 18:11:31 konstantin@stripped +4 -0
    added COM_FETCH handler

  sql/mysql_priv.h
    1.268 04/03/22 18:11:31 konstantin@stripped +1 -0
    further ports of cursor: this is the working version already

  libmysql/libmysql.c
    1.218 04/03/22 18:11:31 konstantin@stripped +1 -2
    fixed bug in stmt_read_row_from_cursor

  configure.in
    1.260 04/03/22 18:11:31 konstantin@stripped +2 -2
    tests directory was added.

# 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:	konstantin
# Host:	oak.local
# Root:	/home/kostja/mysql/mysql-4.1-sap

--- 1.259/configure.in	Fri Mar 19 00:46:34 2004
+++ 1.260/configure.in	Mon Mar 22 18:11:31 2004
@@ -95,7 +95,6 @@
 
 AC_PREREQ(2.12)dnl		Minimum Autoconf version required.
 
-AM_MAINTAINER_MODE
 #AC_ARG_PROGRAM			# Automaticly invoked by AM_INIT_AUTOMAKE
 AM_SANITY_CHECK
 # This is needed is SUBDIRS is set
@@ -2823,7 +2822,8 @@
  sql-common/Makefile SSL/Makefile dnl
  merge/Makefile dbug/Makefile scripts/Makefile dnl
  include/Makefile sql-bench/Makefile tools/Makefile dnl
- tests/Makefile Docs/Makefile support-files/Makefile dnl
+ tests/Makefile tests/c_api/Makefile dnl
+ Docs/Makefile support-files/Makefile dnl
  support-files/MacOSX/Makefile mysql-test/Makefile dnl
  include/mysql_version.h dnl
  cmd-line-utils/Makefile dnl

--- 1.217/libmysql/libmysql.c	Mon Mar 22 14:37:25 2004
+++ 1.218/libmysql/libmysql.c	Mon Mar 22 18:11:31 2004
@@ -2371,8 +2371,7 @@
     stmt->server_status= mysql->server_status;
 
     stmt->data_cursor= result->data;
-    if (!stmt->data_cursor)
-      goto no_data;
+    return stmt_read_row_buffered(stmt, row);
   }
   return 0;
 no_data:

--- 1.267/sql/mysql_priv.h	Thu Mar 18 01:13:26 2004
+++ 1.268/sql/mysql_priv.h	Mon Mar 22 18:11:31 2004
@@ -615,6 +615,7 @@
 /* sql_prepare.cc */
 void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length);
 void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
 void mysql_stmt_free(THD *thd, char *packet);
 void mysql_stmt_reset(THD *thd, char *packet);
 void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);

--- 1.327/sql/sql_parse.cc	Sun Mar 21 17:39:34 2004
+++ 1.328/sql/sql_parse.cc	Mon Mar 22 18:11:31 2004
@@ -1420,6 +1420,10 @@
     mysql_stmt_execute(thd, packet, packet_length);
     break;
   }
+  case COM_FETCH:
+  {
+    mysql_stmt_fetch(thd, packet, packet_length);
+  }
   case COM_LONG_DATA:
   {
     mysql_stmt_get_longdata(thd, packet, packet_length);

--- 1.310/sql/sql_select.cc	Mon Mar 22 14:37:25 2004
+++ 1.311/sql/sql_select.cc	Mon Mar 22 18:11:31 2004
@@ -207,7 +207,8 @@
       send_error(thd, 0, NullS);
     res= 1;					// Error sent to client
   }
-  delete result;
+  if (!thd->cursor || !thd->cursor->is_open())
+    delete result;
   DBUG_RETURN(res);
 }
 
@@ -1499,12 +1500,45 @@
 	DBUG_VOID_RETURN;
     }
   }
+  /* XXX: When can we have here thd->net.report_error not zero? */
+  if (thd->net.report_error)
+  {
+    error= thd->net.report_error;
+    DBUG_VOID_RETURN;
+  }
   curr_join->having= curr_join->tmp_having;
-  thd->proc_info="Sending data";
-  error= thd->net.report_error ||
-    do_select(curr_join, curr_fields_list, NULL, procedure);
-  thd->limit_found_rows= curr_join->send_records;
-  thd->examined_row_count= curr_join->examined_rows;
+  curr_join->fields= curr_fields_list;
+  curr_join->procedure= procedure;
+
+  if (unit == &thd->lex->unit &&
+      (unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex) &&
+      thd->cursor && tables != const_tables)
+  {
+    /*
+      We are here if this is JOIN::exec for the last select of the main unit
+      and the client requested to open a cursor.
+      We check that not all tables are constant because this case is not
+      handled by do_select() separately, and this case is not implemented
+      for cursors yet.
+    */
+    DBUG_ASSERT(error == 0);
+    /*
+      curr_join is used only for reusable joins - that is, 
+      to perform SELECT for each outer row (like in subselects).
+      This join is main, so we know for sure that curr_join == join.
+    */
+    DBUG_ASSERT(curr_join == this);
+    /* Open cursor for the last join sweep */
+    error= thd->cursor->open(this);
+  }
+  else
+  {
+    thd->proc_info="Sending data";
+    error= do_select(curr_join, curr_fields_list, NULL, procedure);
+    thd->limit_found_rows= curr_join->send_records;
+    thd->examined_row_count= curr_join->examined_rows;
+  }
+
   DBUG_VOID_RETURN;
 }
 
@@ -1580,6 +1614,9 @@
     XXX: thd->locked_tables is not changed.
     What problems can we have with it if cursor is open?
   */
+  /*
+    TODO: grab thd->free_list here?
+  */
 }
   
 
@@ -1733,7 +1770,7 @@
       else
       {
         enum { END_OF_RECORDS= 1 };
-        error= join_tab->next_select(join, join_tab+1, END_OF_RECORDS);
+        error= join_tab->next_select(join, join_tab+1, (int) END_OF_RECORDS);
       }
       break;
     }
@@ -1763,15 +1800,23 @@
     thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
     ::send_eof(thd);
     thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
-    mem_root= thd->mem_root;
-    free_root(&mem_root, MYF(0));
     join= 0;
     unit= 0;
+    /*
+      Must be last, as some memory might be allocated for free purposes,
+      like in free_tmp_table() (TODO: fix this issue)
+    */
+    mem_root= thd->mem_root;
+    free_root(&mem_root, MYF(0));
     break;
   default:
     close();
     join= 0;
     unit= 0;
+    /*
+      Must be last, as some memory might be allocated for free purposes,
+      like in free_tmp_table() (TODO: fix this issue)
+    */
     mem_root= thd->mem_root;
     free_root(&mem_root, MYF(0));
     break;
@@ -1812,10 +1857,6 @@
     thd->derived_tables= tmp_derived_tables;
     thd->lock= tmp_lock;
   }
-  /*
-    Must be last, as some memory might be allocated for free purposes,
-    like in free_tmp_table() (TODO: fix this issue)
-  */
 }
 
 
@@ -1823,6 +1864,10 @@
 {
   if (is_open())
     close();
+  /*
+    Must be last, as some memory might be allocated for free purposes,
+    like in free_tmp_table() (TODO: fix this issue)
+  */
   free_root(&mem_root, MYF(0));
 }
 
@@ -1901,6 +1946,16 @@
 
   join->exec();
 
+  if (thd->cursor && thd->cursor->is_open())
+  {
+    /*
+      A cursor was opened for the last sweep in exec().
+      We are here only if this is mysql_select for top-level SELECT_LEX_UNIT
+      and there were no error.
+    */
+    free_join= 0;
+  }
+
   if (thd->lex->describe & DESCRIBE_EXTENDED)
   {
     select_lex->where= join->conds_history;
@@ -6652,6 +6707,14 @@
       }
       DBUG_RETURN(-3);				// Abort nicely
     }
+    else if (join->send_records >= join->fetch_limit)
+    {
+      /*
+        There is a server-side cursor and all rows for this fetch request
+        are sent.
+      */
+      DBUG_RETURN(-4);
+    }
   }
   else
   {
@@ -6725,6 +6788,14 @@
 	    DBUG_RETURN(-3);				// Abort nicely
 	  join->do_send_rows=0;
 	  join->unit->select_limit_cnt = HA_POS_ERROR;
+        }
+        else if (join->send_records >= join->fetch_limit)
+        {
+          /*
+            There is a server-side cursor and all rows for this fetch
+            request are sent.
+          */
+          DBUG_RETURN(-4);
         }
       }
     }

--- 1.68/sql/sql_select.h	Mon Mar 22 14:37:26 2004
+++ 1.69/sql/sql_select.h	Mon Mar 22 18:11:32 2004
@@ -324,7 +324,6 @@
 class Cursor: public Sql_alloc 
 {
   JOIN *join;
-  ulong type;
   SELECT_LEX_UNIT *unit;
 
   MEM_ROOT mem_root;
@@ -334,10 +333,6 @@
   ulong query_id;
 public:
   select_send result;
-  /*
-    Type of cursor (read-only, for update, scrollable, etc).
-    Contains combination of values of Enum_cursor_type 
-  */
 
   /* Temporary implementation as now we replace THD state by value */
   /* Save THD state into cursor */
@@ -349,12 +344,12 @@
 
   int open(JOIN *join);
   int fetch(ulong num_rows);
-  void reset() { type= 0; join= 0; }
+  void reset() { join= 0; }
   bool is_open() const { return join != 0; }
   void close();
 
   void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
-  Cursor(ulong type_arg= 0) :join(0), type(type_arg), unit(0) {}
+  Cursor() :join(0), unit(0) {}
   ~Cursor();
 };
  

--- 1.105/sql/sql_union.cc	Sat Mar 20 14:36:22 2004
+++ 1.106/sql/sql_union.cc	Mon Mar 22 18:11:32 2004
@@ -31,7 +31,11 @@
   int res= 0;
   if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK)))
     res= unit->exec();
-  res|= unit->cleanup();
+  if (res == 0 && thd->cursor && thd->cursor->is_open())
+    thd->cursor->set_unit(unit);
+  else
+    res|= unit->cleanup();
+
   DBUG_RETURN(res);
 }
 
--- New file ---
+++ tests/c_api/Makefile.am	04/03/22 18:11:32
## Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
##
## This library is free software; you can redistribute it and/or
## modify it under the terms of the GNU Library General Public
## License as published by the Free Software Foundation; either
## version 2 of the License, or (at your option) any later version.
##
## This library is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## Library General Public License for more details.
##
## You should have received a copy of the GNU Library General Public
## License along with this library; if not, write to the Free
## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
## MA 02111-1307, USA

INCLUDES= -I$(top_srcdir)/include 
#CXX=g++
#CXXFLAGS=-Wall -ansi -pedantic -Wno-long-long -W -g -O0
#CFLAGS=-Wall -ansi -pedantic -Wno-long-long -W -g -O0
LDADD= @CLIENT_EXTRA_LDFLAGS@ $(top_builddir)/libmysql/libmysqlclient.la
LDFLAGS=-static

noinst_PROGRAMS= sap 
## To run all tests use 'make check'
check_PROGRAMS= $(noinst_PROGRAMS)
TESTS= $(check_PROGRAMS) 

sap_SOURCES= sap.cc

## don't update the files from bitkeeper
%::SCCS/s.%

--- New file ---
+++ tests/c_api/sap.cc	04/03/22 18:11:32
/* Copyright (C) 2004 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include <mysql.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/* zeroed by linker: */

MYSQL *mysql; 
MYSQL_BIND bind1, bind2;
MYSQL_STMT *stmt1, *stmt2;
char res1[60];
char res2[60]; 
unsigned long res1_len, res2_len;
int rc;
unsigned long type= CURSOR_TYPE_READ_ONLY;

/*
  Print place and possible MySQL error message to stderr and exit if there
  was an error.
*/
void die_if(int rc, const char *place);

/* Initialize structures and connect to the test database */
void init_connect();
/* Fill test tables data */
void fill_tables();

 /* Perform simultaneous fetch from two statements */
void fetch2(const char *query1, const char *query2);

void fetch1(const char *query); /* fetch all data from the first statement */

void section(const char *message); /* pretty print given message */

/*
  Basic read-nly cursors implementation test program. 
  For any questions/comments please contact Konstantin Osipov
  <konstantin@stripped>
  Note: lots of functionality is not tested here.
*/

int
main(int argc, char *argv[])
{
  
  init_connect();
  fill_tables();

  /*********************************************************************/
  section("BASICS: Open two cursors to different tables and fetch from\n"
          "them in turn.");

  fetch2("SELECT name FROM t1", "SELECT name FROM t2");

#if 0
  /*********************************************************************/
  section("UNIONS: open a cursor for UNION statement. The same table\n"
          "can be used in two cursors.");
  
  fetch1("SELECT t1.name FROM t1 UNION SELECT t2.name FROM t2");
  fetch1("SELECT t1.id FROM t1 WHERE t1.id < 5");
  
  /*********************************************************************/
  section("JOIN: equi-join + LIMIT, equi-join + where clause + filesort");

  /* Note: LIMIT A, B is not working yet */

  fetch1("SELECT City.Name as Capital "
         "FROM Country, City "
         "WHERE City.ID=Country.Capital "
         "LIMIT 10"
         );
  
#endif
  mysql_close(mysql);

  return 0;
}


void die_if(int rc, const char *message)
{
  if (rc)
  {
    fprintf(stderr, "%s:\n", message);
    if (mysql)
    {
      fprintf(stderr, "%s\n", mysql_error(mysql));

      if (stmt1)
        mysql_stmt_close(stmt1);
      if (stmt2)
        mysql_stmt_close(stmt2);

      mysql_close(mysql);
    }
    exit(1);
  }
}


void init_connect()
{
  die_if(!(mysql= mysql_init(0)),
         "mysql_init failed");

  die_if(!(mysql_real_connect(mysql,
                              0, /* host */
                              "kostja", /* user */
                              0, /* password */
                              "world", /* database */
                              0, /* port */
                              "/opt/local/var/mysql/mysql.sock",
                              0 /* flags */)),
         "mysql_real_connect failed");
  
  bind1.buffer_type= FIELD_TYPE_STRING;
  bind1.buffer= (char *) res1;
  bind1.buffer_length= sizeof(res1);
  bind1.length= &res1_len;
  bind1.is_null= 0;
  
  bind2.buffer_type= FIELD_TYPE_STRING;
  bind2.buffer= (char *) res2;
  bind2.buffer_length= sizeof(res2);
  bind2.length= &res2_len;
  bind2.is_null= 0;
}

void fill_tables()
{
  /*
    create two simple tables with the following content
    t1:                    t2:
    +----+-------------+   +----+---------------+
    | id | name        |   | id | name          |
    +----+-------------+   +----+---------------+
    |  2 | Ja          |   |  4 | Guam          |
    |  3 | Ede         |   |  5 | Aruba         |
    |  4 | Haag        |   |  6 | Angola        |
    |  5 | Kabul       |   |  7 | Albania       |
    |  6 | Almere      |   |  8 | Anguilla      |
    |  7 | Utrecht     |   |  9 | Argentina     |
    |  8 | Qandahar    |   | 10 | Azerbaijan    |
    |  9 | Amsterdam   |   | 11 | Afghanistan   |
    | 10 | Amersfoort  |   | 12 | Burkina Faso  |
    | 11 | Constantine |   | 13 | Faroe Islands |
    +----+-------------+   +----+---------------+

    to test basic cursors in operation.
  */

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"),
  die_if(rc, "in drop table t1");

  rc= mysql_query(mysql,
                  "CREATE TABLE t1 "
                  "(id INTEGER NOT NULL PRIMARY KEY, "
                  " name VARCHAR(20) NOT NULL)");
  die_if(rc, "in create table t1");
  rc= mysql_query(mysql,
                  "INSERT INTO t1 (id, name) VALUES "
                  "(2, 'Ja'), (3, 'Ede'), "
                  "(4, 'Haag'), (5, 'Kabul'), "
                  "(6, 'Almere'), (7, 'Utrecht'), "
                  "(8, 'Qandahar'), (9, 'Amsterdam'), "
                  "(10, 'Amersfoort'), (11, 'Constantine')");
  die_if(rc, "in fill table t1");


  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2"),
  die_if(rc, "in drop table t2");

  rc= mysql_query(mysql,
                  "CREATE TABLE t2 "
                  "(id INTEGER NOT NULL PRIMARY KEY, "
                  " name VARCHAR(20) NOT NULL)");
  die_if(rc, "in create table t2");
  rc= mysql_query(mysql,
                  "INSERT INTO t2 (id, name) VALUES "
                  "(4, 'Guam'), (5, 'Aruba'), "
                  "(6, 'Angola'), (7, 'Albania'), "
                  "(8, 'Anguilla'), (9, 'Argentina'), "
                  "(10, 'Azerbaijan'), (11, 'Afghanistan'), "
                  "(12, 'Burkina Faso'), (13, 'Faroe Islands')");
  die_if(rc, "in fill table t2");
}


void fetch2(const char *query1, const char *query2)
{
  int rc1= 0;
  int rc2= 0;
  int row_count= 0;

  stmt1= mysql_stmt_init(mysql);
  die_if(mysql_stmt_prepare(stmt1, query1, strlen(query1)),
         "prepare(1) failed");

  stmt2= mysql_stmt_init(mysql);
  die_if(mysql_stmt_prepare(stmt2, query2, strlen(query2)),
         "prepare (2) failed");

  mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void *) &type);
  mysql_stmt_attr_set(stmt2, STMT_ATTR_CURSOR_TYPE, (const void *) &type);

  die_if(mysql_stmt_execute(stmt1), "execute (1) failed");
  die_if(mysql_stmt_execute(stmt2), "execute (2) failed");

  mysql_stmt_bind_result(stmt1, &bind1);
  mysql_stmt_bind_result(stmt2, &bind2);

  do
  {
    if (rc1 == 0)
    {
      rc1= mysql_stmt_fetch(stmt1);
      if (rc1 == 0)
      {
        res1[res1_len]= '\0';
        printf("cursor 1, fetched value '%s'\n", res1);
        ++row_count;
      }
    }
    if (rc2 == 0)
    {
      rc2= mysql_stmt_fetch(stmt2);
      if (rc2 == 0)
      {
        res2[res2_len]= '\0';
        printf("cursor 2, fetched value '%s'\n", res2);
        ++row_count;
      }
    }
  }
  while (rc1 == 0 || rc2 == 0);
  die_if(rc1 != MYSQL_NO_DATA, "mysql_stmt_fetch() (1) failed");
  die_if(rc2 != MYSQL_NO_DATA, "mysql_stmt_fetch() (2) failed");
  printf("total rows fetched: %d\n", row_count);

  mysql_stmt_close(stmt1);
  mysql_stmt_close(stmt2);
  stmt1= stmt2= 0;
}

void fetch1(const char *query)
{
  int rc1= 0;
  int row_count= 0;

  stmt1= mysql_stmt_init(mysql);
  die_if(mysql_stmt_prepare(stmt1, query, strlen(query)),
         "prepare failed");

  mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void *) &type);

  die_if(mysql_stmt_execute(stmt1), "execute (1) failed");

  mysql_stmt_bind_result(stmt1, &bind1);

  do
  {
    if (rc1 == 0)
    {
      rc1= mysql_stmt_fetch(stmt1);
      if (rc1 == 0)
      {
        printf("fetched value '%s'\n", res1);
        ++row_count;
      }
    }
  }
  while (rc1  == 0);
  die_if(rc1 != MYSQL_NO_DATA, "mysql_stmt_fetch() failed");
  printf("total rows fetched: %d\n", row_count);
  mysql_stmt_close(stmt1);
  stmt1= 0;
}

void section(const char *message)
{
  puts("---------------------------------------------------------------");
  puts(message);
  puts("---------------------------------------------------------------");
}


--- 1.85/sql/sql_prepare.cc	Sat Mar 20 00:41:36 2004
+++ 1.86/sql/sql_prepare.cc	Mon Mar 22 18:11:31 2004
@@ -1108,6 +1108,7 @@
 
 void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
 {
+  /* there should always be place for 8-16 bytes in the network buffer */
   ulong stmt_id= uint4korr(packet);
   ulong flags= uint4korr(packet+=4);
   Prepared_statement *stmt;
@@ -1124,6 +1125,30 @@
     DBUG_VOID_RETURN;
   }
 
+  if (flags)
+  {
+    if (stmt->lex->result)
+    {
+      /*
+        If lex->result is set in the parser, it's not SELECT statement:
+        we won't be able to open a cursor for it
+      */
+      flags= 0;
+    }
+    else
+    {
+      if (!stmt->cursor &&
+          !(stmt->cursor= new (&stmt->mem_root) Cursor()))
+      {
+        send_error(thd, ER_OUT_OF_RESOURCES);
+        DBUG_VOID_RETURN;
+      }
+      /* If lex->result is set, mysql_execute_command will use it */
+      stmt->lex->result= &stmt->cursor->result;
+
+    }
+  }
+
   thd->stmt_backup.set_statement(thd);
   thd->set_statement(stmt);
 
@@ -1165,8 +1190,13 @@
   if (!(specialflag & SPECIAL_NO_PRIOR))
     my_pthread_setprio(pthread_self(), WAIT_PRIOR);
 
-  cleanup_items(stmt->free_list);
-  close_thread_tables(thd); // to close derived tables
+  if (flags && stmt->cursor->is_open())
+    stmt->cursor->init_from_thd(thd);
+  else
+  {
+    cleanup_items(stmt->free_list);
+    close_thread_tables(thd); // to close derived tables
+  }
   thd->set_statement(&thd->stmt_backup);
   DBUG_VOID_RETURN;
 
@@ -1174,6 +1204,59 @@
   thd->set_statement(&thd->stmt_backup);
   my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
   send_error(thd);
+  DBUG_VOID_RETURN;
+}
+
+
+/*
+  COM_FETCH handler: fetches requested amount of rows from cursor 
+  SYNOPSYS
+    
+*/
+
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
+{
+  /* assume there is always place for 8-16 bytes */
+  ulong stmt_id= uint4korr(packet);
+  ulong num_rows= uint4korr(packet+=4);
+  Statement *stmt;
+  int error;
+  
+  DBUG_ENTER("mysql_stmt_fetch");
+
+  if (!(stmt= thd->stmt_map.find(stmt_id)) ||
+      !stmt->cursor ||
+      !stmt->cursor->is_open())
+  {
+    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "fetch");
+    send_error(thd);
+    DBUG_VOID_RETURN;
+  }
+
+  thd->stmt_backup.set_statement(thd);
+  thd->stmt_backup.set_item_arena(thd);
+  thd->set_statement(stmt);
+  thd->set_item_arena(stmt);
+  stmt->cursor->init_thd(thd);
+  
+  if (!(specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_setprio(pthread_self(),QUERY_PRIOR);  
+
+  thd->protocol= &thd->protocol_prep;		// Switch to binary protocol
+  error= stmt->cursor->fetch(num_rows);
+  thd->protocol= &thd->protocol_simple;         // Use normal protocol
+
+  if (!(specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
+
+  /* Restore THD state */
+  stmt->cursor->reset_thd(thd);
+  thd->set_statement(&thd->stmt_backup);
+  thd->set_item_arena(&thd->stmt_backup);
+
+  if (error && error != -4)
+    send_error(thd, ER_OUT_OF_RESOURCES);
+
   DBUG_VOID_RETURN;
 }
 
Thread
bk commit into 4.1 tree (konstantin:1.1713)konstantin22 Mar