Below is the list of changes that have just been committed into a local
5.0 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://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet
1.1829 05/03/23 18:01:03 konstantin@stripped +5 -0
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.
tests/mysql_client_test.c
1.108 05/03/23 18:00:54 konstantin@stripped +69 -0
Test cases for Bug#8880 and Bug#9159
sql/sql_select.h
1.78 05/03/23 18:00:54 konstantin@stripped +1 -1
Initialize Item_arena of Cursor object. A case when a cursor object
is created but not used is possible with single-row result sets.
sql/sql_select.cc
1.301 05/03/23 18:00:54 konstantin@stripped +38 -23
Move sending metadata out of do_select.
Implement special handling of single-row result sets when a cursor
is requested.
sql/sql_prepare.cc
1.107 05/03/23 18:00:53 konstantin@stripped +8 -6
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.
libmysql/libmysql.c
1.203 05/03/23 18:00:53 konstantin@stripped +15 -0
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.
# 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: dragonfly.local
# Root: /home/kostja/work/current
--- 1.202/libmysql/libmysql.c 2005-03-17 21:27:19 +03:00
+++ 1.203/libmysql/libmysql.c 2005-03-23 18:00:53 +03:00
@@ -2862,6 +2862,21 @@
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: the server did not open a cursor
+ because it's more efficient to precache the single result set row on
+ client and free all server's resources. Another case when this
+ branch works and there is no cursor is a SHOW command: some of the
+ SHOW commands bypass the cursors framework in the server and write
+ data directly to the network. Consequently, their metadata doesn't
+ contain SERVER_STATUS_CURSOR_EXISTS or SERVER_STATUS_LAST_ROW_SENT.
+ For now let's try to catch all SHOW rebels with the assertion below.
+ */
+ DBUG_ASSERT(stmt->server_status & SERVER_STATUS_LAST_ROW_SENT);
+ DBUG_RETURN(mysql_stmt_store_result(stmt));
+ }
else
{
stmt->mysql->unbuffered_fetch_owner=
&stmt->unbuffered_fetch_cancelled;
--- 1.300/sql/sql_select.cc 2005-03-18 08:46:14 +03:00
+++ 1.301/sql/sql_select.cc 2005-03-23 18:00:54 +03:00
@@ -1606,35 +1606,53 @@
curr_join->fields= curr_fields_list;
curr_join->procedure= procedure;
+ thd->proc_info= "Sending data";
+ DBUG_PRINT("info", ("%s", thd->proc_info));
+
if (unit == &thd->lex->unit &&
(unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex)
&&
- thd->cursor && tables != const_tables)
+ thd->cursor)
{
+ DBUG_ASSERT(curr_join == this);
+ if (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.
+ In case when all tables are constant, we don't open a cursor:
+ it's faster to send the single row result set once and ask client
+ to precache it (store_result).
+ */
+ 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.
+ */
+ /* Open cursor for the last join sweep */
+ error= thd->cursor->open(this);
+ DBUG_VOID_RETURN;
+ }
/*
- 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.
+ We want to inform the client that this is a special case
+ with a single row result set and it's not worth the overhead
+ to open a cursor.
*/
- DBUG_ASSERT(curr_join == this);
- /* Open cursor for the last join sweep */
- error= thd->cursor->open(this);
+ thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
+ result->send_fields(*fields, Protocol::SEND_NUM_ROWS);
+ ::send_eof(thd);
+ do_select(this, fields, 0, procedure);
+ thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
}
else
{
- thd->proc_info="Sending data";
- DBUG_PRINT("info", ("%s", thd->proc_info));
+ /* Tell the client how many fields there are in a row */
+ result->send_fields(*curr_fields_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
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;
}
+ thd->limit_found_rows= curr_join->send_records;
+ thd->examined_row_count= curr_join->examined_rows;
DBUG_VOID_RETURN;
}
@@ -8752,10 +8770,7 @@
/*
Tell the client how many fields there are in a row
*/
- if (!table)
- join->result->send_fields(*fields,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
- else
+ if (table)
{
VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
empty_record(table);
--- 1.77/sql/sql_select.h 2005-03-17 15:23:28 +03:00
+++ 1.78/sql/sql_select.h 2005-03-23 18:00:54 +03:00
@@ -370,7 +370,7 @@
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();
};
--- 1.106/sql/sql_prepare.cc 2005-03-16 17:10:55 +03:00
+++ 1.107/sql/sql_prepare.cc 2005-03-23 18:00:53 +03:00
@@ -1970,6 +1970,7 @@
{
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 @@
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 @@
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
{
--- 1.107/tests/mysql_client_test.c 2005-03-19 18:16:20 +03:00
+++ 1.108/tests/mysql_client_test.c 2005-03-23 18:00:54 +03:00
@@ -12740,6 +12740,73 @@
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 @@
{ "test_bug8330", test_bug8330 },
{ "test_bug7990", test_bug7990 },
{ "test_bug8378", test_bug8378 },
+ { "test_bug8880", test_bug8880 },
+ { "test_bug9159", test_bug9159 },
{ 0, 0 }
};
| Thread |
|---|
| • bk commit into 5.0 tree (konstantin:1.1829) BUG#9159 | konstantin | 23 Mar |