summaryrefslogtreecommitdiff
path: root/libmysql
diff options
context:
space:
mode:
authorunknown <konstantin@mysql.com>2005-05-12 11:16:12 +0400
committerunknown <konstantin@mysql.com>2005-05-12 11:16:12 +0400
commit2a8556f32deb10ecf2b1f857a1038330b38af53f (patch)
tree554b6292888722f7406c34e5af18657d1bab8645 /libmysql
parentedcdc57bff956bccd085de326d200a698919ac5c (diff)
downloadmariadb-git-2a8556f32deb10ecf2b1f857a1038330b38af53f.tar.gz
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).
Diffstat (limited to 'libmysql')
-rw-r--r--libmysql/errmsg.c3
-rw-r--r--libmysql/libmysql.c120
2 files changed, 74 insertions, 49 deletions
diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c
index 90ad3aefaca..48e44327d0f 100644
--- a/libmysql/errmsg.c
+++ b/libmysql/errmsg.c
@@ -80,6 +80,7 @@ const char *client_errors[]=
"Row retrieval was canceled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch",
"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[]=
"Row retrieval was canceled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch",
"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[]=
"Row retrieval was canceled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch",
"Prepared statement contains no metadata",
+ "Attempt to read a row while there is no result set associated with the statement"
""
};
#endif
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index bf797dfd580..3e6b29b7602 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -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)
-{
- 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)
{
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));
}
/*