Commit 420c956d authored by konstantin@mysql.com's avatar konstantin@mysql.com

Intermediate commit of client library (cleanups + fixes of 3 items from

flaws list)
TODO: 
 * verify that no sequence of API calls produces SIGSEGV.
 That is, verify that mysql_stmt_init  -> mysql_stmt_fetch is OK,
 or mysql_stmt_prepare -> mysql_stmt_fetch_column is OK and sets
 meaningful error.
 * remove alloc_stmt_fields call
 * revise stmt->state codes and statement states.
 * there are other items in prepared statements 'to fix' document.

Done:
 - cleanups and comments
 - revision of prepared statement error codes.
 - mysql_stmt_prepare is now can always be called (that is, you can reprepare
   a statement)
 - new implementation of mysql_stmt_close and fetch cancellation
parent e90c78ce
...@@ -67,7 +67,7 @@ extern const char *client_errors[]; /* Error messages */ ...@@ -67,7 +67,7 @@ extern const char *client_errors[]; /* Error messages */
/* new 4.1 error codes */ /* new 4.1 error codes */
#define CR_NULL_POINTER 2028 #define CR_NULL_POINTER 2028
#define CR_NO_PREPARE_STMT 2029 #define CR_NO_PREPARE_STMT 2029
#define CR_NOT_ALL_PARAMS_BOUND 2030 #define CR_PARAMS_NOT_BOUND 2030
#define CR_DATA_TRUNCATED 2031 #define CR_DATA_TRUNCATED 2031
#define CR_NO_PARAMETERS_EXISTS 2032 #define CR_NO_PARAMETERS_EXISTS 2032
#define CR_INVALID_PARAMETER_NO 2033 #define CR_INVALID_PARAMETER_NO 2033
...@@ -87,3 +87,5 @@ extern const char *client_errors[]; /* Error messages */ ...@@ -87,3 +87,5 @@ extern const char *client_errors[]; /* Error messages */
#define CR_CONN_UNKNOW_PROTOCOL 2046 #define CR_CONN_UNKNOW_PROTOCOL 2046
#define CR_INVALID_CONN_HANDLE 2047 #define CR_INVALID_CONN_HANDLE 2047
#define CR_SECURE_AUTH 2048 #define CR_SECURE_AUTH 2048
#define CR_FETCH_CANCELLED 2049
#define CR_NO_DATA 2050
...@@ -256,6 +256,11 @@ typedef struct st_mysql ...@@ -256,6 +256,11 @@ typedef struct st_mysql
LIST *stmts; /* list of all statements */ LIST *stmts; /* list of all statements */
const struct st_mysql_methods *methods; const struct st_mysql_methods *methods;
void *thd; void *thd;
/*
Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag
from mysql_stmt_close if close had to cancel result set of this object.
*/
my_bool *unbuffered_fetch_owner;
} MYSQL; } MYSQL;
typedef struct st_mysql_res { typedef struct st_mysql_res {
...@@ -270,6 +275,8 @@ typedef struct st_mysql_res { ...@@ -270,6 +275,8 @@ typedef struct st_mysql_res {
MYSQL_ROW row; /* If unbuffered read */ MYSQL_ROW row; /* If unbuffered read */
MYSQL_ROW current_row; /* buffer to current row */ MYSQL_ROW current_row; /* buffer to current row */
my_bool eof; /* Used by mysql_fetch_row */ my_bool eof; /* Used by mysql_fetch_row */
/* mysql_stmt_close() had to cancel this result */
my_bool unbuffered_fetch_cancelled;
const struct st_mysql_methods *methods; const struct st_mysql_methods *methods;
} MYSQL_RES; } MYSQL_RES;
...@@ -479,7 +486,11 @@ my_bool STDCALL mysql_read_query_result(MYSQL *mysql); ...@@ -479,7 +486,11 @@ my_bool STDCALL mysql_read_query_result(MYSQL *mysql);
*/ */
/* statement state */ /* statement state */
enum PREP_STMT_STATE { MY_ST_UNKNOWN, MY_ST_PREPARE, MY_ST_EXECUTE }; enum enum_mysql_stmt_state
{
MYSQL_STMT_INIT_DONE= 1, MYSQL_STMT_PREPARE_DONE, MYSQL_STMT_EXECUTE_DONE,
MYSQL_STMT_FETCH_DONE
};
/* /*
client TIME structure to handle TIME, DATE and TIMESTAMP directly in client TIME structure to handle TIME, DATE and TIMESTAMP directly in
...@@ -525,31 +536,34 @@ typedef struct st_mysql_bind ...@@ -525,31 +536,34 @@ typedef struct st_mysql_bind
/* statement handler */ /* statement handler */
typedef struct st_mysql_stmt typedef struct st_mysql_stmt
{ {
MYSQL *mysql; /* connection handle */ MEM_ROOT mem_root; /* root allocations */
MYSQL_BIND *params; /* input parameters */
MYSQL_RES *result; /* resultset */
MYSQL_BIND *bind; /* row binding */
MYSQL_FIELD *fields; /* prepare meta info */
LIST list; /* list to keep track of all stmts */ LIST list; /* list to keep track of all stmts */
unsigned char *current_row; /* unbuffered row */ MYSQL *mysql; /* connection handle */
unsigned char *last_fetched_buffer; /* last fetched column buffer */ MYSQL_BIND *params; /* input parameters */
char *query; /* query buffer */ MYSQL_BIND *bind; /* output parameters */
MEM_ROOT mem_root; /* root allocations */ MYSQL_FIELD *fields; /* result set metadata */
my_ulonglong last_fetched_column; /* last fetched column */ MYSQL_RES *result; /* cached result set */
/* copy of mysql->affected_rows after statement execution */ /* copy of mysql->affected_rows after statement execution */
my_ulonglong affected_rows; my_ulonglong affected_rows;
/*
mysql_stmt_fetch() calls this function to fetch one row (it's different
for buffered, unbuffered and cursor fetch).
*/
int (*read_row_func)(struct st_mysql_stmt *stmt,
unsigned char **row);
unsigned long stmt_id; /* Id for prepared statement */ unsigned long stmt_id; /* Id for prepared statement */
unsigned int last_errno; /* error code */ unsigned int last_errno; /* error code */
unsigned int param_count; /* parameters count */ unsigned int param_count; /* inpute parameters count */
unsigned int field_count; /* fields count */ unsigned int field_count; /* number of columns in result set */
enum PREP_STMT_STATE state; /* statement state */ enum enum_mysql_stmt_state state; /* statement state */
char last_error[MYSQL_ERRMSG_SIZE]; /* error message */ char last_error[MYSQL_ERRMSG_SIZE]; /* error message */
char sqlstate[SQLSTATE_LENGTH+1]; char sqlstate[SQLSTATE_LENGTH+1];
my_bool long_alloced; /* flag to indicate long alloced */ /* Types of input parameters should be sent to server */
my_bool send_types_to_server; /* Types sent to server */ my_bool send_types_to_server;
my_bool param_buffers; /* param bound buffers */ my_bool bind_param_done; /* input buffers were supplied */
my_bool res_buffers; /* output bound buffers */ my_bool bind_result_done; /* output buffers were supplied */
my_bool result_buffered; /* Results buffered */ /* mysql_stmt_close() had to cancel this result */
my_bool unbuffered_fetch_cancelled;
} MYSQL_STMT; } MYSQL_STMT;
......
...@@ -25,6 +25,7 @@ extern "C" { ...@@ -25,6 +25,7 @@ extern "C" {
MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
my_bool default_value, uint server_capabilities); my_bool default_value, uint server_capabilities);
void free_rows(MYSQL_DATA *cur); void free_rows(MYSQL_DATA *cur);
void flush_use_result(MYSQL *mysql);
my_bool mysql_autenticate(MYSQL *mysql, const char *passwd); my_bool mysql_autenticate(MYSQL *mysql, const char *passwd);
void free_old_query(MYSQL *mysql); void free_old_query(MYSQL *mysql);
void end_server(MYSQL *mysql); void end_server(MYSQL *mysql);
......
...@@ -22,7 +22,6 @@ extern my_string mysql_unix_port; ...@@ -22,7 +22,6 @@ extern my_string mysql_unix_port;
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION) CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
sig_handler pipe_sig_handler(int sig __attribute__((unused))); sig_handler pipe_sig_handler(int sig __attribute__((unused)));
my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_free);
void read_user_name(char *name); void read_user_name(char *name);
my_bool send_file_to_server(MYSQL *mysql, const char *filename); my_bool send_file_to_server(MYSQL *mysql, const char *filename);
......
...@@ -54,7 +54,7 @@ const char *client_errors[]= ...@@ -54,7 +54,7 @@ const char *client_errors[]=
"Malformed packet", "Malformed packet",
"Invalid use of null pointer", "Invalid use of null pointer",
"Statement not prepared", "Statement not prepared",
"Not all parameters data supplied", "Parameters data was not supplied",
"Data truncated", "Data truncated",
"No parameters exists in the statement", "No parameters exists in the statement",
"Invalid parameter number", "Invalid parameter number",
...@@ -72,7 +72,9 @@ const char *client_errors[]= ...@@ -72,7 +72,9 @@ const char *client_errors[]=
"Can't open shared memory. Can't send the request event to server (%lu)", "Can't open shared memory. Can't send the request event to server (%lu)",
"Wrong or unknown protocol", "Wrong or unknown protocol",
"Invalid connection handle", "Invalid connection handle",
"Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)",
"Row retrieval was cancelled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch"
}; };
/* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ /* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */
...@@ -110,7 +112,7 @@ const char *client_errors[]= ...@@ -110,7 +112,7 @@ const char *client_errors[]=
"Malformed packet", "Malformed packet",
"Invalid use of null pointer", "Invalid use of null pointer",
"Statement not prepared", "Statement not prepared",
"Not all parameters data supplied", "Parameters data was not supplied",
"Data truncated", "Data truncated",
"No parameters exists in the statement", "No parameters exists in the statement",
"Invalid parameter number", "Invalid parameter number",
...@@ -128,7 +130,9 @@ const char *client_errors[]= ...@@ -128,7 +130,9 @@ const char *client_errors[]=
"Can't open shared memory. Can't send the request event to server (%lu)", "Can't open shared memory. Can't send the request event to server (%lu)",
"Wrong or unknown protocol", "Wrong or unknown protocol",
"Invalid connection handle", "Invalid connection handle",
"Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)",
"Row retrieval was cancelled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch"
}; };
#else /* ENGLISH */ #else /* ENGLISH */
...@@ -182,7 +186,9 @@ const char *client_errors[]= ...@@ -182,7 +186,9 @@ const char *client_errors[]=
"Can't open shared memory. Can't send the request event to server (%lu)", "Can't open shared memory. Can't send the request event to server (%lu)",
"Wrong or unknown protocol", "Wrong or unknown protocol",
"Invalid connection handle", "Invalid connection handle",
"Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)",
"Row retrieval was cancelled by mysql_stmt_close() call",
"Attempt to read column without prior row fetch"
}; };
#endif #endif
......
...@@ -638,6 +638,7 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) ...@@ -638,6 +638,7 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
return 0; return 0;
} }
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
const char *passwd, const char *db) const char *passwd, const char *db)
{ {
...@@ -866,6 +867,7 @@ STDCALL mysql_set_master(MYSQL* mysql, const char* host, ...@@ -866,6 +867,7 @@ STDCALL mysql_set_master(MYSQL* mysql, const char* host,
return 0; return 0;
} }
int int
STDCALL mysql_add_slave(MYSQL* mysql, const char* host, STDCALL mysql_add_slave(MYSQL* mysql, const char* host,
unsigned int port, unsigned int port,
...@@ -987,6 +989,7 @@ mysql_list_tables(MYSQL *mysql, const char *wild) ...@@ -987,6 +989,7 @@ mysql_list_tables(MYSQL *mysql, const char *wild)
DBUG_RETURN (mysql_store_result(mysql)); DBUG_RETURN (mysql_store_result(mysql));
} }
MYSQL_FIELD *cli_list_fields(MYSQL *mysql) MYSQL_FIELD *cli_list_fields(MYSQL *mysql)
{ {
MYSQL_DATA *query; MYSQL_DATA *query;
...@@ -1062,6 +1065,7 @@ mysql_list_processes(MYSQL *mysql) ...@@ -1062,6 +1065,7 @@ mysql_list_processes(MYSQL *mysql)
DBUG_RETURN(mysql_store_result(mysql)); DBUG_RETURN(mysql_store_result(mysql));
} }
#ifdef USE_OLD_FUNCTIONS #ifdef USE_OLD_FUNCTIONS
int STDCALL int STDCALL
mysql_create_db(MYSQL *mysql, const char *db) mysql_create_db(MYSQL *mysql, const char *db)
...@@ -1099,6 +1103,7 @@ mysql_refresh(MYSQL *mysql,uint options) ...@@ -1099,6 +1103,7 @@ mysql_refresh(MYSQL *mysql,uint options)
DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0)); DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0));
} }
int STDCALL int STDCALL
mysql_kill(MYSQL *mysql,ulong pid) mysql_kill(MYSQL *mysql,ulong pid)
{ {
...@@ -1126,6 +1131,7 @@ mysql_dump_debug_info(MYSQL *mysql) ...@@ -1126,6 +1131,7 @@ mysql_dump_debug_info(MYSQL *mysql)
DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0)); DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0));
} }
const char *cli_read_statistics(MYSQL *mysql) const char *cli_read_statistics(MYSQL *mysql)
{ {
mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */ mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */
...@@ -1139,6 +1145,7 @@ const char *cli_read_statistics(MYSQL *mysql) ...@@ -1139,6 +1145,7 @@ const char *cli_read_statistics(MYSQL *mysql)
return (char*) mysql->net.read_pos; return (char*) mysql->net.read_pos;
} }
const char * STDCALL const char * STDCALL
mysql_stat(MYSQL *mysql) mysql_stat(MYSQL *mysql)
{ {
...@@ -1576,6 +1583,10 @@ static my_bool my_realloc_str(NET *net, ulong length) ...@@ -1576,6 +1583,10 @@ static my_bool my_realloc_str(NET *net, ulong length)
Prepare related implementations Prepare related implementations
********************************************************************/ ********************************************************************/
static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);
/* /*
Read the prepared statement results .. Read the prepared statement results ..
...@@ -1591,13 +1602,12 @@ static my_bool my_realloc_str(NET *net, ulong length) ...@@ -1591,13 +1602,12 @@ static my_bool my_realloc_str(NET *net, ulong length)
my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt)
{ {
uchar *pos; uchar *pos;
uint field_count; uint field_count, param_count;
ulong length, param_count;
MYSQL_DATA *fields_data; MYSQL_DATA *fields_data;
DBUG_ENTER("read_prepare_result"); DBUG_ENTER("read_prepare_result");
mysql= mysql->last_used_con; mysql= mysql->last_used_con;
if ((length= net_safe_read(mysql)) == packet_error) if (net_safe_read(mysql) == packet_error)
DBUG_RETURN(1); DBUG_RETURN(1);
pos= (uchar*) mysql->net.read_pos; pos= (uchar*) mysql->net.read_pos;
...@@ -1634,7 +1644,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query, ...@@ -1634,7 +1644,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query,
stmt= mysql_stmt_init(mysql); stmt= mysql_stmt_init(mysql);
if (stmt && mysql_stmt_prepare(stmt, query, query_length)) if (stmt && mysql_stmt_prepare(stmt, query, query_length))
{ {
stmt_close(stmt, 0); mysql_stmt_close(stmt);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
DBUG_RETURN(stmt); DBUG_RETURN(stmt);
...@@ -1646,6 +1656,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query, ...@@ -1646,6 +1656,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query,
SYNOPSIS SYNOPSIS
mysql_stmt_init() mysql_stmt_init()
mysql connection handle mysql connection handle
RETURN VALUE RETURN VALUE
statement structure upon success and NULL if out of statement structure upon success and NULL if out of
memory memory
...@@ -1664,20 +1675,35 @@ mysql_stmt_init(MYSQL *mysql) ...@@ -1664,20 +1675,35 @@ mysql_stmt_init(MYSQL *mysql)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
init_alloc_root(&stmt->mem_root,8192,0); init_alloc_root(&stmt->mem_root, 2048, 2048);
mysql->stmts= list_add(mysql->stmts, &stmt->list); mysql->stmts= list_add(mysql->stmts, &stmt->list);
stmt->list.data= stmt; stmt->list.data= stmt;
stmt->state= MY_ST_UNKNOWN; stmt->state= MYSQL_STMT_INIT_DONE;
stmt->mysql= mysql; stmt->mysql= mysql;
stmt->read_row_func= stmt_read_row_no_data;
/* The rest of statement members was bzeroed inside malloc */
DBUG_RETURN(stmt); DBUG_RETURN(stmt);
} }
/* /*
Prepare server side statement with query. Prepare server side statement with query:
SYNOPSIS
mysql_stmt_prepare()
query statement to prepare
length statement length
DESCRIPTION
- if this is a re-prepare of the statement, first close previous data
structure on the server and free old statement data
- send the query to server and get back number of placeholders,
number of columns in result set (if any), and result set metadata.
At the same time allocate memory for input and output parameters
to have less checks in mysql_stmt_bind_{param, result}.
Also update the total parameter count along with resultset RETURN VALUES
metadata information by reading from server 0 success
!0 error
*/ */
...@@ -1687,17 +1713,44 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) ...@@ -1687,17 +1713,44 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
MYSQL *mysql= stmt->mysql; MYSQL *mysql= stmt->mysql;
DBUG_ENTER("mysql_stmt_prepare"); DBUG_ENTER("mysql_stmt_prepare");
DBUG_ASSERT(mysql != 0); if (!mysql)
/* In case we reprepare this handle with another statement */
free_root(&stmt->mem_root, MYF(0));
/* stmt->query is never used yet */
if (!(stmt->query= strmake_root(&stmt->mem_root, query, length)))
{ {
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); /* mysql can be reset in mysql_close called from mysql_reconnect */
set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
{
/* This is second prepare with another statement */
char buff[4];
mysql_stmt_free_result(stmt);
/*
These members must be reset for API to
function in case of error or misuse.
*/
stmt->bind_param_done= stmt->bind_result_done= FALSE;
stmt->param_count= stmt->field_count= 0;
stmt->last_errno= 0;
stmt->last_error[0]= '\0';
free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
int4store(buff, stmt->stmt_id);
/*
If there was a 'use' result from another statement, or from
mysql_use_result it won't be freed in mysql_stmt_free_result and
we should get 'Commands out of sync' here.
*/
if (simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
DBUG_RETURN(1);
}
stmt->state= MYSQL_STMT_INIT_DONE;
}
if (simple_command(mysql, COM_PREPARE, query, length, 1)) if (simple_command(mysql, COM_PREPARE, query, length, 1))
{ {
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
...@@ -1706,8 +1759,18 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) ...@@ -1706,8 +1759,18 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
} }
if ((*mysql->methods->read_prepare_result)(mysql, stmt)) if ((*mysql->methods->read_prepare_result)(mysql, stmt))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
DBUG_RETURN(1); DBUG_RETURN(1);
}
/*
alloc_root will return valid address even in case param_count
and field_count are zero. Thus we should never rely on stmt->bind
or stmt->params when checking for existence of placeholders or
result set.
*/
if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root, if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root,
sizeof(MYSQL_BIND)* sizeof(MYSQL_BIND)*
(stmt->param_count + (stmt->param_count +
...@@ -1717,26 +1780,22 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) ...@@ -1717,26 +1780,22 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
stmt->bind= stmt->params + stmt->param_count; stmt->bind= stmt->params + stmt->param_count;
stmt->state= MY_ST_PREPARE; stmt->state= MYSQL_STMT_PREPARE_DONE;
mysql->status= MYSQL_STATUS_READY;
DBUG_PRINT("info", ("Parameter count: %ld", stmt->param_count)); DBUG_PRINT("info", ("Parameter count: %ld", stmt->param_count));
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/* /*
Get the execute query meta information for non-select Get the execute query meta information for non-select
statements (on demand). statements.
*/ */
unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
{ {
MYSQL_FIELD *fields, *field, *end; MYSQL_FIELD *fields, *field, *end;
MEM_ROOT *alloc= &stmt->mem_root; MEM_ROOT *alloc= &stmt->mem_root;
MYSQL *mysql= stmt->mysql->last_used_con; MYSQL *mysql= stmt->mysql->last_used_con;
if (stmt->state != MY_ST_EXECUTE || !mysql->field_count)
return 0;
stmt->field_count= mysql->field_count; stmt->field_count= mysql->field_count;
/* /*
...@@ -1782,20 +1841,25 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt) ...@@ -1782,20 +1841,25 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt)
MYSQL_RES *result; MYSQL_RES *result;
DBUG_ENTER("mysql_stmt_result_metadata"); DBUG_ENTER("mysql_stmt_result_metadata");
if (!stmt->field_count || !stmt->fields) /*
stmt->fields is only defined if stmt->field_count is not null;
stmt->field_count is initialized in prepare.
*/
if (!stmt->field_count)
DBUG_RETURN(0);
if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result),
MYF(MY_WME | MY_ZEROFILL))))
{ {
if (!alloc_stmt_fields(stmt)) set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+
sizeof(ulong)*stmt->field_count,
MYF(MY_WME | MY_ZEROFILL))))
return 0;
result->methods= stmt->mysql->methods; result->methods= stmt->mysql->methods;
result->eof=1; /* Marker for buffered */ result->eof= 1; /* Marker for buffered */
result->fields= stmt->fields; result->fields= stmt->fields;
result->field_count= stmt->field_count; result->field_count= stmt->field_count;
/* The rest of members of 'result' was bzeroed inside malloc */
DBUG_RETURN(result); DBUG_RETURN(result);
} }
...@@ -1989,11 +2053,9 @@ static void store_param_null(NET *net, MYSQL_BIND *param) ...@@ -1989,11 +2053,9 @@ static void store_param_null(NET *net, MYSQL_BIND *param)
client application client application
*/ */
static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param)
{ {
MYSQL *mysql= stmt->mysql; NET *net= &stmt->mysql->net;
NET *net = &mysql->net;
DBUG_ENTER("store_param"); DBUG_ENTER("store_param");
DBUG_PRINT("enter",("type: %d, buffer:%lx, length: %lu is_null: %d", DBUG_PRINT("enter",("type: %d, buffer:%lx, length: %lu is_null: %d",
param->buffer_type, param->buffer_type,
...@@ -2059,6 +2121,12 @@ int cli_stmt_execute(MYSQL_STMT *stmt) ...@@ -2059,6 +2121,12 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
uint null_count; uint null_count;
my_bool result; my_bool result;
if (!stmt->bind_param_done)
{
set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate);
DBUG_RETURN(1);
}
net_clear(net); /* Sets net->write_pos */ net_clear(net); /* Sets net->write_pos */
/* Reserve place for null-marker bytes */ /* Reserve place for null-marker bytes */
null_count= (stmt->param_count+7) /8; null_count= (stmt->param_count+7) /8;
...@@ -2093,7 +2161,6 @@ int cli_stmt_execute(MYSQL_STMT *stmt) ...@@ -2093,7 +2161,6 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
net->write_pos= net->buff; /* Reset for net_write() */
result= execute(stmt, param_data, length); result= execute(stmt, param_data, length);
stmt->send_types_to_server=0; stmt->send_types_to_server=0;
my_free(param_data, MYF(MY_WME)); my_free(param_data, MYF(MY_WME));
...@@ -2102,22 +2169,146 @@ int cli_stmt_execute(MYSQL_STMT *stmt) ...@@ -2102,22 +2169,146 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
DBUG_RETURN((int) execute(stmt,0,0)); DBUG_RETURN((int) execute(stmt,0,0));
} }
/*
Read one row from buffered result set. Result set is created by prior
call to mysql_stmt_store_result().
SYNOPSIS
stmt_read_row_buffered()
RETURN VALUE
0 - success; *row is set to valid row pointer (row data
is stored in result set buffer)
MYSQL_NO_DATA - end of result set. *row is set to NULL
*/
static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row)
{
MYSQL_RES *result= stmt->result;
if (result && result->data_cursor)
{
*row= (uchar *) result->data_cursor->data;
result->data_cursor= result->data_cursor->next;
return 0;
}
*row= 0;
return MYSQL_NO_DATA;
}
/*
Read one row from network: unbuffered non-cursor fetch.
If last row was read, or error occured, erase this statement
from record pointing to object unbuffered fetch is performed from.
SYNOPSIS
stmt_read_row_unbuffered()
stmt statement handle
row pointer to write pointer to row data;
RETURN VALUE
0 - success; *row contains valid address of a row;
row data is stored in network buffer
1 - error; error code is written to
stmt->last_{errno,error}; *row is not changed
MYSQL_NO_DATA - end of file was read from network;
*row is to NULL
*/
static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row)
{
int rc= 1;
MYSQL *mysql= stmt->mysql;
/*
This function won't be called if stmt->field_count is zero
or execution wasn't done: this is ensured by mysql_stmt_execute.
*/
if (!mysql)
{
set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate);
return 1;
}
if (mysql->status != MYSQL_STATUS_GET_RESULT)
{
set_stmt_error(stmt, stmt->unbuffered_fetch_cancelled ?
CR_FETCH_CANCELLED : CR_COMMANDS_OUT_OF_SYNC,
unknown_sqlstate);
goto error;
}
if ((*mysql->methods->unbuffered_fetch)(mysql, (char**) row))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
goto error;
}
if (!*row)
{
mysql->status= MYSQL_STATUS_READY;
rc= MYSQL_NO_DATA;
goto error;
}
return 0;
error:
if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
mysql->unbuffered_fetch_owner= 0;
return rc;
}
/*
Default read row function to not SIGSEGV in client in
case of wrong sequence of API calls.
*/
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;
}
/* /*
Execute the prepared query Execute the prepared query
*/ */
int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
{ {
MYSQL *mysql= stmt->mysql;
DBUG_ENTER("mysql_stmt_execute"); DBUG_ENTER("mysql_stmt_execute");
if ((*stmt->mysql->methods->stmt_execute)(stmt)) if (!mysql)
{
set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate);
DBUG_RETURN(1); DBUG_RETURN(1);
}
mysql_stmt_free_result(stmt);
/*
No need to check for stmt->state: if the statement wasn't
prepared we'll get 'unknown statemenet handler' error from server.
*/
if (mysql->methods->stmt_execute(stmt))
DBUG_RETURN(1);
if (!stmt->field_count && mysql->field_count)
{
/*
This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
prepared statements can't send result set metadata for this queries
on prepare stage. Read it now.
*/
alloc_stmt_fields(stmt);
}
stmt->state= MY_ST_EXECUTE; stmt->state= MYSQL_STMT_EXECUTE_DONE;
mysql_free_result(stmt->result); if (stmt->field_count)
stmt->result= (MYSQL_RES *)0; {
stmt->result_buffered= 0; stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
stmt->current_row= 0; stmt->unbuffered_fetch_cancelled= FALSE;
stmt->read_row_func= stmt_read_row_unbuffered;
}
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -2155,6 +2346,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) ...@@ -2155,6 +2346,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
MYSQL_BIND *param, *end; MYSQL_BIND *param, *end;
DBUG_ENTER("mysql_stmt_bind_param"); DBUG_ENTER("mysql_stmt_bind_param");
if (!stmt->param_count)
{
if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
{
set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
DBUG_RETURN(1);
}
DBUG_RETURN(0);
}
/* Allocated on prepare */ /* Allocated on prepare */
memcpy((char*) stmt->params, (char*) bind, memcpy((char*) stmt->params, (char*) bind,
sizeof(MYSQL_BIND) * stmt->param_count); sizeof(MYSQL_BIND) * stmt->param_count);
...@@ -2166,13 +2367,6 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) ...@@ -2166,13 +2367,6 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
param->param_number= count++; param->param_number= count++;
param->long_data_used= 0; param->long_data_used= 0;
/*
If param->length is not given, change it to point to buffer_length.
This way we can always use *param->length to get the length of data
*/
if (!param->length)
param->length= &param->buffer_length;
/* If param->is_null is not set, then the value can never be NULL */ /* If param->is_null is not set, then the value can never be NULL */
if (!param->is_null) if (!param->is_null)
param->is_null= &int_is_null_false; param->is_null= &int_is_null_false;
...@@ -2239,10 +2433,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) ...@@ -2239,10 +2433,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
param->buffer_type, count); param->buffer_type, count);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
/*
If param->length is not given, change it to point to buffer_length.
This way we can always use *param->length to get the length of data
*/
if (!param->length)
param->length= &param->buffer_length;
} }
/* We have to send/resendtype information to MySQL */ /* We have to send/resendtype information to MySQL */
stmt->send_types_to_server= 1; stmt->send_types_to_server= TRUE;
stmt->param_buffers= 1; stmt->bind_param_done= TRUE;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -2276,6 +2476,16 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, ...@@ -2276,6 +2476,16 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
DBUG_ASSERT(stmt != 0); DBUG_ASSERT(stmt != 0);
DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld", DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld",
param_number, data, length)); param_number, data, length));
/*
We only need to check for stmt->param_count, if it's not null
prepare was done.
*/
if (param_number >= stmt->param_count)
{
set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
DBUG_RETURN(1);
}
param= stmt->params+param_number; param= stmt->params+param_number;
if (param->buffer_type < MYSQL_TYPE_TINY_BLOB || if (param->buffer_type < MYSQL_TYPE_TINY_BLOB ||
...@@ -2846,9 +3056,20 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) ...@@ -2846,9 +3056,20 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
DBUG_ENTER("mysql_stmt_bind_result"); DBUG_ENTER("mysql_stmt_bind_result");
DBUG_ASSERT(stmt != 0); DBUG_ASSERT(stmt != 0);
if (!(bind_count= stmt->field_count) && if (!stmt->field_count)
!(bind_count= alloc_stmt_fields(stmt))) {
if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
{
set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
}
DBUG_RETURN(0); DBUG_RETURN(0);
}
bind_count= stmt->field_count;
/*
We only need to check that stmt->field_count - if it is not null
stmt->bind was initialized in mysql_stmt_prepare
*/
memcpy((char*) stmt->bind, (char*) bind, memcpy((char*) stmt->bind, (char*) bind,
sizeof(MYSQL_BIND)*bind_count); sizeof(MYSQL_BIND)*bind_count);
...@@ -2929,7 +3150,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) ...@@ -2929,7 +3150,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
} }
stmt->res_buffers= 1; stmt->bind_result_done= TRUE;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -2943,9 +3164,18 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) ...@@ -2943,9 +3164,18 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
MYSQL_BIND *bind, *end; MYSQL_BIND *bind, *end;
MYSQL_FIELD *field, *field_end; MYSQL_FIELD *field, *field_end;
uchar *null_ptr, bit; uchar *null_ptr, bit;
/*
Precondition: if stmt->field_count is zero or row is NULL, read_row_*
function must return no data.
*/
DBUG_ASSERT(stmt->field_count);
DBUG_ASSERT(row);
if (!row || !stmt->res_buffers) if (!stmt->bind_result_done)
{
/* If output parameters were not bound we should just return success */
return 0; return 0;
}
null_ptr= row; null_ptr= row;
row+= (stmt->field_count+9)/8; /* skip null bits */ row+= (stmt->field_count+9)/8; /* skip null bits */
...@@ -2994,57 +3224,22 @@ int cli_unbuffered_fetch(MYSQL *mysql, char **row) ...@@ -2994,57 +3224,22 @@ int cli_unbuffered_fetch(MYSQL *mysql, char **row)
int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt)
{ {
MYSQL *mysql= stmt->mysql; int rc;
uchar *row; uchar *row;
DBUG_ENTER("mysql_stmt_fetch"); DBUG_ENTER("mysql_stmt_fetch");
stmt->last_fetched_column= 0; /* reset */ if ((rc= (*stmt->read_row_func)(stmt, &row)) ||
if (stmt->result_buffered) /* buffered */ (rc= stmt_fetch_row(stmt, row)))
{ {
MYSQL_RES *res; stmt->state= MYSQL_STMT_PREPARE_DONE; /* XXX: this is buggy */
stmt->read_row_func= stmt_read_row_no_data;
if (!(res= stmt->result))
goto no_data;
if (!res->data_cursor)
{
stmt->current_row= 0;
goto no_data;
}
row= (uchar *)res->data_cursor->data;
res->data_cursor= res->data_cursor->next;
} }
else /* un-buffered */ else
{ {
if (mysql->status != MYSQL_STATUS_GET_RESULT) /* This is to know in mysql_stmt_fetch_column that data was fetched */
{ stmt->state= MYSQL_STMT_FETCH_DONE;
if (!stmt->field_count)
goto no_data;
set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
DBUG_RETURN(1);
}
if ((*mysql->methods->unbuffered_fetch)(mysql, (char**) &row))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
DBUG_RETURN(1);
}
if (!row)
{
mysql->status= MYSQL_STATUS_READY;
stmt->current_row= 0;
goto no_data;
}
} }
DBUG_RETURN(rc);
stmt->current_row= row;
DBUG_RETURN(stmt_fetch_row(stmt, row));
no_data:
DBUG_PRINT("info", ("end of data"));
DBUG_RETURN(MYSQL_NO_DATA); /* no more data */
} }
...@@ -3065,13 +3260,22 @@ no_data: ...@@ -3065,13 +3260,22 @@ no_data:
*/ */
int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
uint column, ulong offset) uint column, ulong offset)
{ {
MYSQL_BIND *param= stmt->bind+column; MYSQL_BIND *param= stmt->bind+column;
DBUG_ENTER("mysql_stmt_fetch_column"); DBUG_ENTER("mysql_stmt_fetch_column");
if (!stmt->current_row) if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE)
goto no_data; {
set_stmt_error(stmt, CR_NO_DATA, unknown_sqlstate);
return 1;
}
if (column >= stmt->field_count)
{
set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
DBUG_RETURN(1);
}
if (param->null_field) if (param->null_field)
{ {
...@@ -3092,10 +3296,6 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, ...@@ -3092,10 +3296,6 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
fetch_results(bind, field, &row); fetch_results(bind, field, &row);
} }
DBUG_RETURN(0); DBUG_RETURN(0);
no_data:
DBUG_PRINT("info", ("end of data"));
DBUG_RETURN(MYSQL_NO_DATA); /* no more data */
} }
...@@ -3111,7 +3311,7 @@ MYSQL_DATA *cli_read_binary_rows(MYSQL_STMT *stmt) ...@@ -3111,7 +3311,7 @@ MYSQL_DATA *cli_read_binary_rows(MYSQL_STMT *stmt)
MYSQL_DATA *result; MYSQL_DATA *result;
MYSQL_ROWS *cur, **prev_ptr; MYSQL_ROWS *cur, **prev_ptr;
NET *net = &mysql->net; NET *net = &mysql->net;
DBUG_ENTER("read_binary_rows"); DBUG_ENTER("cli_read_binary_rows");
mysql= mysql->last_used_con; mysql= mysql->last_used_con;
if ((pkt_len= net_safe_read(mysql)) == packet_error) if ((pkt_len= net_safe_read(mysql)) == packet_error)
...@@ -3181,33 +3381,37 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) ...@@ -3181,33 +3381,37 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
if (!stmt->field_count) if (!stmt->field_count)
DBUG_RETURN(0); DBUG_RETURN(0);
if (mysql->status != MYSQL_STATUS_GET_RESULT) if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE ||
mysql->status != MYSQL_STATUS_GET_RESULT)
{ {
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);
} }
mysql->status= MYSQL_STATUS_READY; /* server is ready */ if (!(result= (MYSQL_RES*) my_malloc(sizeof(MYSQL_RES),
if (!(result= (MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+
sizeof(ulong) *
stmt->field_count),
MYF(MY_WME | MY_ZEROFILL)))) MYF(MY_WME | MY_ZEROFILL))))
{ {
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
result->methods= mysql->methods; result->methods= mysql->methods;
stmt->result_buffered= 1; if ((result->data= (*mysql->methods->read_binary_rows)(stmt)))
if (!(result->data= (*stmt->mysql->methods->read_binary_rows)(stmt))) {
result->row_count= result->data->rows;
result->data_cursor= result->data->data;
}
else if (stmt->last_errno)
{ {
my_free((gptr) result,MYF(0)); my_free((gptr) result,MYF(0));
DBUG_RETURN(0); DBUG_RETURN(1);
} }
mysql->affected_rows= result->row_count= result->data->rows; mysql->affected_rows= stmt->affected_rows= result->row_count;
stmt->affected_rows= result->row_count;
result->data_cursor= result->data->data;
result->fields= stmt->fields; result->fields= stmt->fields;
result->field_count= stmt->field_count; result->field_count= stmt->field_count;
/* The rest of MYSQL_RES members were bzeroed inside my_malloc */
stmt->result= result; stmt->result= result;
stmt->read_row_func= stmt_read_row_buffered;
mysql->unbuffered_fetch_owner= 0; /* set in stmt_execute */
mysql->status= MYSQL_STATUS_READY; /* server is ready */
DBUG_RETURN(0); /* Data buffered, must be fetched with mysql_stmt_fetch() */ DBUG_RETURN(0); /* Data buffered, must be fetched with mysql_stmt_fetch() */
} }
...@@ -3292,6 +3496,40 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) ...@@ -3292,6 +3496,40 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
{
DBUG_ENTER("mysql_stmt_free_result");
DBUG_ASSERT(stmt != 0);
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
{
MYSQL *mysql= stmt->mysql;
if (stmt->result)
{
/* Result buffered */
mysql_free_result(stmt->result);
stmt->result= 0;
}
else if (mysql && stmt->field_count
&& (int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE)
{
if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
mysql->unbuffered_fetch_owner= 0;
if (mysql->status != MYSQL_STATUS_READY)
{
/* There is a result set and it belongs to this statement */
flush_use_result(mysql);
mysql->status= MYSQL_STATUS_READY;
}
}
stmt->state= MYSQL_STMT_PREPARE_DONE;
stmt->read_row_func= stmt_read_row_no_data;
}
DBUG_RETURN(0);
}
/******************************************************************** /********************************************************************
statement error handling and close statement error handling and close
*********************************************************************/ *********************************************************************/
...@@ -3300,82 +3538,55 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) ...@@ -3300,82 +3538,55 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
Close the statement handle by freeing all alloced resources Close the statement handle by freeing all alloced resources
SYNOPSIS SYNOPSIS
mysql_stmt_free_result() mysql_stmt_close()
stmt Statement handle stmt Statement handle
skip_list Flag to indicate delete from list or not
RETURN VALUES RETURN VALUES
0 ok 0 ok
1 error 1 error
*/ */
my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
{
MYSQL *mysql;
DBUG_ENTER("mysql_stmt_free_result");
DBUG_ASSERT(stmt != 0);
mysql= stmt->mysql;
if (mysql->status != MYSQL_STATUS_READY)
{
/* Clear the current execution status */
DBUG_PRINT("warning",("Not all packets read, clearing them"));
for (;;)
{
ulong pkt_len;
if ((pkt_len= net_safe_read(mysql)) == packet_error)
break;
if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
break;
}
mysql->status= MYSQL_STATUS_READY;
}
mysql_free_result(stmt->result);
stmt->result= 0;
stmt->result_buffered= 0;
stmt->current_row= 0;
DBUG_RETURN(0);
}
my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_free)
{ {
MYSQL *mysql; MYSQL *mysql= stmt->mysql;
DBUG_ENTER("stmt_close"); int rc= 0;
DBUG_ENTER("mysql_stmt_close");
DBUG_ASSERT(stmt != 0); mysql_free_result(stmt->result); /* if result is buffered */
free_root(&stmt->mem_root, MYF(0));
if (!(mysql= stmt->mysql))
{ if (mysql)
if (!skip_free)
my_free((gptr) stmt, MYF(MY_WME));
DBUG_RETURN(0);
}
mysql_stmt_free_result(stmt);
if (stmt->state == MY_ST_PREPARE || stmt->state == MY_ST_EXECUTE)
{ {
char buff[4]; mysql->stmts= list_delete(mysql->stmts, &stmt->list);
int4store(buff, stmt->stmt_id); if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
if (skip_free || simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1))
{ {
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, char buff[4];
mysql->net.sqlstate);
stmt->mysql= NULL; /* connection isn't valid anymore */ if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
DBUG_RETURN(1); mysql->unbuffered_fetch_owner= 0;
if (mysql->status != MYSQL_STATUS_READY)
{
/*
Flush result set of the connection. If it does not belong
to this statement, set a warning.
*/
flush_use_result(mysql);
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
mysql->status= MYSQL_STATUS_READY;
}
int4store(buff, stmt->stmt_id);
if ((rc= simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1)))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
}
} }
} }
stmt->field_count= 0;
free_root(&stmt->mem_root, MYF(0));
mysql->stmts= list_delete(mysql->stmts, &stmt->list);
mysql->status= MYSQL_STATUS_READY;
my_free((gptr) stmt, MYF(MY_WME));
DBUG_RETURN(0);
}
my_free((gptr) stmt, MYF(MY_WME));
my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) DBUG_RETURN(test(rc));
{
return stmt_close(stmt, 0);
} }
/* /*
...@@ -3388,6 +3599,10 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) ...@@ -3388,6 +3599,10 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
MYSQL *mysql; MYSQL *mysql;
DBUG_ENTER("mysql_stmt_reset"); DBUG_ENTER("mysql_stmt_reset");
DBUG_ASSERT(stmt != 0); 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; mysql= stmt->mysql->last_used_con;
int4store(buff, stmt->stmt_id); /* Send stmt id to server */ int4store(buff, stmt->stmt_id); /* Send stmt id to server */
......
...@@ -712,6 +712,34 @@ void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate) ...@@ -712,6 +712,34 @@ void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate)
net->last_errno= errcode; net->last_errno= errcode;
strmov(net->last_error, ER(errcode)); strmov(net->last_error, ER(errcode));
strmov(net->sqlstate, sqlstate); strmov(net->sqlstate, sqlstate);
DBUG_VOID_RETURN;
}
/*
Flush result set sent from server
*/
void flush_use_result(MYSQL *mysql)
{
/* Clear the current execution status */
DBUG_PRINT("warning",("Not all packets read, clearing them"));
for (;;)
{
ulong pkt_len;
if ((pkt_len=net_safe_read(mysql)) == packet_error)
break;
if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
{
if (protocol_41(mysql))
{
char *pos= (char*) mysql->net.read_pos;
mysql->warning_count=uint2korr(pos); pos+=2;
mysql->server_status=uint2korr(pos); pos+=2;
}
break; /* End of data */
}
}
} }
...@@ -752,26 +780,16 @@ mysql_free_result(MYSQL_RES *result) ...@@ -752,26 +780,16 @@ mysql_free_result(MYSQL_RES *result)
DBUG_PRINT("enter",("mysql_res: %lx",result)); DBUG_PRINT("enter",("mysql_res: %lx",result));
if (result) if (result)
{ {
if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) MYSQL *mysql= result->handle;
if (mysql)
{ {
DBUG_PRINT("warning",("Not all rows in set where read; Ignoring rows")); if (mysql->unbuffered_fetch_owner == &result->unbuffered_fetch_cancelled)
for (;;) mysql->unbuffered_fetch_owner= 0;
if (mysql->status == MYSQL_STATUS_USE_RESULT)
{ {
ulong pkt_len; flush_use_result(mysql);
if ((pkt_len=net_safe_read(result->handle)) == packet_error) mysql->status=MYSQL_STATUS_READY;
break;
if (pkt_len <= 8 && result->handle->net.read_pos[0] == 254)
{
if (protocol_41(result->handle))
{
char *pos= (char*) result->handle->net.read_pos;
result->handle->warning_count=uint2korr(pos); pos+=2;
result->handle->server_status=uint2korr(pos); pos+=2;
}
break; /* End of data */
}
} }
result->handle->status=MYSQL_STATUS_READY;
} }
free_rows(result->data); free_rows(result->data);
if (result->fields) if (result->fields)
...@@ -2177,12 +2195,13 @@ void STDCALL mysql_close(MYSQL *mysql) ...@@ -2177,12 +2195,13 @@ void STDCALL mysql_close(MYSQL *mysql)
#ifdef MYSQL_CLIENT #ifdef MYSQL_CLIENT
if (mysql->stmts) if (mysql->stmts)
{ {
/* Free any open prepared statements */ /* Reset connection handle in all prepared statements. */
LIST *element, *next_element; LIST *element;
for (element= mysql->stmts; element; element= next_element) for (element= mysql->stmts; element; element= element->next)
{ {
next_element= element->next; MYSQL_STMT *stmt= (MYSQL_STMT *) element->data;
stmt_close((MYSQL_STMT *)element->data, 1); stmt->mysql= 0;
/* No need to call list_delete for statement here */
} }
mysql->stmts= 0; mysql->stmts= 0;
} }
...@@ -2372,9 +2391,10 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql) ...@@ -2372,9 +2391,10 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql)
result->fields= mysql->fields; result->fields= mysql->fields;
result->field_alloc= mysql->field_alloc; result->field_alloc= mysql->field_alloc;
result->field_count= mysql->field_count; result->field_count= mysql->field_count;
result->current_field=0; /* The rest of result members is bzeroed in malloc */
result->current_row=0; /* Must do a fetch first */
mysql->fields=0; /* fields is now in result */ mysql->fields=0; /* fields is now in result */
/* just in case this was mistakenly called after mysql_stmt_execute() */
mysql->unbuffered_fetch_owner= 0;
DBUG_RETURN(result); /* Data fetched */ DBUG_RETURN(result); /* Data fetched */
} }
...@@ -2423,6 +2443,7 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql) ...@@ -2423,6 +2443,7 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql)
result->current_row= 0; result->current_row= 0;
mysql->fields=0; /* fields is now in result */ mysql->fields=0; /* fields is now in result */
mysql->status=MYSQL_STATUS_USE_RESULT; mysql->status=MYSQL_STATUS_USE_RESULT;
mysql->unbuffered_fetch_owner= &result->unbuffered_fetch_cancelled;
DBUG_RETURN(result); /* Data is read to be fetched */ DBUG_RETURN(result); /* Data is read to be fetched */
} }
...@@ -2439,19 +2460,30 @@ mysql_fetch_row(MYSQL_RES *res) ...@@ -2439,19 +2460,30 @@ mysql_fetch_row(MYSQL_RES *res)
{ /* Unbufferred fetch */ { /* Unbufferred fetch */
if (!res->eof) if (!res->eof)
{ {
if (!(read_one_row(res->handle,res->field_count,res->row, res->lengths))) MYSQL *mysql= res->handle;
if (mysql->status != MYSQL_STATUS_USE_RESULT)
{ {
res->row_count++; set_mysql_error(mysql,
DBUG_RETURN(res->current_row=res->row); res->unbuffered_fetch_cancelled ?
CR_FETCH_CANCELLED : CR_COMMANDS_OUT_OF_SYNC,
unknown_sqlstate);
} }
else else if (!(read_one_row(mysql, res->field_count, res->row, res->lengths)))
{ {
DBUG_PRINT("info",("end of data")); res->row_count++;
res->eof=1; DBUG_RETURN(res->current_row=res->row);
res->handle->status=MYSQL_STATUS_READY;
/* Don't clear handle in mysql_free_results */
res->handle=0;
} }
DBUG_PRINT("info",("end of data"));
res->eof=1;
mysql->status=MYSQL_STATUS_READY;
/*
Reset only if owner points to us: there is a chance that somebody
started new query after mysql_stmt_close():
*/
if (mysql->unbuffered_fetch_owner == &res->unbuffered_fetch_cancelled)
mysql->unbuffered_fetch_owner= 0;
/* Don't clear handle in mysql_free_result */
res->handle=0;
} }
DBUG_RETURN((MYSQL_ROW) NULL); DBUG_RETURN((MYSQL_ROW) NULL);
} }
......
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