Commit ec9ac3fe authored by unknown's avatar unknown

A fix and a test case for Bug#10794 "mysql_stmt_attr_set no

open cursor after mysql_stmt_execute" + post-review fixes.
The bug was caused by wrong flags in stmt->server_status on the client
side: if there was no cursor, the server didn't send server_status
flags to the client, and the old flags were used to set up the
fetch function of a statement. Consequently, stmt_read_row_from_cursor was
used when there was no cursor. The fix fixes the server to always
send server flags to the client.


include/mysql_com.h:
  Update stale comments.
libmysql/libmysql.c:
  Remove an extra assignment.
libmysqld/lib_sql.cc:
  Update to correspond to the changed signature of send_eof
sql/protocol.cc:
  Actual fix for bug#10794: create a function that writes the eof
  packet to network and use it from send_fields. We need to send
  a full eof packet from send_fields to inform the client about
  the cursor status (that there is no cursor in this case).
sql/protocol.h:
  Remove an unused parameter for send_eof.
tests/mysql_client_test.c:
  A test case for Bug#10794 "mysql_stmt_attr_set no open cursor 
  after mysql_stmt_execute"
parent 7a5ec760
...@@ -137,14 +137,14 @@ enum enum_server_command ...@@ -137,14 +137,14 @@ enum enum_server_command
#define SERVER_QUERY_NO_GOOD_INDEX_USED 16 #define SERVER_QUERY_NO_GOOD_INDEX_USED 16
#define SERVER_QUERY_NO_INDEX_USED 32 #define SERVER_QUERY_NO_INDEX_USED 32
/* /*
The server was able to fulfill client request and open read-only The server was able to fulfill the clients request and opened a
non-scrollable cursor for the query. This flag comes in server read-only non-scrollable cursor for a query. This flag comes
status with reply to COM_EXECUTE and COM_EXECUTE_DIRECT commands. in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
*/ */
#define SERVER_STATUS_CURSOR_EXISTS 64 #define SERVER_STATUS_CURSOR_EXISTS 64
/* /*
This flag is sent with last row of read-only cursor, in reply to This flag is sent when a read-only cursor is exhausted, in reply to
COM_FETCH command. COM_STMT_FETCH command.
*/ */
#define SERVER_STATUS_LAST_ROW_SENT 128 #define SERVER_STATUS_LAST_ROW_SENT 128
#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ #define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
......
...@@ -2726,7 +2726,6 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row) ...@@ -2726,7 +2726,6 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row)
set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
return 1; return 1;
} }
stmt->server_status= mysql->server_status;
if (cli_read_binary_rows(stmt)) if (cli_read_binary_rows(stmt))
return 1; return 1;
stmt->server_status= mysql->server_status; stmt->server_status= mysql->server_status;
......
...@@ -773,7 +773,7 @@ send_ok(THD *thd,ha_rows affected_rows,ulonglong id,const char *message) ...@@ -773,7 +773,7 @@ send_ok(THD *thd,ha_rows affected_rows,ulonglong id,const char *message)
} }
void void
send_eof(THD *thd, bool no_flush) send_eof(THD *thd)
{ {
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <stdarg.h> #include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
static void write_eof_packet(THD *thd, NET *net);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
bool Protocol::net_store_data(const char *from, uint length) bool Protocol::net_store_data(const char *from, uint length)
...@@ -362,19 +363,37 @@ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ ...@@ -362,19 +363,37 @@ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
*/ */
void void
send_eof(THD *thd, bool no_flush) send_eof(THD *thd)
{ {
NET *net= &thd->net; NET *net= &thd->net;
DBUG_ENTER("send_eof"); DBUG_ENTER("send_eof");
if (net->vio != 0 && !net->no_send_eof) if (net->vio != 0 && !net->no_send_eof)
{ {
write_eof_packet(thd, net);
VOID(net_flush(net));
thd->net.no_send_error= 1;
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
}
DBUG_VOID_RETURN;
}
/*
Format EOF packet according to the current protocol and
write it to the network output buffer.
*/
static void write_eof_packet(THD *thd, NET *net)
{
if (thd->client_capabilities & CLIENT_PROTOCOL_41) if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{ {
uchar buff[5]; uchar buff[5];
/* Don't send warn count during SP execution, as the warn_list /*
is cleared between substatements, and mysqltest gets confused */ Don't send warn count during SP execution, as the warn_list
is cleared between substatements, and mysqltest gets confused
*/
uint tmp= (thd->spcont ? 0 : min(thd->total_warn_count, 65535)); uint tmp= (thd->spcont ? 0 : min(thd->total_warn_count, 65535));
buff[0]=254; buff[0]= 254;
int2store(buff+1, tmp); int2store(buff+1, tmp);
/* /*
The following test should never be true, but it's better to do it The following test should never be true, but it's better to do it
...@@ -384,19 +403,10 @@ send_eof(THD *thd, bool no_flush) ...@@ -384,19 +403,10 @@ send_eof(THD *thd, bool no_flush)
if (thd->is_fatal_error) if (thd->is_fatal_error)
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
int2store(buff+3, thd->server_status); int2store(buff+3, thd->server_status);
VOID(my_net_write(net,(char*) buff,5)); VOID(my_net_write(net, (char*) buff, 5));
VOID(net_flush(net));
} }
else else
{ VOID(my_net_write(net, eof_buff, 1));
VOID(my_net_write(net,eof_buff,1));
if (!no_flush)
VOID(net_flush(net));
}
thd->net.no_send_error= 1;
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
}
DBUG_VOID_RETURN;
} }
/* /*
...@@ -640,7 +650,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags) ...@@ -640,7 +650,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
} }
if (flags & SEND_EOF) if (flags & SEND_EOF)
my_net_write(&thd->net, eof_buff, 1); write_eof_packet(thd, &thd->net);
DBUG_RETURN(prepare_for_send(list)); DBUG_RETURN(prepare_for_send(list));
err: err:
......
...@@ -179,7 +179,7 @@ void net_printf_error(THD *thd, uint sql_errno, ...); ...@@ -179,7 +179,7 @@ void net_printf_error(THD *thd, uint sql_errno, ...);
void net_send_error(THD *thd, uint sql_errno=0, const char *err=0); void net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L,
const char *info=0); const char *info=0);
void send_eof(THD *thd, bool no_flush=0); void send_eof(THD *thd);
bool send_old_password_request(THD *thd); bool send_old_password_request(THD *thd);
char *net_store_length(char *packet,uint length); char *net_store_length(char *packet,uint length);
char *net_store_data(char *to,const char *from, uint length); char *net_store_data(char *to,const char *from, uint length);
......
...@@ -13389,6 +13389,93 @@ static void test_bug10736() ...@@ -13389,6 +13389,93 @@ static void test_bug10736()
myquery(rc); myquery(rc);
} }
/* Bug#10794: cursors, packets out of order */
static void test_bug10794()
{
MYSQL_STMT *stmt, *stmt1;
MYSQL_BIND bind[2];
char a[21];
int id_val;
ulong a_len;
int rc;
const char *stmt_text;
int i= 0;
ulong type;
myheader("test_bug10794");
mysql_query(mysql, "drop table if exists t1");
mysql_query(mysql, "create table t1 (id integer not null primary key,"
"name varchar(20) not null)");
stmt= mysql_stmt_init(mysql);
stmt_text= "insert into t1 (id, name) values (?, ?)";
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
check_execute(stmt, rc);
bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void*) &id_val;
bind[1].buffer_type= MYSQL_TYPE_STRING;
bind[1].buffer= (void*) a;
bind[1].length= &a_len;
rc= mysql_stmt_bind_param(stmt, bind);
check_execute(stmt, rc);
for (i= 0; i < 34; i++)
{
id_val= (i+1)*10;
sprintf(a, "a%d", i);
a_len= strlen(a); /* safety against broken sprintf */
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
}
stmt_text= "select name from t1";
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
type= (ulong) CURSOR_TYPE_READ_ONLY;
mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
stmt1= mysql_stmt_init(mysql);
mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (void*) a;
bind[0].buffer_length= sizeof(a);
bind[0].length= &a_len;
rc= mysql_stmt_bind_result(stmt, bind);
check_execute(stmt, rc);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc);
if (!opt_silent)
printf("Fetched row from stmt: %s\n", a);
/* Don't optimize: an attribute of the original test case */
mysql_stmt_free_result(stmt);
mysql_stmt_reset(stmt);
stmt_text= "select name from t1 where id=10";
rc= mysql_stmt_prepare(stmt1, stmt_text, strlen(stmt_text));
check_execute(stmt1, rc);
rc= mysql_stmt_bind_result(stmt1, bind);
check_execute(stmt1, rc);
rc= mysql_stmt_execute(stmt1);
while (1)
{
rc= mysql_stmt_fetch(stmt1);
if (rc == MYSQL_NO_DATA)
{
if (!opt_silent)
printf("End of data in stmt1\n");
break;
}
check_execute(stmt1, rc);
if (!opt_silent)
printf("Fetched row from stmt1: %s\n", a);
}
mysql_stmt_close(stmt);
mysql_stmt_close(stmt1);
rc= mysql_query(mysql, "drop table t1");
myquery(rc);
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
...@@ -13626,6 +13713,7 @@ static struct my_tests_st my_tests[]= { ...@@ -13626,6 +13713,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug11111", test_bug11111 }, { "test_bug11111", test_bug11111 },
{ "test_bug9992", test_bug9992 }, { "test_bug9992", test_bug9992 },
{ "test_bug10736", test_bug10736 }, { "test_bug10736", test_bug10736 },
{ "test_bug10794", test_bug10794 },
{ 0, 0 } { 0, 0 }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment