mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Fixes and test cases for Bug#8880 "Commands out of sync error with cursors"
and Bug#9159 "Server crash during mysql_stmt_close". The patch adds support for single-row result sets in cursors. libmysql/libmysql.c: If we wanted a cursor, and the server wasn't able to create one, buffer all rows on client. Currently this is possible only for single row result sets and some SHOW commands. sql/sql_prepare.cc: Properly free resources if there was a request to open a cursor which wasn't fullfilled. Give error on attempt to open a cursor for a statement not returning a result set. sql/sql_select.h: Initialize Item_arena of Cursor object. A case when a cursor object is created but not used is possible with single-row result sets. tests/mysql_client_test.c: Test cases for Bug#8880 and Bug#9159
This commit is contained in:
parent
648d40ea66
commit
3b236b1dfd
4 changed files with 89 additions and 7 deletions
|
@ -2862,6 +2862,17 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
|
|||
mysql->status= MYSQL_STATUS_READY;
|
||||
stmt->read_row_func= stmt_read_row_from_cursor;
|
||||
}
|
||||
else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
|
||||
{
|
||||
/*
|
||||
This is a single-row result set, a result set with no rows, EXPLAIN,
|
||||
SHOW VARIABLES, or some other command which either a) bypasses the
|
||||
cursors framework in the server and writes rows directly to the
|
||||
network or b) is more efficient if all (few) result set rows are
|
||||
precached on client and server's resources are freed.
|
||||
*/
|
||||
DBUG_RETURN(mysql_stmt_store_result(stmt));
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
|
||||
|
|
|
@ -1970,6 +1970,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
{
|
||||
ulong stmt_id= uint4korr(packet);
|
||||
ulong flags= (ulong) ((uchar) packet[4]);
|
||||
Cursor *cursor= 0;
|
||||
/*
|
||||
Query text for binary log, or empty string if the query is not put into
|
||||
binary log.
|
||||
|
@ -2007,15 +2008,17 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
statement: we can't open a cursor for it.
|
||||
*/
|
||||
flags= 0;
|
||||
my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("info",("Using READ_ONLY cursor"));
|
||||
if (!stmt->cursor &&
|
||||
!(stmt->cursor= new (&stmt->main_mem_root) Cursor()))
|
||||
!(cursor= stmt->cursor= new (&stmt->main_mem_root) Cursor()))
|
||||
DBUG_VOID_RETURN;
|
||||
/* If lex->result is set, mysql_execute_command will use it */
|
||||
stmt->lex->result= &stmt->cursor->result;
|
||||
stmt->lex->result= &cursor->result;
|
||||
}
|
||||
}
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
|
@ -2061,11 +2064,10 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||
thd->protocol= &thd->protocol_simple; // Use normal protocol
|
||||
|
||||
if (flags & (ulong) CURSOR_TYPE_READ_ONLY)
|
||||
if (cursor && cursor->is_open())
|
||||
{
|
||||
if (stmt->cursor->is_open())
|
||||
stmt->cursor->init_from_thd(thd);
|
||||
stmt->cursor->state= stmt->state;
|
||||
cursor->init_from_thd(thd);
|
||||
cursor->state= stmt->state;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -370,7 +370,7 @@ public:
|
|||
void close();
|
||||
|
||||
void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
|
||||
Cursor() :join(0), unit(0) {}
|
||||
Cursor() :Item_arena(TRUE), join(0), unit(0) {}
|
||||
~Cursor();
|
||||
};
|
||||
|
||||
|
|
|
@ -12740,6 +12740,73 @@ static void test_bug8378()
|
|||
mysql_close(lmysql);
|
||||
}
|
||||
|
||||
|
||||
MYSQL_STMT *open_cursor(char *query)
|
||||
{
|
||||
int rc;
|
||||
const ulong type= (ulong)CURSOR_TYPE_READ_ONLY;
|
||||
|
||||
MYSQL_STMT *stmt= mysql_stmt_init(mysql);
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(query));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
|
||||
static void test_bug8880()
|
||||
{
|
||||
MYSQL_STMT *stmt_list[2], **stmt;
|
||||
MYSQL_STMT **stmt_list_end= (MYSQL_STMT**) stmt_list + 2;
|
||||
int rc;
|
||||
|
||||
myheader("test_bug8880");
|
||||
|
||||
mysql_query(mysql, "drop table if exists t1");
|
||||
mysql_query(mysql, "create table t1 (a int not null primary key, b int)");
|
||||
rc= mysql_query(mysql, "insert into t1 values (1,1)");
|
||||
myquery(rc); /* one check is enough */
|
||||
/*
|
||||
when inserting 2 rows everything works well
|
||||
mysql_query(mysql, "INSERT INTO t1 VALUES (1,1),(2,2)");
|
||||
*/
|
||||
for (stmt= stmt_list; stmt < stmt_list_end; stmt++)
|
||||
*stmt= open_cursor("select a from t1");
|
||||
for (stmt= stmt_list; stmt < stmt_list_end; stmt++)
|
||||
{
|
||||
rc= mysql_stmt_execute(*stmt);
|
||||
check_execute(*stmt, rc);
|
||||
}
|
||||
for (stmt= stmt_list; stmt < stmt_list_end; stmt++)
|
||||
mysql_stmt_close(*stmt);
|
||||
}
|
||||
|
||||
|
||||
static void test_bug9159()
|
||||
{
|
||||
MYSQL_STMT *stmt;
|
||||
int rc;
|
||||
const char *stmt_text= "select a, b from t1";
|
||||
const unsigned long type= CURSOR_TYPE_READ_ONLY;
|
||||
|
||||
myheader("test_bug9159");
|
||||
|
||||
mysql_query(mysql, "drop table if exists t1");
|
||||
mysql_query(mysql, "create table t1 (a int not null primary key, b int)");
|
||||
rc= mysql_query(mysql, "insert into t1 values (1,1)");
|
||||
myquery(rc);
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void *)&type);
|
||||
|
||||
mysql_stmt_execute(stmt);
|
||||
mysql_stmt_close(stmt);
|
||||
rc= mysql_query(mysql, "drop table if exists t1");
|
||||
myquery(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
Read and parse arguments and MySQL options from my.cnf
|
||||
*/
|
||||
|
@ -12962,6 +13029,8 @@ static struct my_tests_st my_tests[]= {
|
|||
{ "test_bug8330", test_bug8330 },
|
||||
{ "test_bug7990", test_bug7990 },
|
||||
{ "test_bug8378", test_bug8378 },
|
||||
{ "test_bug8880", test_bug8880 },
|
||||
{ "test_bug9159", test_bug9159 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue