diff options
author | unknown <konstantin@mysql.com> | 2005-06-30 16:17:10 +0400 |
---|---|---|
committer | unknown <konstantin@mysql.com> | 2005-06-30 16:17:10 +0400 |
commit | ec9ac3fe5c823b6ae7ccb0902c267b811bf732e7 (patch) | |
tree | b91ecb9596611574c3dc8bcfd25af12fca92bfe9 | |
parent | 7a5ec7606d9cf70697484df7c235341bc574b4a0 (diff) | |
download | mariadb-git-ec9ac3fe5c823b6ae7ccb0902c267b811bf732e7.tar.gz |
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"
-rw-r--r-- | include/mysql_com.h | 10 | ||||
-rw-r--r-- | libmysql/libmysql.c | 1 | ||||
-rw-r--r-- | libmysqld/lib_sql.cc | 2 | ||||
-rw-r--r-- | sql/protocol.cc | 64 | ||||
-rw-r--r-- | sql/protocol.h | 2 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 88 |
6 files changed, 132 insertions, 35 deletions
diff --git a/include/mysql_com.h b/include/mysql_com.h index 2293476c76c..88a614bc4a3 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -137,14 +137,14 @@ enum enum_server_command #define SERVER_QUERY_NO_GOOD_INDEX_USED 16 #define SERVER_QUERY_NO_INDEX_USED 32 /* - The server was able to fulfill client request and open read-only - non-scrollable cursor for the query. This flag comes in server - status with reply to COM_EXECUTE and COM_EXECUTE_DIRECT commands. + The server was able to fulfill the clients request and opened a + read-only non-scrollable cursor for a query. This flag comes + in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. */ #define SERVER_STATUS_CURSOR_EXISTS 64 /* - This flag is sent with last row of read-only cursor, in reply to - COM_FETCH command. + This flag is sent when a read-only cursor is exhausted, in reply to + COM_STMT_FETCH command. */ #define SERVER_STATUS_LAST_ROW_SENT 128 #define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 8ee11519615..e33fd470582 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -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); return 1; } - stmt->server_status= mysql->server_status; if (cli_read_binary_rows(stmt)) return 1; stmt->server_status= mysql->server_status; diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index dd4ba939ebe..c3b239ac7b9 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -773,7 +773,7 @@ send_ok(THD *thd,ha_rows affected_rows,ulonglong id,const char *message) } void -send_eof(THD *thd, bool no_flush) +send_eof(THD *thd) { } diff --git a/sql/protocol.cc b/sql/protocol.cc index 1c399a89a99..ade94a483a8 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -28,6 +28,7 @@ #include <stdarg.h> static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; +static void write_eof_packet(THD *thd, NET *net); #ifndef EMBEDDED_LIBRARY bool Protocol::net_store_data(const char *from, uint length) @@ -362,43 +363,52 @@ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ */ void -send_eof(THD *thd, bool no_flush) +send_eof(THD *thd) { NET *net= &thd->net; DBUG_ENTER("send_eof"); if (net->vio != 0 && !net->no_send_eof) { - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - uchar buff[5]; - /* 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)); - buff[0]=254; - int2store(buff+1, tmp); - /* - The following test should never be true, but it's better to do it - because if 'is_fatal_error' is set the server is not going to execute - other queries (see the if test in dispatch_command / COM_QUERY) - */ - if (thd->is_fatal_error) - thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; - int2store(buff+3, thd->server_status); - VOID(my_net_write(net,(char*) buff,5)); - VOID(net_flush(net)); - } - else - { - VOID(my_net_write(net,eof_buff,1)); - if (!no_flush) - VOID(net_flush(net)); - } + 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) + { + uchar buff[5]; + /* + 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)); + buff[0]= 254; + int2store(buff+1, tmp); + /* + The following test should never be true, but it's better to do it + because if 'is_fatal_error' is set the server is not going to execute + other queries (see the if test in dispatch_command / COM_QUERY) + */ + if (thd->is_fatal_error) + thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; + int2store(buff+3, thd->server_status); + VOID(my_net_write(net, (char*) buff, 5)); + } + else + VOID(my_net_write(net, eof_buff, 1)); +} + /* Please client to send scrambled_password in old format. SYNOPSYS @@ -640,7 +650,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags) } if (flags & SEND_EOF) - my_net_write(&thd->net, eof_buff, 1); + write_eof_packet(thd, &thd->net); DBUG_RETURN(prepare_for_send(list)); err: diff --git a/sql/protocol.h b/sql/protocol.h index 5b402cb2669..2717d2258fa 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -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 send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, 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); char *net_store_length(char *packet,uint length); char *net_store_data(char *to,const char *from, uint length); diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 585763c164d..fa70168d0ef 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -13389,6 +13389,93 @@ static void test_bug10736() 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 @@ -13626,6 +13713,7 @@ static struct my_tests_st my_tests[]= { { "test_bug11111", test_bug11111 }, { "test_bug9992", test_bug9992 }, { "test_bug10736", test_bug10736 }, + { "test_bug10794", test_bug10794 }, { 0, 0 } }; |