Commit 57ba4322 authored by Staale Smedseng's avatar Staale Smedseng

Recommit of bug 43560 after merge with mysql-5.1-bugteam

parent 182df740
......@@ -924,6 +924,7 @@ void end_server(MYSQL *mysql)
vio_delete(mysql->net.vio);
reset_sigpipe(mysql);
mysql->net.vio= 0; /* Marker */
mysql_prune_stmt_list(mysql);
}
net_end(&mysql->net);
free_old_query(mysql);
......@@ -2526,30 +2527,9 @@ my_bool mysql_reconnect(MYSQL *mysql)
tmp_mysql.reconnect= 1;
tmp_mysql.free_me= mysql->free_me;
/*
For each stmt in mysql->stmts, move it to tmp_mysql if it is
in state MYSQL_STMT_INIT_DONE, otherwise close it.
*/
{
LIST *element= mysql->stmts;
for (; element; element= element->next)
{
MYSQL_STMT *stmt= (MYSQL_STMT *) element->data;
if (stmt->state != MYSQL_STMT_INIT_DONE)
{
stmt->mysql= 0;
stmt->last_errno= CR_SERVER_LOST;
strmov(stmt->last_error, ER(CR_SERVER_LOST));
strmov(stmt->sqlstate, unknown_sqlstate);
}
else
{
tmp_mysql.stmts= list_add(tmp_mysql.stmts, &stmt->list);
}
/* No need to call list_delete for statement here */
}
mysql->stmts= NULL;
}
/* Move prepared statements (if any) over to the new mysql object */
tmp_mysql.stmts= mysql->stmts;
mysql->stmts= 0;
/* Don't free options as these are now used in tmp_mysql */
bzero((char*) &mysql->options,sizeof(mysql->options));
......@@ -2639,6 +2619,46 @@ static void mysql_close_free(MYSQL *mysql)
}
/**
For use when the connection to the server has been lost (in which case
the server has discarded all information about prepared statements
associated with the connection).
Mark all statements in mysql->stmts by setting stmt->mysql= 0 if the
statement has transitioned beyond the MYSQL_STMT_INIT_DONE state, and
unlink the statement from the mysql->stmts list.
The remaining pruned list of statements (if any) is kept in mysql->stmts.
@param mysql pointer to the MYSQL object
@return none
*/
void mysql_prune_stmt_list(MYSQL *mysql)
{
LIST *element= mysql->stmts;
LIST *pruned_list= 0;
for (; element; element= element->next)
{
MYSQL_STMT *stmt= (MYSQL_STMT *) element->data;
if (stmt->state != MYSQL_STMT_INIT_DONE)
{
stmt->mysql= 0;
stmt->last_errno= CR_SERVER_LOST;
strmov(stmt->last_error, ER(CR_SERVER_LOST));
strmov(stmt->sqlstate, unknown_sqlstate);
}
else
{
pruned_list= list_add(pruned_list, element);
}
}
mysql->stmts= pruned_list;
}
/*
Clear connection pointer of every statement: this is necessary
to give error on attempt to use a prepared statement of closed
......
......@@ -2461,6 +2461,9 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
DBUG_VOID_RETURN;
}
......
......@@ -103,7 +103,7 @@ if (!opt_silent) \
static void print_error(const char *msg);
static void print_st_error(MYSQL_STMT *stmt, const char *msg);
static void client_disconnect(void);
static void client_disconnect(MYSQL* mysql, my_bool drop_db);
/*
......@@ -271,10 +271,20 @@ mysql_simple_prepare(MYSQL *mysql_arg, const char *query)
}
/* Connect to the server */
/**
Connect to the server with options given by arguments to this application,
stored in global variables opt_host, opt_user, opt_password, opt_db,
opt_port and opt_unix_socket.
@param flag[in] client_flag passed on to mysql_real_connect
@param protocol[in] MYSQL_PROTOCOL_* to use for this connection
@param auto_reconnect[in] set to 1 for auto reconnect
static void client_connect(ulong flag)
@return pointer to initialized and connected MYSQL object
*/
static MYSQL* client_connect(ulong flag, uint protocol, my_bool auto_reconnect)
{
MYSQL* mysql;
int rc;
static char query[MAX_TEST_QUERY_LENGTH];
myheader_r("client_connect");
......@@ -291,6 +301,7 @@ static void client_connect(ulong flag)
}
/* enable local infile, in non-binary builds often disabled by default */
mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0);
mysql_options(mysql, MYSQL_OPT_PROTOCOL, &protocol);
if (!(mysql_real_connect(mysql, opt_host, opt_user,
opt_password, opt_db ? opt_db:"test", opt_port,
......@@ -302,7 +313,7 @@ static void client_connect(ulong flag)
fprintf(stdout, "\n Check the connection options using --help or -?\n");
exit(1);
}
mysql->reconnect= 1;
mysql->reconnect= auto_reconnect;
if (!opt_silent)
fprintf(stdout, "OK");
......@@ -329,18 +340,22 @@ static void client_connect(ulong flag)
if (!opt_silent)
fprintf(stdout, "OK");
return mysql;
}
/* Close the connection */
static void client_disconnect()
static void client_disconnect(MYSQL* mysql, my_bool drop_db)
{
static char query[MAX_TEST_QUERY_LENGTH];
myheader_r("client_disconnect");
if (mysql)
{
if (drop_db)
{
if (!opt_silent)
fprintf(stdout, "\n dropping the test database '%s' ...", current_db);
......@@ -349,6 +364,7 @@ static void client_disconnect()
mysql_query(mysql, query);
if (!opt_silent)
fprintf(stdout, "OK");
}
if (!opt_silent)
fprintf(stdout, "\n closing the connection ...");
......@@ -17712,6 +17728,100 @@ static void test_bug40365(void)
}
/**
Subtest for Bug#43560. Verifies that a loss of connection on the server side
is handled well by the mysql_stmt_execute() call, i.e., no SIGSEGV due to
a vio socket that is cleared upon closed connection.
Assumes the presence of the close_conn_after_stmt_execute debug feature in
the server. Verifies that it is connected to a debug server before proceeding
with the test.
*/
static void test_bug43560(void)
{
MYSQL* conn;
uint rc;
MYSQL_STMT *stmt= 0;
MYSQL_BIND bind;
my_bool is_null= 0;
const uint BUFSIZE= 256;
char buffer[BUFSIZE];
const char* values[] = {"eins", "zwei", "drei", "viele", NULL};
const char insert_str[] = "INSERT INTO t1 (c2) VALUES (?)";
unsigned long length;
DBUG_ENTER("test_bug43560");
myheader("test_bug43560");
/* Make sure we only run against a debug server. */
if (!strstr(mysql->server_version, "debug"))
{
fprintf(stdout, "Skipping test_bug43560: server not DEBUG version\n");
DBUG_VOID_RETURN;
}
/*
Set up a separate connection for this test to avoid messing up the
general MYSQL object used in other subtests. Use TCP protocol to avoid
problems with the buffer semantics of AF_UNIX, and turn off auto reconnect.
*/
conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
rc= mysql_query(conn, "DROP TABLE IF EXISTS t1");
myquery(rc);
rc= mysql_query(conn,
"CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT, c2 CHAR(10))");
myquery(rc);
stmt= mysql_stmt_init(conn);
check_stmt(stmt);
rc= mysql_stmt_prepare(stmt, insert_str, strlen(insert_str));
check_execute(stmt, rc);
bind.buffer_type= MYSQL_TYPE_STRING;
bind.buffer_length= BUFSIZE;
bind.buffer= buffer;
bind.is_null= &is_null;
bind.length= &length;
rc= mysql_stmt_bind_param(stmt, &bind);
check_execute(stmt, rc);
/* First execute; should succeed. */
strncpy(buffer, values[0], BUFSIZE);
length= strlen(buffer);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
/*
Set up the server to close this session's server-side socket after
next execution of prep statement.
*/
rc= mysql_query(conn,"SET SESSION debug='+d,close_conn_after_stmt_execute'");
myquery(rc);
/* Second execute; should fail due to socket closed during execution. */
strncpy(buffer, values[1], BUFSIZE);
length= strlen(buffer);
rc= mysql_stmt_execute(stmt);
DIE_UNLESS(rc && mysql_stmt_errno(stmt) == CR_SERVER_LOST);
/*
Third execute; should fail (connection already closed), or SIGSEGV in
case of a Bug#43560 type regression in which case the whole test fails.
*/
strncpy(buffer, values[2], BUFSIZE);
length= strlen(buffer);
rc= mysql_stmt_execute(stmt);
DIE_UNLESS(rc && mysql_stmt_errno(stmt) == CR_SERVER_LOST);
client_disconnect(conn, 0);
rc= mysql_query(mysql, "DROP TABLE t1");
myquery(rc);
DBUG_VOID_RETURN;
}
/**
Bug#36326: nested transaction and select
*/
......@@ -18140,6 +18250,7 @@ static struct my_tests_st my_tests[]= {
{ "test_wl4166_2", test_wl4166_2 },
{ "test_bug38486", test_bug38486 },
{ "test_bug40365", test_bug40365 },
{ "test_bug43560", test_bug43560 },
#ifdef HAVE_QUERY_CACHE
{ "test_bug36326", test_bug36326 },
#endif
......@@ -18268,7 +18379,8 @@ int main(int argc, char **argv)
(char**) embedded_server_groups))
DIE("Can't initialize MySQL server");
client_connect(0); /* connect to server */
/* connect to server with no flags, default protocol, auto reconnect true */
mysql= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 1);
total_time= 0;
for (iter_count= 1; iter_count <= opt_count; iter_count++)
......@@ -18298,7 +18410,7 @@ int main(int argc, char **argv)
fprintf(stderr, "\n\nGiven test not found: '%s'\n", *argv);
fprintf(stderr, "See legal test names with %s -T\n\nAborting!\n",
my_progname);
client_disconnect();
client_disconnect(mysql, 1);
free_defaults(defaults_argv);
exit(1);
}
......@@ -18311,7 +18423,7 @@ int main(int argc, char **argv)
/* End of tests */
}
client_disconnect(); /* disconnect from server */
client_disconnect(mysql, 1); /* disconnect from server */
free_defaults(defaults_argv);
print_test_output();
......
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