Bug#14013 mysql_stmt_store_result() bombs if a cursor is open
- Add code to 'mysql_stmt_store_result' to allow it to be called on a prepared statement with open server side cursor. - Add tests to mysql_client_test that uses 'mysql_stmt_store_result'
This commit is contained in:
parent
102aa5a79f
commit
5000951ab4
@ -3167,7 +3167,7 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
|
|||||||
|
|
||||||
if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
|
if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
|
||||||
die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
|
die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
|
||||||
mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
|
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||||
|
|
||||||
free_replace_column();
|
free_replace_column();
|
||||||
|
|
||||||
@ -3632,7 +3632,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
|
|||||||
if (mysql_stmt_prepare(stmt, query, query_len))
|
if (mysql_stmt_prepare(stmt, query, query_len))
|
||||||
{
|
{
|
||||||
handle_error(query, command, mysql_stmt_errno(stmt),
|
handle_error(query, command, mysql_stmt_errno(stmt),
|
||||||
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3648,29 +3648,34 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
|
|||||||
parameter markers.
|
parameter markers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef BUG14013_FIXED
|
|
||||||
/*
|
|
||||||
Use cursor when retrieving result
|
|
||||||
*/
|
|
||||||
if (cursor_protocol_enabled)
|
if (cursor_protocol_enabled)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
Use cursor when retrieving result
|
||||||
|
*/
|
||||||
ulong type= CURSOR_TYPE_READ_ONLY;
|
ulong type= CURSOR_TYPE_READ_ONLY;
|
||||||
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
|
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
|
||||||
die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
|
die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
|
||||||
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Execute the query
|
Execute the query
|
||||||
*/
|
*/
|
||||||
if (mysql_stmt_execute(stmt))
|
if (mysql_stmt_execute(stmt))
|
||||||
{
|
{
|
||||||
handle_error(query, command, mysql_stmt_errno(stmt),
|
handle_error(query, command, mysql_stmt_errno(stmt),
|
||||||
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
When running in cursor_protocol get the warnings from execute here
|
||||||
|
and keep them in a separate string for later.
|
||||||
|
*/
|
||||||
|
if (cursor_protocol_enabled && !disable_warnings)
|
||||||
|
append_warnings(&ds_execute_warnings, mysql);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We instruct that we want to update the "max_length" field in
|
We instruct that we want to update the "max_length" field in
|
||||||
mysql_stmt_store_result(), this is our only way to know how much
|
mysql_stmt_store_result(), this is our only way to know how much
|
||||||
@ -3680,7 +3685,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
|
|||||||
my_bool one= 1;
|
my_bool one= 1;
|
||||||
if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
|
if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
|
||||||
die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
|
die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
|
||||||
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3690,7 +3695,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
|
|||||||
if (mysql_stmt_store_result(stmt))
|
if (mysql_stmt_store_result(stmt))
|
||||||
{
|
{
|
||||||
handle_error(query, command, mysql_stmt_errno(stmt),
|
handle_error(query, command, mysql_stmt_errno(stmt),
|
||||||
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3711,10 +3716,10 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
|
|||||||
uint num_fields= mysql_num_fields(res);
|
uint num_fields= mysql_num_fields(res);
|
||||||
|
|
||||||
if (display_metadata)
|
if (display_metadata)
|
||||||
append_metadata(ds, fields, num_fields);
|
append_metadata(ds, fields, num_fields);
|
||||||
|
|
||||||
if (!display_result_vertically)
|
if (!display_result_vertically)
|
||||||
append_table_headings(ds, fields, num_fields);
|
append_table_headings(ds, fields, num_fields);
|
||||||
|
|
||||||
append_stmt_result(ds, stmt, fields, num_fields);
|
append_stmt_result(ds, stmt, fields, num_fields);
|
||||||
|
|
||||||
@ -3736,10 +3741,11 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
|
|||||||
|
|
||||||
/* Append warnings to ds - if there are any */
|
/* Append warnings to ds - if there are any */
|
||||||
if (append_warnings(&ds_execute_warnings, mysql) ||
|
if (append_warnings(&ds_execute_warnings, mysql) ||
|
||||||
ds_prepare_warnings.length ||
|
ds_execute_warnings.length ||
|
||||||
ds_warnings->length)
|
ds_prepare_warnings.length ||
|
||||||
|
ds_warnings->length)
|
||||||
{
|
{
|
||||||
dynstr_append_mem(ds, "Warnings:\n", 10);
|
dynstr_append_mem(ds, "Warnings:\n", 10);
|
||||||
if (ds_warnings->length)
|
if (ds_warnings->length)
|
||||||
dynstr_append_mem(ds, ds_warnings->str,
|
dynstr_append_mem(ds, ds_warnings->str,
|
||||||
ds_warnings->length);
|
ds_warnings->length);
|
||||||
|
@ -4757,12 +4757,39 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
|
|||||||
|
|
||||||
if (!stmt->field_count)
|
if (!stmt->field_count)
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE ||
|
|
||||||
mysql->status != MYSQL_STATUS_GET_RESULT)
|
if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE)
|
||||||
{
|
{
|
||||||
set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
|
set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mysql->status == MYSQL_STATUS_READY &&
|
||||||
|
stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Server side cursor exist, tell server to start sending the rows
|
||||||
|
*/
|
||||||
|
NET *net= &mysql->net;
|
||||||
|
char buff[4 /* statement id */ +
|
||||||
|
4 /* number of rows to fetch */];
|
||||||
|
|
||||||
|
/* Send row request to the server */
|
||||||
|
int4store(buff, stmt->stmt_id);
|
||||||
|
int4store(buff + 4, (int)~0); /* number of rows to fetch */
|
||||||
|
if (cli_advanced_command(mysql, COM_STMT_FETCH, buff, sizeof(buff),
|
||||||
|
NullS, 0, 1))
|
||||||
|
{
|
||||||
|
set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mysql->status != MYSQL_STATUS_GET_RESULT)
|
||||||
|
{
|
||||||
|
set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (result->data)
|
if (result->data)
|
||||||
{
|
{
|
||||||
free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
|
free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
|
||||||
@ -4803,6 +4830,10 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
|
|||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Assert that if there was a cursor, all rows have been fetched */
|
||||||
|
DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY ||
|
||||||
|
(mysql->server_status & SERVER_STATUS_LAST_ROW_SENT));
|
||||||
|
|
||||||
if (stmt->update_max_length)
|
if (stmt->update_max_length)
|
||||||
{
|
{
|
||||||
MYSQL_ROWS *cur= result->data;
|
MYSQL_ROWS *cur= result->data;
|
||||||
|
@ -1049,7 +1049,10 @@ void stmt_fetch_close(Stmt_fetch *fetch)
|
|||||||
reading from the rest.
|
reading from the rest.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
my_bool fetch_n(const char **query_list, unsigned query_count)
|
enum fetch_type { USE_ROW_BY_ROW_FETCH= 0, USE_STORE_RESULT= 1 };
|
||||||
|
|
||||||
|
my_bool fetch_n(const char **query_list, unsigned query_count,
|
||||||
|
enum fetch_type fetch_type)
|
||||||
{
|
{
|
||||||
unsigned open_statements= query_count;
|
unsigned open_statements= query_count;
|
||||||
int rc, error_count= 0;
|
int rc, error_count= 0;
|
||||||
@ -1065,6 +1068,15 @@ my_bool fetch_n(const char **query_list, unsigned query_count)
|
|||||||
query_list[fetch - fetch_array]);
|
query_list[fetch - fetch_array]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fetch_type == USE_STORE_RESULT)
|
||||||
|
{
|
||||||
|
for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
|
||||||
|
{
|
||||||
|
rc= mysql_stmt_store_result(fetch->handle);
|
||||||
|
check_execute(fetch->handle, rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (open_statements)
|
while (open_statements)
|
||||||
{
|
{
|
||||||
for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
|
for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
|
||||||
@ -11867,7 +11879,8 @@ static void test_basic_cursors()
|
|||||||
|
|
||||||
fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables));
|
fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables));
|
||||||
|
|
||||||
fetch_n(queries, sizeof(queries)/sizeof(*queries));
|
fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH);
|
||||||
|
fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11880,7 +11893,8 @@ static void test_cursors_with_union()
|
|||||||
"SELECT t1.id FROM t1 WHERE t1.id < 5"
|
"SELECT t1.id FROM t1 WHERE t1.id < 5"
|
||||||
};
|
};
|
||||||
myheader("test_cursors_with_union");
|
myheader("test_cursors_with_union");
|
||||||
fetch_n(queries, sizeof(queries)/sizeof(*queries));
|
fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH);
|
||||||
|
fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user