1
0
mirror of https://github.com/MariaDB/server.git synced 2025-06-12 01:53:02 +03:00

A fix and test case for Bug#9478 "mysql_stmt_attr_set mysql_stmt_execute"

(crash on attempt to re-execute a statement with an open cursor) + 
post-review fixes.


include/errmsg.h:
  Add a special error message when we attempt to mysql_stmt_fetch
  from a statement which has no result set.
libmysql/errmsg.c:
  Error message text for CR_NO_RESULT_SET
libmysql/libmysql.c:
  Move the code which frees result sets on client and closes the cursor
  on server, resets long data state on client and server.
  This makes one function out of two (mysql_stmt_reset and
  mysql_stmt_free_result), thus aggregating all related reset work
  in one place.
sql-common/client.c:
  Fix one place where we flushed the pending result set of a statement,
  but didn't set unbuffered_fetch_cancelled flag.
sql/share/errmsg.txt:
  Fix format of ER_UNKNOWN_STMT_HANDLER error message (needs to
  be fixed separately in 4.1). Add two new error messages 
  for the case when we fetch from when there is no cursor
  and for the case when we attempt to execute a statement while there is
  a cursor.
sql/sql_prepare.cc:
  Return error when we fetch while there is no open cursor and
  when we call execute while there is a pending cursor.
  Fix mysql_stmt_reset to close the open cursor if there is any.
sql/sql_select.cc:
  free_items and free_root moved to Cursor::close().
sql/sql_select.h:
  A comment added.
tests/mysql_client_test.c:
  A test case for Bug#9478, test the case of mysql_stmt_reset
  called for client-side cached result set and for the case with open cursor.
  All strcpy replaced with strmov (review request).
This commit is contained in:
unknown
2005-05-12 11:16:12 +04:00
parent edcdc57bff
commit 2a8556f32d
9 changed files with 294 additions and 94 deletions

View File

@ -1724,6 +1724,13 @@ static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);
static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
static my_bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field);
/* Auxilary function used to reset statement handle. */
#define RESET_SERVER_SIDE 1
#define RESET_LONG_DATA 2
static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags);
/*
Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME
values stored in network buffer.
@ -2019,7 +2026,8 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
/* This is second prepare with another statement */
char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */
mysql_stmt_free_result(stmt);
if (reset_stmt_handle(stmt, RESET_LONG_DATA))
DBUG_RETURN(1);
/*
These members must be reset for API to
function in case of error or misuse.
@ -2702,12 +2710,8 @@ static int
stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)),
unsigned char **row __attribute__((unused)))
{
if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
{
set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
return 1;
}
return MYSQL_NO_DATA;
set_stmt_error(stmt, CR_NO_RESULT_SET, unknown_sqlstate);
return 1;
}
@ -2817,7 +2821,8 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
DBUG_RETURN(1);
}
mysql_stmt_free_result(stmt);
if (reset_stmt_handle(stmt, 0))
DBUG_RETURN(1);
/*
No need to check for stmt->state: if the statement wasn't
prepared we'll get 'unknown statement handler' error from server.
@ -4805,16 +4810,21 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
DBUG_RETURN(stmt->result.rows);
}
my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
/*
Free the client side memory buffers, reset long data state
on client if necessary, and reset the server side statement if
this has been requested.
*/
static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags)
{
MYSQL_DATA *result= &stmt->result;
DBUG_ENTER("mysql_stmt_free_result");
DBUG_ASSERT(stmt != 0);
/* If statement hasn't been prepared there is nothing to reset */
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
{
MYSQL *mysql= stmt->mysql;
MYSQL_DATA *result= &stmt->result;
my_bool has_cursor= stmt->read_row_func == stmt_read_row_from_cursor;
if (result->data)
{
@ -4824,23 +4834,58 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
result->rows= 0;
stmt->data_cursor= NULL;
}
if (mysql && stmt->field_count &&
(int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE)
if (flags & RESET_LONG_DATA)
{
if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
mysql->unbuffered_fetch_owner= 0;
if (mysql->status != MYSQL_STATUS_READY)
MYSQL_BIND *param= stmt->params, *param_end= param + stmt->param_count;
/* Clear long_data_used flags */
for (; param < param_end; param++)
param->long_data_used= 0;
}
stmt->read_row_func= stmt_read_row_no_data;
if (mysql)
{
if ((int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE)
{
/* There is a result set and it belongs to this statement */
(*mysql->methods->flush_use_result)(mysql);
mysql->status= MYSQL_STATUS_READY;
if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
mysql->unbuffered_fetch_owner= 0;
if (stmt->field_count && mysql->status != MYSQL_STATUS_READY)
{
/* There is a result set and it belongs to this statement */
(*mysql->methods->flush_use_result)(mysql);
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
mysql->status= MYSQL_STATUS_READY;
}
}
if (has_cursor || (flags & RESET_SERVER_SIDE))
{
/*
Reset the server side statement and close the server side
cursor if it exists.
*/
char buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */
int4store(buff, stmt->stmt_id);
if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff,
sizeof(buff), 0, 0, 0))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
stmt->state= MYSQL_STMT_INIT_DONE;
return 1;
}
}
}
stmt->state= MYSQL_STMT_PREPARE_DONE;
stmt->read_row_func= stmt_read_row_no_data;
}
DBUG_RETURN(0);
return 0;
}
my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
{
DBUG_ENTER("mysql_stmt_free_result");
/* Free the client side and close the server side cursor if there is one */
DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA));
}
/********************************************************************
@ -4913,33 +4958,10 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
{
char buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */
MYSQL *mysql;
MYSQL_BIND *param, *param_end;
DBUG_ENTER("mysql_stmt_reset");
DBUG_ASSERT(stmt != 0);
/* If statement hasnt been prepared there is nothing to reset */
if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
DBUG_RETURN(0);
mysql= stmt->mysql->last_used_con;
int4store(buff, stmt->stmt_id); /* Send stmt id to server */
if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff,
sizeof(buff), 0, 0, 0))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
DBUG_RETURN(1);
}
/* Clear long_data_used for next call (as we do in mysql_stmt_execute() */
for (param= stmt->params, param_end= param + stmt->param_count;
param < param_end;
param++)
param->long_data_used= 0;
DBUG_RETURN(0);
/* Reset the client and server sides of the prepared statement */
DBUG_RETURN(reset_stmt_handle(stmt, RESET_SERVER_SIDE | RESET_LONG_DATA));
}
/*