Commit d0e24153 authored by konstantin@mysql.com's avatar konstantin@mysql.com

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.
parent 3c814444
...@@ -95,6 +95,7 @@ extern const char *client_errors[]; /* Error messages */ ...@@ -95,6 +95,7 @@ extern const char *client_errors[]; /* Error messages */
#define CR_FETCH_CANCELED 2050 #define CR_FETCH_CANCELED 2050
#define CR_NO_DATA 2051 #define CR_NO_DATA 2051
#define CR_NO_STMT_METADATA 2052 #define CR_NO_STMT_METADATA 2052
#define CR_ERROR_LAST /*Copy last error nr:*/ 2052 #define CR_NO_RESULT_SET 2053
#define CR_ERROR_LAST /*Copy last error nr:*/ 2053
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */ /* Add error numbers before CR_ERROR_LAST and change it accordingly. */
...@@ -80,6 +80,7 @@ const char *client_errors[]= ...@@ -80,6 +80,7 @@ const char *client_errors[]=
"Row retrieval was canceled by mysql_stmt_close() call", "Row retrieval was canceled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch", "Attempt to read column without prior row fetch",
"Prepared statement contains no metadata", "Prepared statement contains no metadata",
"Attempt to read a row while there is no result set associated with the statement"
"" ""
}; };
...@@ -141,6 +142,7 @@ const char *client_errors[]= ...@@ -141,6 +142,7 @@ const char *client_errors[]=
"Row retrieval was canceled by mysql_stmt_close() call", "Row retrieval was canceled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch", "Attempt to read column without prior row fetch",
"Prepared statement contains no metadata", "Prepared statement contains no metadata",
"Attempt to read a row while there is no result set associated with the statement"
"" ""
}; };
...@@ -200,6 +202,7 @@ const char *client_errors[]= ...@@ -200,6 +202,7 @@ const char *client_errors[]=
"Row retrieval was canceled by mysql_stmt_close() call", "Row retrieval was canceled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch", "Attempt to read column without prior row fetch",
"Prepared statement contains no metadata", "Prepared statement contains no metadata",
"Attempt to read a row while there is no result set associated with the statement"
"" ""
}; };
#endif #endif
......
...@@ -1724,6 +1724,13 @@ static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); ...@@ -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 void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
static my_bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field); 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 Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME
values stored in network buffer. values stored in network buffer.
...@@ -2019,7 +2026,8 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) ...@@ -2019,7 +2026,8 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
/* This is second prepare with another statement */ /* This is second prepare with another statement */
char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ 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 These members must be reset for API to
function in case of error or misuse. function in case of error or misuse.
...@@ -2702,12 +2710,8 @@ static int ...@@ -2702,12 +2710,8 @@ static int
stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)), stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)),
unsigned char **row __attribute__((unused))) unsigned char **row __attribute__((unused)))
{ {
if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) set_stmt_error(stmt, CR_NO_RESULT_SET, unknown_sqlstate);
{ return 1;
set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
return 1;
}
return MYSQL_NO_DATA;
} }
...@@ -2817,7 +2821,8 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) ...@@ -2817,7 +2821,8 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
DBUG_RETURN(1); 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 No need to check for stmt->state: if the statement wasn't
prepared we'll get 'unknown statement handler' error from server. prepared we'll get 'unknown statement handler' error from server.
...@@ -4805,16 +4810,21 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) ...@@ -4805,16 +4810,21 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
DBUG_RETURN(stmt->result.rows); DBUG_RETURN(stmt->result.rows);
} }
my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
{
MYSQL_DATA *result= &stmt->result;
DBUG_ENTER("mysql_stmt_free_result");
DBUG_ASSERT(stmt != 0); /*
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)
{
/* If statement hasn't been prepared there is nothing to reset */
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
{ {
MYSQL *mysql= stmt->mysql; 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) if (result->data)
{ {
...@@ -4824,23 +4834,58 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) ...@@ -4824,23 +4834,58 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
result->rows= 0; result->rows= 0;
stmt->data_cursor= NULL; stmt->data_cursor= NULL;
} }
if (flags & RESET_LONG_DATA)
if (mysql && stmt->field_count &&
(int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE)
{ {
if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) MYSQL_BIND *param= stmt->params, *param_end= param + stmt->param_count;
mysql->unbuffered_fetch_owner= 0; /* Clear long_data_used flags */
if (mysql->status != MYSQL_STATUS_READY) 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 */ if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
(*mysql->methods->flush_use_result)(mysql); mysql->unbuffered_fetch_owner= 0;
mysql->status= MYSQL_STATUS_READY; 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->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) ...@@ -4913,33 +4958,10 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
my_bool STDCALL mysql_stmt_reset(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_ENTER("mysql_stmt_reset");
DBUG_ASSERT(stmt != 0); DBUG_ASSERT(stmt != 0);
/* Reset the client and server sides of the prepared statement */
/* If statement hasnt been prepared there is nothing to reset */ DBUG_RETURN(reset_stmt_handle(stmt, RESET_SERVER_SIDE | RESET_LONG_DATA));
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);
} }
/* /*
......
...@@ -864,6 +864,8 @@ mysql_free_result(MYSQL_RES *result) ...@@ -864,6 +864,8 @@ mysql_free_result(MYSQL_RES *result)
{ {
(*mysql->methods->flush_use_result)(mysql); (*mysql->methods->flush_use_result)(mysql);
mysql->status=MYSQL_STATUS_READY; mysql->status=MYSQL_STATUS_READY;
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
} }
} }
free_rows(result->data); free_rows(result->data);
......
...@@ -4766,13 +4766,13 @@ ER_SUBQUERY_NO_1_ROW 21000 ...@@ -4766,13 +4766,13 @@ ER_SUBQUERY_NO_1_ROW 21000
swe "Subquery returnerade mer n 1 rad" swe "Subquery returnerade mer n 1 rad"
ukr " ¦ i 1 " ukr " ¦ i 1 "
ER_UNKNOWN_STMT_HANDLER ER_UNKNOWN_STMT_HANDLER
dan "Unknown prepared statement handler (%ld) given to %s" dan "Unknown prepared statement handler (%.*s) given to %s"
eng "Unknown prepared statement handler (%.*s) given to %s" eng "Unknown prepared statement handler (%.*s) given to %s"
ger "Unbekannter Prepared-Statement-Handler (%.*s) fr %s angegeben" ger "Unbekannter Prepared-Statement-Handler (%.*s) fr %s angegeben"
por "Desconhecido manipulador de declarao preparado (%.*s) determinado para %s" por "Desconhecido manipulador de declarao preparado (%.*s) determinado para %s"
spa "Desconocido preparado comando handler (%ld) dado para %s" spa "Desconocido preparado comando handler (%.*s) dado para %s"
swe "Oknd PREPARED STATEMENT id (%ld) var given till %s" swe "Oknd PREPARED STATEMENT id (%.*s) var given till %s"
ukr "Unknown prepared statement handler (%ld) given to %s" ukr "Unknown prepared statement handler (%.*s) given to %s"
ER_CORRUPT_HELP_DB ER_CORRUPT_HELP_DB
eng "Help database is corrupt or does not exist" eng "Help database is corrupt or does not exist"
ger "Die Hilfe-Datenbank ist beschdigt oder existiert nicht" ger "Die Hilfe-Datenbank ist beschdigt oder existiert nicht"
...@@ -5352,3 +5352,7 @@ ER_BINLOG_UNSAFE_ROUTINE ...@@ -5352,3 +5352,7 @@ ER_BINLOG_UNSAFE_ROUTINE
eng "This routine is declared to be non-deterministic and to modify data and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable)" eng "This routine is declared to be non-deterministic and to modify data and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable)"
ER_BINLOG_CREATE_ROUTINE_NEED_SUPER ER_BINLOG_CREATE_ROUTINE_NEED_SUPER
eng "You do not have SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable)" eng "You do not have SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable)"
ER_EXEC_STMT_WITH_OPEN_CURSOR
eng "You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it."
ER_STMT_HAS_NO_OPEN_CURSOR
eng "The statement (%d) has no open cursor."
...@@ -135,7 +135,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where) ...@@ -135,7 +135,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where)
if (stmt == 0 || stmt->type() != Item_arena::PREPARED_STATEMENT) if (stmt == 0 || stmt->type() != Item_arena::PREPARED_STATEMENT)
{ {
char llbuf[22]; char llbuf[22];
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where); my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf), llstr(id, llbuf),
where);
return 0; return 0;
} }
return (Prepared_statement *) stmt; return (Prepared_statement *) stmt;
...@@ -1969,7 +1970,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1969,7 +1970,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
{ {
ulong stmt_id= uint4korr(packet); ulong stmt_id= uint4korr(packet);
ulong flags= (ulong) ((uchar) packet[4]); ulong flags= (ulong) ((uchar) packet[4]);
Cursor *cursor= 0; Cursor *cursor;
/* /*
Query text for binary log, or empty string if the query is not put into Query text for binary log, or empty string if the query is not put into
binary log. binary log.
...@@ -1995,6 +1996,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1995,6 +1996,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
cursor= stmt->cursor;
if (cursor && cursor->is_open())
{
my_error(ER_EXEC_STMT_WITH_OPEN_CURSOR, MYF(0));
DBUG_VOID_RETURN;
}
DBUG_ASSERT(thd->free_list == NULL); DBUG_ASSERT(thd->free_list == NULL);
mysql_reset_thd_for_next_command(thd); mysql_reset_thd_for_next_command(thd);
if (flags & (ulong) CURSOR_TYPE_READ_ONLY) if (flags & (ulong) CURSOR_TYPE_READ_ONLY)
...@@ -2013,7 +2021,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -2013,7 +2021,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
else else
{ {
DBUG_PRINT("info",("Using READ_ONLY cursor")); DBUG_PRINT("info",("Using READ_ONLY cursor"));
if (!stmt->cursor && if (!cursor &&
!(cursor= stmt->cursor= new (&stmt->main_mem_root) Cursor())) !(cursor= stmt->cursor= new (&stmt->main_mem_root) Cursor()))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
/* If lex->result is set, mysql_execute_command will use it */ /* If lex->result is set, mysql_execute_command will use it */
...@@ -2208,13 +2216,15 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) ...@@ -2208,13 +2216,15 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
Statement *stmt; Statement *stmt;
DBUG_ENTER("mysql_stmt_fetch"); DBUG_ENTER("mysql_stmt_fetch");
if (!(stmt= thd->stmt_map.find(stmt_id)) || if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch")))
!stmt->cursor || DBUG_VOID_RETURN;
!stmt->cursor->is_open())
if (!stmt->cursor || !stmt->cursor->is_open())
{ {
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "fetch"); my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
thd->current_arena= stmt; thd->current_arena= stmt;
thd->set_n_backup_statement(stmt, &thd->stmt_backup); thd->set_n_backup_statement(stmt, &thd->stmt_backup);
stmt->cursor->init_thd(thd); stmt->cursor->init_thd(thd);
...@@ -2266,6 +2276,9 @@ void mysql_stmt_reset(THD *thd, char *packet) ...@@ -2266,6 +2276,9 @@ void mysql_stmt_reset(THD *thd, char *packet)
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset"))) if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset")))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
if (stmt->cursor && stmt->cursor->is_open())
stmt->cursor->close();
stmt->state= Item_arena::PREPARED; stmt->state= Item_arena::PREPARED;
/* /*
......
...@@ -1742,6 +1742,7 @@ Cursor::init_from_thd(THD *thd) ...@@ -1742,6 +1742,7 @@ Cursor::init_from_thd(THD *thd)
/* /*
XXX: thd->locked_tables is not changed. XXX: thd->locked_tables is not changed.
What problems can we have with it if cursor is open? What problems can we have with it if cursor is open?
TODO: must be fixed because of the prelocked mode.
*/ */
/* /*
TODO: grab thd->free_list here? TODO: grab thd->free_list here?
...@@ -1871,10 +1872,6 @@ Cursor::fetch(ulong num_rows) ...@@ -1871,10 +1872,6 @@ Cursor::fetch(ulong num_rows)
} }
else if (error != NESTED_LOOP_KILLED) else if (error != NESTED_LOOP_KILLED)
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
/* free cursor memory */
free_items(free_list);
free_list= 0;
free_root(&main_mem_root, MYF(0));
} }
} }
...@@ -1914,6 +1911,13 @@ Cursor::close() ...@@ -1914,6 +1911,13 @@ Cursor::close()
} }
join= 0; join= 0;
unit= 0; unit= 0;
free_items(free_list);
free_list= 0;
/*
Must be last, as some memory might be allocated for free purposes,
like in free_tmp_table() (TODO: fix this issue)
*/
free_root(mem_root, MYF(0));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -1922,12 +1926,6 @@ Cursor::~Cursor() ...@@ -1922,12 +1926,6 @@ Cursor::~Cursor()
{ {
if (is_open()) if (is_open())
close(); close();
free_items(free_list);
/*
Must be last, as some memory might be allocated for free purposes,
like in free_tmp_table() (TODO: fix this issue)
*/
free_root(&main_mem_root, MYF(0));
} }
/*********************************************************************/ /*********************************************************************/
......
...@@ -364,6 +364,10 @@ class JOIN :public Sql_alloc ...@@ -364,6 +364,10 @@ class JOIN :public Sql_alloc
/* /*
Server-side cursor (now stands only for basic read-only cursor) Server-side cursor (now stands only for basic read-only cursor)
See class implementation in sql_select.cc See class implementation in sql_select.cc
A cursor has its own runtime state - list of used items and memory root of
used memory - which is different from Prepared statement runtime: it must
be different at least for the purpose of reusing the same prepared
statement for many cursors.
*/ */
class Cursor: public Sql_alloc, public Item_arena class Cursor: public Sql_alloc, public Item_arena
......
...@@ -2269,7 +2269,7 @@ static void test_ps_conj_select() ...@@ -2269,7 +2269,7 @@ static void test_ps_conj_select()
check_execute(stmt, rc); check_execute(stmt, rc);
int_data= 1; int_data= 1;
strcpy(str_data, "hh"); strmov(str_data, "hh");
str_length= strlen(str_data); str_length= strlen(str_data);
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
...@@ -7288,7 +7288,7 @@ static void test_decimal_bug() ...@@ -7288,7 +7288,7 @@ static void test_decimal_bug()
rc= mysql_stmt_bind_param(stmt, bind); rc= mysql_stmt_bind_param(stmt, bind);
check_execute(stmt, rc); check_execute(stmt, rc);
strcpy(data, "8.0"); strmov(data, "8.0");
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc); check_execute(stmt, rc);
...@@ -7306,7 +7306,7 @@ static void test_decimal_bug() ...@@ -7306,7 +7306,7 @@ static void test_decimal_bug()
rc= mysql_stmt_fetch(stmt); rc= mysql_stmt_fetch(stmt);
DIE_UNLESS(rc == MYSQL_NO_DATA); DIE_UNLESS(rc == MYSQL_NO_DATA);
strcpy(data, "5.61"); strmov(data, "5.61");
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc); check_execute(stmt, rc);
...@@ -7331,7 +7331,7 @@ static void test_decimal_bug() ...@@ -7331,7 +7331,7 @@ static void test_decimal_bug()
rc= mysql_stmt_fetch(stmt); rc= mysql_stmt_fetch(stmt);
DIE_UNLESS(rc == MYSQL_NO_DATA); DIE_UNLESS(rc == MYSQL_NO_DATA);
strcpy(data, "10.22"); is_null= 0; strmov(data, "10.22"); is_null= 0;
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc); check_execute(stmt, rc);
...@@ -8510,13 +8510,13 @@ static void test_sqlmode() ...@@ -8510,13 +8510,13 @@ static void test_sqlmode()
myquery(rc); myquery(rc);
/* PIPES_AS_CONCAT */ /* PIPES_AS_CONCAT */
strcpy(query, "SET SQL_MODE= \"PIPES_AS_CONCAT\""); strmov(query, "SET SQL_MODE= \"PIPES_AS_CONCAT\"");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n With %s", query); fprintf(stdout, "\n With %s", query);
rc= mysql_query(mysql, query); rc= mysql_query(mysql, query);
myquery(rc); myquery(rc);
strcpy(query, "INSERT INTO test_piping VALUES(?||?)"); strmov(query, "INSERT INTO test_piping VALUES(?||?)");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n query: %s", query); fprintf(stdout, "\n query: %s", query);
stmt= mysql_simple_prepare(mysql, query); stmt= mysql_simple_prepare(mysql, query);
...@@ -8542,7 +8542,7 @@ static void test_sqlmode() ...@@ -8542,7 +8542,7 @@ static void test_sqlmode()
rc= mysql_stmt_bind_param(stmt, bind); rc= mysql_stmt_bind_param(stmt, bind);
check_execute(stmt, rc); check_execute(stmt, rc);
strcpy(c1, "My"); strcpy(c2, "SQL"); strmov(c1, "My"); strmov(c2, "SQL");
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc); check_execute(stmt, rc);
...@@ -8552,20 +8552,20 @@ static void test_sqlmode() ...@@ -8552,20 +8552,20 @@ static void test_sqlmode()
rc= mysql_query(mysql, "DELETE FROM test_piping"); rc= mysql_query(mysql, "DELETE FROM test_piping");
myquery(rc); myquery(rc);
strcpy(query, "SELECT connection_id ()"); strmov(query, "SELECT connection_id ()");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n query: %s", query); fprintf(stdout, "\n query: %s", query);
stmt= mysql_simple_prepare(mysql, query); stmt= mysql_simple_prepare(mysql, query);
check_stmt_r(stmt); check_stmt_r(stmt);
/* ANSI */ /* ANSI */
strcpy(query, "SET SQL_MODE= \"ANSI\""); strmov(query, "SET SQL_MODE= \"ANSI\"");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n With %s", query); fprintf(stdout, "\n With %s", query);
rc= mysql_query(mysql, query); rc= mysql_query(mysql, query);
myquery(rc); myquery(rc);
strcpy(query, "INSERT INTO test_piping VALUES(?||?)"); strmov(query, "INSERT INTO test_piping VALUES(?||?)");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n query: %s", query); fprintf(stdout, "\n query: %s", query);
stmt= mysql_simple_prepare(mysql, query); stmt= mysql_simple_prepare(mysql, query);
...@@ -8576,7 +8576,7 @@ static void test_sqlmode() ...@@ -8576,7 +8576,7 @@ static void test_sqlmode()
rc= mysql_stmt_bind_param(stmt, bind); rc= mysql_stmt_bind_param(stmt, bind);
check_execute(stmt, rc); check_execute(stmt, rc);
strcpy(c1, "My"); strcpy(c2, "SQL"); strmov(c1, "My"); strmov(c2, "SQL");
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc); check_execute(stmt, rc);
...@@ -8584,7 +8584,7 @@ static void test_sqlmode() ...@@ -8584,7 +8584,7 @@ static void test_sqlmode()
verify_col_data("test_piping", "name", "MySQL"); verify_col_data("test_piping", "name", "MySQL");
/* ANSI mode spaces ... */ /* ANSI mode spaces ... */
strcpy(query, "SELECT connection_id ()"); strmov(query, "SELECT connection_id ()");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n query: %s", query); fprintf(stdout, "\n query: %s", query);
stmt= mysql_simple_prepare(mysql, query); stmt= mysql_simple_prepare(mysql, query);
...@@ -8604,13 +8604,13 @@ static void test_sqlmode() ...@@ -8604,13 +8604,13 @@ static void test_sqlmode()
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
/* IGNORE SPACE MODE */ /* IGNORE SPACE MODE */
strcpy(query, "SET SQL_MODE= \"IGNORE_SPACE\""); strmov(query, "SET SQL_MODE= \"IGNORE_SPACE\"");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n With %s", query); fprintf(stdout, "\n With %s", query);
rc= mysql_query(mysql, query); rc= mysql_query(mysql, query);
myquery(rc); myquery(rc);
strcpy(query, "SELECT connection_id ()"); strmov(query, "SELECT connection_id ()");
if (!opt_silent) if (!opt_silent)
fprintf(stdout, "\n query: %s", query); fprintf(stdout, "\n query: %s", query);
stmt= mysql_simple_prepare(mysql, query); stmt= mysql_simple_prepare(mysql, query);
...@@ -9115,7 +9115,7 @@ static void test_bug2248() ...@@ -9115,7 +9115,7 @@ static void test_bug2248()
/* This too should not hang but should return proper error */ /* This too should not hang but should return proper error */
rc= mysql_stmt_fetch(stmt); rc= mysql_stmt_fetch(stmt);
DIE_UNLESS(rc == MYSQL_NO_DATA); DIE_UNLESS(rc == 1);
/* This too should not hang but should not bark */ /* This too should not hang but should not bark */
rc= mysql_stmt_store_result(stmt); rc= mysql_stmt_store_result(stmt);
...@@ -9124,7 +9124,7 @@ static void test_bug2248() ...@@ -9124,7 +9124,7 @@ static void test_bug2248()
/* This should return proper error */ /* This should return proper error */
rc= mysql_stmt_fetch(stmt); rc= mysql_stmt_fetch(stmt);
check_execute_r(stmt, rc); check_execute_r(stmt, rc);
DIE_UNLESS(rc == MYSQL_NO_DATA); DIE_UNLESS(rc == 1);
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
...@@ -10266,7 +10266,7 @@ static void test_union_param() ...@@ -10266,7 +10266,7 @@ static void test_union_param()
my_bool my_null= FALSE; my_bool my_null= FALSE;
myheader("test_union_param"); myheader("test_union_param");
strcpy(my_val, "abc"); strmov(my_val, "abc");
query= (char*)"select ? as my_col union distinct select ?"; query= (char*)"select ? as my_col union distinct select ?";
stmt= mysql_simple_prepare(mysql, query); stmt= mysql_simple_prepare(mysql, query);
...@@ -10548,14 +10548,14 @@ static void test_bug3796() ...@@ -10548,14 +10548,14 @@ static void test_bug3796()
if (!opt_silent) if (!opt_silent)
printf("Concat result: '%s'\n", out_buff); printf("Concat result: '%s'\n", out_buff);
check_execute(stmt, rc); check_execute(stmt, rc);
strcpy(canonical_buff, concat_arg0); strmov(canonical_buff, concat_arg0);
strcat(canonical_buff, "ONE"); strcat(canonical_buff, "ONE");
DIE_UNLESS(strlen(canonical_buff) == out_length && DIE_UNLESS(strlen(canonical_buff) == out_length &&
strncmp(out_buff, canonical_buff, out_length) == 0); strncmp(out_buff, canonical_buff, out_length) == 0);
rc= mysql_stmt_fetch(stmt); rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc); check_execute(stmt, rc);
strcpy(canonical_buff + strlen(concat_arg0), "TWO"); strmov(canonical_buff + strlen(concat_arg0), "TWO");
DIE_UNLESS(strlen(canonical_buff) == out_length && DIE_UNLESS(strlen(canonical_buff) == out_length &&
strncmp(out_buff, canonical_buff, out_length) == 0); strncmp(out_buff, canonical_buff, out_length) == 0);
if (!opt_silent) if (!opt_silent)
...@@ -10852,7 +10852,8 @@ static void test_view() ...@@ -10852,7 +10852,8 @@ static void test_view()
rc= mysql_stmt_prepare(stmt, query, strlen(query)); rc= mysql_stmt_prepare(stmt, query, strlen(query));
check_execute(stmt, rc); check_execute(stmt, rc);
strcpy(str_data, "TEST"); strmov(str_data, "TEST");
bzero(bind, sizeof(bind));
bind[0].buffer_type= FIELD_TYPE_STRING; bind[0].buffer_type= FIELD_TYPE_STRING;
bind[0].buffer= (char *)&str_data; bind[0].buffer= (char *)&str_data;
bind[0].buffer_length= 50; bind[0].buffer_length= 50;
...@@ -10971,8 +10972,9 @@ static void test_view_2where() ...@@ -10971,8 +10972,9 @@ static void test_view_2where()
" AENAME,T0001.DEPENDVARS AS DEPENDVARS,T0001.INACTIVE AS " " AENAME,T0001.DEPENDVARS AS DEPENDVARS,T0001.INACTIVE AS "
" INACTIVE from LTDX T0001 where (T0001.SRTF2 = 0)"); " INACTIVE from LTDX T0001 where (T0001.SRTF2 = 0)");
myquery(rc); myquery(rc);
bzero(bind, sizeof(bind));
for (i=0; i < 8; i++) { for (i=0; i < 8; i++) {
strcpy(parms[i], "1"); strmov(parms[i], "1");
bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
bind[i].buffer = (char *)&parms[i]; bind[i].buffer = (char *)&parms[i];
bind[i].buffer_length = 100; bind[i].buffer_length = 100;
...@@ -11019,6 +11021,7 @@ static void test_view_star() ...@@ -11019,6 +11021,7 @@ static void test_view_star()
myquery(rc); myquery(rc);
rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1"); rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1");
myquery(rc); myquery(rc);
bzero(bind, sizeof(bind));
for (i= 0; i < 2; i++) { for (i= 0; i < 2; i++) {
sprintf((char *)&parms[i], "%d", i); sprintf((char *)&parms[i], "%d", i);
bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
...@@ -11084,6 +11087,7 @@ static void test_view_insert() ...@@ -11084,6 +11087,7 @@ static void test_view_insert()
rc= mysql_stmt_prepare(select_stmt, query, strlen(query)); rc= mysql_stmt_prepare(select_stmt, query, strlen(query));
check_execute(select_stmt, rc); check_execute(select_stmt, rc);
bzero(bind, sizeof(bind));
bind[0].buffer_type = FIELD_TYPE_LONG; bind[0].buffer_type = FIELD_TYPE_LONG;
bind[0].buffer = (char *)&my_val; bind[0].buffer = (char *)&my_val;
bind[0].length = &my_length; bind[0].length = &my_length;
...@@ -11187,6 +11191,7 @@ static void test_view_insert_fields() ...@@ -11187,6 +11191,7 @@ static void test_view_insert_fields()
" F7F8 AS F7F8, F6N4 AS F6N4, F5C8 AS F5C8, F9D8 AS F9D8" " F7F8 AS F7F8, F6N4 AS F6N4, F5C8 AS F5C8, F9D8 AS F9D8"
" from t1 T0001"); " from t1 T0001");
bzero(bind, sizeof(bind));
for (i= 0; i < 11; i++) for (i= 0; i < 11; i++)
{ {
l[i]= 20; l[i]= 20;
...@@ -11194,7 +11199,7 @@ static void test_view_insert_fields() ...@@ -11194,7 +11199,7 @@ static void test_view_insert_fields()
bind[i].is_null= 0; bind[i].is_null= 0;
bind[i].buffer= (char *)&parm[i]; bind[i].buffer= (char *)&parm[i];
strcpy(parm[i], "1"); strmov(parm[i], "1");
bind[i].buffer_length= 2; bind[i].buffer_length= 2;
bind[i].length= &l[i]; bind[i].length= &l[i];
} }
...@@ -12707,6 +12712,7 @@ from t2);"); ...@@ -12707,6 +12712,7 @@ from t2);");
rc= mysql_query(mysql, "DROP VIEW v1"); rc= mysql_query(mysql, "DROP VIEW v1");
myquery(rc); myquery(rc);
rc= mysql_query(mysql, "DROP TABLE t1, t2"); rc= mysql_query(mysql, "DROP TABLE t1, t2");
mysql_free_result(res);
myquery(rc); myquery(rc);
} }
...@@ -12923,6 +12929,152 @@ static void test_bug9520() ...@@ -12923,6 +12929,152 @@ static void test_bug9520()
} }
/*
We can't have more than one cursor open for a prepared statement.
Test re-executions of a PS with cursor; mysql_stmt_reset must close
the cursor attached to the statement, if there is one.
*/
static void test_bug9478()
{
MYSQL_STMT *stmt;
MYSQL_BIND bind[1];
char a[6];
ulong a_len;
int rc, i;
myheader("test_bug9478");
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)");
rc= mysql_query(mysql, "insert into t1 (id, name) values "
" (1, 'aaa'), (2, 'bbb'), (3, 'ccc')");
myquery(rc);
stmt= open_cursor("select name from t1 where id=2");
bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (char*) a;
bind[0].buffer_length= sizeof(a);
bind[0].length= &a_len;
mysql_stmt_bind_result(stmt, bind);
for (i= 0; i < 5; i++)
{
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc);
if (!opt_silent && i == 0)
printf("Fetched row: %s\n", a);
/*
The query above is a one-row result set. Therefore, there is no
cursor associated with it, as the server won't bother with opening
a cursor for a one-row result set. The first row was read from the
server in the fetch above. But there is eof packet pending in the
network. mysql_stmt_execute will flush the packet and successfully
execute the statement.
*/
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc);
if (!opt_silent && i == 0)
printf("Fetched row: %s\n", a);
rc= mysql_stmt_fetch(stmt);
DIE_UNLESS(rc == MYSQL_NO_DATA);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc);
if (!opt_silent && i == 0)
printf("Fetched row: %s\n", a);
rc= mysql_stmt_reset(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
DIE_UNLESS(rc && mysql_stmt_errno(stmt));
if (!opt_silent && i == 0)
printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));
}
rc= mysql_stmt_close(stmt);
DIE_UNLESS(rc == 0);
/* Test the case with a server side cursor */
stmt= open_cursor("select name from t1");
mysql_stmt_bind_result(stmt, bind);
for (i= 0; i < 5; i++)
{
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc);
if (!opt_silent && i == 0)
printf("Fetched row: %s\n", a);
/*
Although protocol-wise an attempt to execute a statement which
already has an open cursor associated with it will yield an error,
the client library behavior tested here is consistent with
the non-cursor execution scenario: mysql_stmt_execute will
silently close the cursor if necessary.
*/
{
char buff[9];
bzero(buff, sizeof(buff));
/* Fill in the execute packet */
int4store(buff, stmt->stmt_id);
int4store(buff+5, 1);
rc= ((*mysql->methods->advanced_command)(mysql, COM_EXECUTE, buff,
sizeof(buff), 0,0,1) ||
(*mysql->methods->read_query_result)(mysql));
DIE_UNLESS(rc);
if (!opt_silent && i == 0)
printf("Got error (as expected): %s\n", mysql_error(mysql));
}
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
while (! (rc= mysql_stmt_fetch(stmt)))
{
if (!opt_silent && i == 0)
printf("Fetched row: %s\n", a);
}
DIE_UNLESS(rc == MYSQL_NO_DATA);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
check_execute(stmt, rc);
if (!opt_silent && i == 0)
printf("Fetched row: %s\n", a);
rc= mysql_stmt_reset(stmt);
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
DIE_UNLESS(rc && mysql_stmt_errno(stmt));
if (!opt_silent && i == 0)
printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));
}
rc= mysql_stmt_close(stmt);
DIE_UNLESS(rc == 0);
rc= mysql_query(mysql, "drop table t1");
myquery(rc);
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
*/ */
...@@ -13153,6 +13305,7 @@ static struct my_tests_st my_tests[]= { ...@@ -13153,6 +13305,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug8880", test_bug8880 }, { "test_bug8880", test_bug8880 },
{ "test_bug9159", test_bug9159 }, { "test_bug9159", test_bug9159 },
{ "test_bug9520", test_bug9520 }, { "test_bug9520", test_bug9520 },
{ "test_bug9478", test_bug9478 },
{ 0, 0 } { 0, 0 }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment