summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <konstantin@mysql.com>2004-03-16 01:04:04 +0300
committerunknown <konstantin@mysql.com>2004-03-16 01:04:04 +0300
commit321422c86e29199d1d2fb0337640fa819550baa0 (patch)
tree752a7c190fcf0f92940319d09105b114f3e76d52
parent4c0d915652466b8f6c7f1bb5b397f59cb5cea8d3 (diff)
downloadmariadb-git-321422c86e29199d1d2fb0337640fa819550baa0.tar.gz
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 include/errmsg.h: - CR_NOT_ALL_PARAMS_BOUND - this error code wasn't used until now. Apparently it was added in advance, but then interface of mysql_stmt_bind_param changed. Now it's not possible to bind only some parameters - either all or none of parameters are bound. This error code is renamed to CR_PARAMS_NOT_BOUND - CR_FETCH_CANCELLED - error code set on server side when fetch from MYSQL_RES or MYSQL_STMT (in blocking mode) was cancelled because of intercepting call to mysql_stmt_close - CR_NO_DATA - this is proposed error code to return from mysql_stmt_fetch_column if no row was fetched (by any type of fetch). We always can fall back to CR_COMMANDS_OUT_OF_SYNC though. Need reviewer's opinion on this one. include/mysql.h: - added unbuffered_fetch_owner member to MYSQL to point to MYSQL_RES or MYSQL_STMT which is used to fetch result at the moment. This is to be able to set CR_FETCH_CANCELLED error without fantoms. - added unbuffered_fetch_cancelled boolean variable to MYSQL_STMT and MYSQL_RES structures - rename PREP_STMT_STATE -> enum enum_mysql_stmt_state - members of MYSQL_STMT ordered by size. - removed members of MYSQL_STMT: current_row, result_buffered, last_fetched_column, last_fetched_buffer, query - renamed members of MYSQL_STMT: param_buffers -> bind_param_done, res_buffers -> bind_result_done - now mysql_stmt_fetch calls stmt->read_row_func to read row either from buffer or from network. include/sql_common.h: declaration for flush_use_result libmysql/client_settings.h: stmt_close declaration removed libmysql/errmsg.c: Error messages for changed and added error codes. libmysql/libmysql.c: Many changes: - some unused variables removed - cleanups - better error reporting - some function calls commented - alloc_stmt_fields is now called right after execute, to not read mysql->fields of some other statement - new implementation of mysql_stmt_fetch - this is also with cursor fetch in mind (to implement cursor fetch I'll just need to write special read_row function for it, so this change will be local) - implementation of fetch cancellation, including complete rewrite of mysql_stmt_close - now mysql_stmt_free_result doesn't free results of other statements. sql-common/client.c: - implementation of flush_use_result - implementation of fetch cancellation - changed behaviour of mysql_close in regard to mysql_stmt_close - now mysql_close just set stmt->mysql to 0
-rw-r--r--include/errmsg.h4
-rw-r--r--include/mysql.h52
-rw-r--r--include/sql_common.h1
-rw-r--r--libmysql/client_settings.h1
-rw-r--r--libmysql/errmsg.c16
-rw-r--r--libmysql/libmysql.c579
-rw-r--r--sql-common/client.c98
7 files changed, 510 insertions, 241 deletions
diff --git a/include/errmsg.h b/include/errmsg.h
index 8f3ddfa9796..16f220a7ee2 100644
--- a/include/errmsg.h
+++ b/include/errmsg.h
@@ -67,7 +67,7 @@ extern const char *client_errors[]; /* Error messages */
/* new 4.1 error codes */
#define CR_NULL_POINTER 2028
#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_NO_PARAMETERS_EXISTS 2032
#define CR_INVALID_PARAMETER_NO 2033
@@ -87,3 +87,5 @@ extern const char *client_errors[]; /* Error messages */
#define CR_CONN_UNKNOW_PROTOCOL 2046
#define CR_INVALID_CONN_HANDLE 2047
#define CR_SECURE_AUTH 2048
+#define CR_FETCH_CANCELLED 2049
+#define CR_NO_DATA 2050
diff --git a/include/mysql.h b/include/mysql.h
index 2b4153bb140..1bcc2a58534 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -256,6 +256,11 @@ typedef struct st_mysql
LIST *stmts; /* list of all statements */
const struct st_mysql_methods *methods;
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;
typedef struct st_mysql_res {
@@ -270,6 +275,8 @@ typedef struct st_mysql_res {
MYSQL_ROW row; /* If unbuffered read */
MYSQL_ROW current_row; /* buffer to current 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;
} MYSQL_RES;
@@ -479,7 +486,11 @@ my_bool STDCALL mysql_read_query_result(MYSQL *mysql);
*/
/* 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
@@ -525,31 +536,34 @@ typedef struct st_mysql_bind
/* statement handler */
typedef struct st_mysql_stmt
{
- MYSQL *mysql; /* connection handle */
- MYSQL_BIND *params; /* input parameters */
- MYSQL_RES *result; /* resultset */
- MYSQL_BIND *bind; /* row binding */
- MYSQL_FIELD *fields; /* prepare meta info */
+ MEM_ROOT mem_root; /* root allocations */
LIST list; /* list to keep track of all stmts */
- unsigned char *current_row; /* unbuffered row */
- unsigned char *last_fetched_buffer; /* last fetched column buffer */
- char *query; /* query buffer */
- MEM_ROOT mem_root; /* root allocations */
- my_ulonglong last_fetched_column; /* last fetched column */
+ MYSQL *mysql; /* connection handle */
+ MYSQL_BIND *params; /* input parameters */
+ MYSQL_BIND *bind; /* output parameters */
+ MYSQL_FIELD *fields; /* result set metadata */
+ MYSQL_RES *result; /* cached result set */
/* copy of mysql->affected_rows after statement execution */
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 int last_errno; /* error code */
- unsigned int param_count; /* parameters count */
- unsigned int field_count; /* fields count */
- enum PREP_STMT_STATE state; /* statement state */
+ unsigned int param_count; /* inpute parameters count */
+ unsigned int field_count; /* number of columns in result set */
+ enum enum_mysql_stmt_state state; /* statement state */
char last_error[MYSQL_ERRMSG_SIZE]; /* error message */
char sqlstate[SQLSTATE_LENGTH+1];
- my_bool long_alloced; /* flag to indicate long alloced */
- my_bool send_types_to_server; /* Types sent to server */
- my_bool param_buffers; /* param bound buffers */
- my_bool res_buffers; /* output bound buffers */
- my_bool result_buffered; /* Results buffered */
+ /* Types of input parameters should be sent to server */
+ my_bool send_types_to_server;
+ my_bool bind_param_done; /* input buffers were supplied */
+ my_bool bind_result_done; /* output buffers were supplied */
+ /* mysql_stmt_close() had to cancel this result */
+ my_bool unbuffered_fetch_cancelled;
} MYSQL_STMT;
diff --git a/include/sql_common.h b/include/sql_common.h
index cde53786f83..3f50008a922 100644
--- a/include/sql_common.h
+++ b/include/sql_common.h
@@ -25,6 +25,7 @@ extern "C" {
MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
my_bool default_value, uint server_capabilities);
void free_rows(MYSQL_DATA *cur);
+void flush_use_result(MYSQL *mysql);
my_bool mysql_autenticate(MYSQL *mysql, const char *passwd);
void free_old_query(MYSQL *mysql);
void end_server(MYSQL *mysql);
diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h
index e4475d76958..9fc8e67df27 100644
--- a/libmysql/client_settings.h
+++ b/libmysql/client_settings.h
@@ -22,7 +22,6 @@ extern my_string mysql_unix_port;
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
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);
my_bool send_file_to_server(MYSQL *mysql, const char *filename);
diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c
index 569267ddb37..e651c13897f 100644
--- a/libmysql/errmsg.c
+++ b/libmysql/errmsg.c
@@ -54,7 +54,7 @@ const char *client_errors[]=
"Malformed packet",
"Invalid use of null pointer",
"Statement not prepared",
- "Not all parameters data supplied",
+ "Parameters data was not supplied",
"Data truncated",
"No parameters exists in the statement",
"Invalid parameter number",
@@ -72,7 +72,9 @@ const char *client_errors[]=
"Can't open shared memory. Can't send the request event to server (%lu)",
"Wrong or unknown protocol",
"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 */
@@ -110,7 +112,7 @@ const char *client_errors[]=
"Malformed packet",
"Invalid use of null pointer",
"Statement not prepared",
- "Not all parameters data supplied",
+ "Parameters data was not supplied",
"Data truncated",
"No parameters exists in the statement",
"Invalid parameter number",
@@ -128,7 +130,9 @@ const char *client_errors[]=
"Can't open shared memory. Can't send the request event to server (%lu)",
"Wrong or unknown protocol",
"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 */
@@ -182,7 +186,9 @@ const char *client_errors[]=
"Can't open shared memory. Can't send the request event to server (%lu)",
"Wrong or unknown protocol",
"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
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 0f274021f20..91927e44e49 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -638,6 +638,7 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
return 0;
}
+
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
const char *passwd, const char *db)
{
@@ -866,6 +867,7 @@ STDCALL mysql_set_master(MYSQL* mysql, const char* host,
return 0;
}
+
int
STDCALL mysql_add_slave(MYSQL* mysql, const char* host,
unsigned int port,
@@ -987,6 +989,7 @@ mysql_list_tables(MYSQL *mysql, const char *wild)
DBUG_RETURN (mysql_store_result(mysql));
}
+
MYSQL_FIELD *cli_list_fields(MYSQL *mysql)
{
MYSQL_DATA *query;
@@ -1062,6 +1065,7 @@ mysql_list_processes(MYSQL *mysql)
DBUG_RETURN(mysql_store_result(mysql));
}
+
#ifdef USE_OLD_FUNCTIONS
int STDCALL
mysql_create_db(MYSQL *mysql, const char *db)
@@ -1099,6 +1103,7 @@ mysql_refresh(MYSQL *mysql,uint options)
DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0));
}
+
int STDCALL
mysql_kill(MYSQL *mysql,ulong pid)
{
@@ -1126,6 +1131,7 @@ mysql_dump_debug_info(MYSQL *mysql)
DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0));
}
+
const char *cli_read_statistics(MYSQL *mysql)
{
mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */
@@ -1139,6 +1145,7 @@ const char *cli_read_statistics(MYSQL *mysql)
return (char*) mysql->net.read_pos;
}
+
const char * STDCALL
mysql_stat(MYSQL *mysql)
{
@@ -1576,6 +1583,10 @@ static my_bool my_realloc_str(NET *net, ulong length)
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 ..
@@ -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)
{
uchar *pos;
- uint field_count;
- ulong length, param_count;
+ uint field_count, param_count;
MYSQL_DATA *fields_data;
DBUG_ENTER("read_prepare_result");
mysql= mysql->last_used_con;
- if ((length= net_safe_read(mysql)) == packet_error)
+ if (net_safe_read(mysql) == packet_error)
DBUG_RETURN(1);
pos= (uchar*) mysql->net.read_pos;
@@ -1634,7 +1644,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query,
stmt= mysql_stmt_init(mysql);
if (stmt && mysql_stmt_prepare(stmt, query, query_length))
{
- stmt_close(stmt, 0);
+ mysql_stmt_close(stmt);
DBUG_RETURN(0);
}
DBUG_RETURN(stmt);
@@ -1646,6 +1656,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query,
SYNOPSIS
mysql_stmt_init()
mysql connection handle
+
RETURN VALUE
statement structure upon success and NULL if out of
memory
@@ -1664,20 +1675,35 @@ mysql_stmt_init(MYSQL *mysql)
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);
stmt->list.data= stmt;
- stmt->state= MY_ST_UNKNOWN;
+ stmt->state= MYSQL_STMT_INIT_DONE;
stmt->mysql= mysql;
+ stmt->read_row_func= stmt_read_row_no_data;
+ /* The rest of statement members was bzeroed inside malloc */
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
- metadata information by reading from server
+ RETURN VALUES
+ 0 success
+ !0 error
*/
@@ -1687,17 +1713,44 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
MYSQL *mysql= stmt->mysql;
DBUG_ENTER("mysql_stmt_prepare");
- DBUG_ASSERT(mysql != 0);
-
- /* 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)))
+ if (!mysql)
{
- 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);
}
+
+ 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))
{
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)
}
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);
+ }
+ /*
+ 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,
sizeof(MYSQL_BIND)*
(stmt->param_count +
@@ -1717,26 +1780,22 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
DBUG_RETURN(1);
}
stmt->bind= stmt->params + stmt->param_count;
- stmt->state= MY_ST_PREPARE;
- mysql->status= MYSQL_STATUS_READY;
+ stmt->state= MYSQL_STMT_PREPARE_DONE;
DBUG_PRINT("info", ("Parameter count: %ld", stmt->param_count));
DBUG_RETURN(0);
}
/*
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;
MEM_ROOT *alloc= &stmt->mem_root;
MYSQL *mysql= stmt->mysql->last_used_con;
- if (stmt->state != MY_ST_EXECUTE || !mysql->field_count)
- return 0;
-
stmt->field_count= mysql->field_count;
/*
@@ -1782,20 +1841,25 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt)
MYSQL_RES *result;
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))
- DBUG_RETURN(0);
- }
- if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+
- sizeof(ulong)*stmt->field_count,
- MYF(MY_WME | MY_ZEROFILL))))
- return 0;
+ set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
+ DBUG_RETURN(0);
+ }
- result->methods= stmt->mysql->methods;
- result->eof=1; /* Marker for buffered */
+ result->methods= stmt->mysql->methods;
+ result->eof= 1; /* Marker for buffered */
result->fields= stmt->fields;
result->field_count= stmt->field_count;
+ /* The rest of members of 'result' was bzeroed inside malloc */
DBUG_RETURN(result);
}
@@ -1989,11 +2053,9 @@ static void store_param_null(NET *net, MYSQL_BIND *param)
client application
*/
-
static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param)
{
- MYSQL *mysql= stmt->mysql;
- NET *net = &mysql->net;
+ NET *net= &stmt->mysql->net;
DBUG_ENTER("store_param");
DBUG_PRINT("enter",("type: %d, buffer:%lx, length: %lu is_null: %d",
param->buffer_type,
@@ -2059,6 +2121,12 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
uint null_count;
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 */
/* Reserve place for null-marker bytes */
null_count= (stmt->param_count+7) /8;
@@ -2093,7 +2161,6 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(1);
}
- net->write_pos= net->buff; /* Reset for net_write() */
result= execute(stmt, param_data, length);
stmt->send_types_to_server=0;
my_free(param_data, MYF(MY_WME));
@@ -2103,21 +2170,145 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
}
/*
+ 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
*/
int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
{
+ MYSQL *mysql= stmt->mysql;
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);
+ }
+
+ 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;
- mysql_free_result(stmt->result);
- stmt->result= (MYSQL_RES *)0;
- stmt->result_buffered= 0;
- stmt->current_row= 0;
+ stmt->state= MYSQL_STMT_EXECUTE_DONE;
+ if (stmt->field_count)
+ {
+ stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
+ stmt->unbuffered_fetch_cancelled= FALSE;
+ stmt->read_row_func= stmt_read_row_unbuffered;
+ }
DBUG_RETURN(0);
}
@@ -2155,6 +2346,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
MYSQL_BIND *param, *end;
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 */
memcpy((char*) stmt->params, (char*) bind,
sizeof(MYSQL_BIND) * stmt->param_count);
@@ -2166,13 +2367,6 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
param->param_number= count++;
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)
param->is_null= &int_is_null_false;
@@ -2239,10 +2433,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
param->buffer_type, count);
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 */
- stmt->send_types_to_server= 1;
- stmt->param_buffers= 1;
+ stmt->send_types_to_server= TRUE;
+ stmt->bind_param_done= TRUE;
DBUG_RETURN(0);
}
@@ -2276,6 +2476,16 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
DBUG_ASSERT(stmt != 0);
DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld",
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;
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)
DBUG_ENTER("mysql_stmt_bind_result");
DBUG_ASSERT(stmt != 0);
- if (!(bind_count= stmt->field_count) &&
- !(bind_count= alloc_stmt_fields(stmt)))
+ if (!stmt->field_count)
+ {
+ if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
+ {
+ set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
+ }
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,
sizeof(MYSQL_BIND)*bind_count);
@@ -2929,7 +3150,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
DBUG_RETURN(1);
}
}
- stmt->res_buffers= 1;
+ stmt->bind_result_done= TRUE;
DBUG_RETURN(0);
}
@@ -2943,9 +3164,18 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
MYSQL_BIND *bind, *end;
MYSQL_FIELD *field, *field_end;
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;
+ }
null_ptr= row;
row+= (stmt->field_count+9)/8; /* skip null bits */
@@ -2994,57 +3224,22 @@ int cli_unbuffered_fetch(MYSQL *mysql, char **row)
int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt)
{
- MYSQL *mysql= stmt->mysql;
+ int rc;
uchar *row;
DBUG_ENTER("mysql_stmt_fetch");
- stmt->last_fetched_column= 0; /* reset */
- if (stmt->result_buffered) /* buffered */
+ if ((rc= (*stmt->read_row_func)(stmt, &row)) ||
+ (rc= stmt_fetch_row(stmt, row)))
{
- MYSQL_RES *res;
-
- 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;
+ stmt->state= MYSQL_STMT_PREPARE_DONE; /* XXX: this is buggy */
+ stmt->read_row_func= stmt_read_row_no_data;
}
- else /* un-buffered */
+ else
{
- if (mysql->status != MYSQL_STATUS_GET_RESULT)
- {
- 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;
- }
+ /* This is to know in mysql_stmt_fetch_column that data was fetched */
+ stmt->state= MYSQL_STMT_FETCH_DONE;
}
-
- 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 */
+ DBUG_RETURN(rc);
}
@@ -3065,13 +3260,22 @@ no_data:
*/
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;
DBUG_ENTER("mysql_stmt_fetch_column");
- if (!stmt->current_row)
- goto no_data;
+ if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE)
+ {
+ 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)
{
@@ -3092,10 +3296,6 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
fetch_results(bind, field, &row);
}
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)
MYSQL_DATA *result;
MYSQL_ROWS *cur, **prev_ptr;
NET *net = &mysql->net;
- DBUG_ENTER("read_binary_rows");
+ DBUG_ENTER("cli_read_binary_rows");
mysql= mysql->last_used_con;
if ((pkt_len= net_safe_read(mysql)) == packet_error)
@@ -3181,33 +3381,37 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
if (!stmt->field_count)
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);
DBUG_RETURN(1);
}
- mysql->status= MYSQL_STATUS_READY; /* server is ready */
- if (!(result= (MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+
- sizeof(ulong) *
- stmt->field_count),
+ if (!(result= (MYSQL_RES*) my_malloc(sizeof(MYSQL_RES),
MYF(MY_WME | MY_ZEROFILL))))
{
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(1);
}
result->methods= mysql->methods;
- stmt->result_buffered= 1;
- if (!(result->data= (*stmt->mysql->methods->read_binary_rows)(stmt)))
+ if ((result->data= (*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));
- DBUG_RETURN(0);
+ DBUG_RETURN(1);
}
- mysql->affected_rows= result->row_count= result->data->rows;
- stmt->affected_rows= result->row_count;
- result->data_cursor= result->data->data;
+ mysql->affected_rows= stmt->affected_rows= result->row_count;
result->fields= stmt->fields;
result->field_count= stmt->field_count;
+ /* The rest of MYSQL_RES members were bzeroed inside my_malloc */
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() */
}
@@ -3292,6 +3496,40 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
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
*********************************************************************/
@@ -3300,82 +3538,55 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
Close the statement handle by freeing all alloced resources
SYNOPSIS
- mysql_stmt_free_result()
+ mysql_stmt_close()
stmt Statement handle
- skip_list Flag to indicate delete from list or not
+
RETURN VALUES
0 ok
1 error
*/
-my_bool STDCALL mysql_stmt_free_result(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)
+my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
{
- MYSQL *mysql;
- DBUG_ENTER("stmt_close");
+ MYSQL *mysql= stmt->mysql;
+ int rc= 0;
+ DBUG_ENTER("mysql_stmt_close");
- DBUG_ASSERT(stmt != 0);
-
- if (!(mysql= stmt->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)
+ mysql_free_result(stmt->result); /* if result is buffered */
+ free_root(&stmt->mem_root, MYF(0));
+
+ if (mysql)
{
- char buff[4];
- int4store(buff, stmt->stmt_id);
- if (skip_free || simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1))
+ mysql->stmts= list_delete(mysql->stmts, &stmt->list);
+ if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
{
- set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
- mysql->net.sqlstate);
- stmt->mysql= NULL; /* connection isn't valid anymore */
- DBUG_RETURN(1);
+ char buff[4];
+
+ if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
+ 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)
-{
- return stmt_close(stmt, 0);
+ DBUG_RETURN(test(rc));
}
/*
@@ -3388,6 +3599,10 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
MYSQL *mysql;
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 */
diff --git a/sql-common/client.c b/sql-common/client.c
index 5d2df4a0ddf..6d0da338543 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -712,6 +712,34 @@ void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate)
net->last_errno= errcode;
strmov(net->last_error, ER(errcode));
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)
DBUG_PRINT("enter",("mysql_res: %lx",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"));
- for (;;)
+ if (mysql->unbuffered_fetch_owner == &result->unbuffered_fetch_cancelled)
+ mysql->unbuffered_fetch_owner= 0;
+ if (mysql->status == MYSQL_STATUS_USE_RESULT)
{
- ulong pkt_len;
- if ((pkt_len=net_safe_read(result->handle)) == packet_error)
- 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 */
- }
+ flush_use_result(mysql);
+ mysql->status=MYSQL_STATUS_READY;
}
- result->handle->status=MYSQL_STATUS_READY;
}
free_rows(result->data);
if (result->fields)
@@ -2177,12 +2195,13 @@ void STDCALL mysql_close(MYSQL *mysql)
#ifdef MYSQL_CLIENT
if (mysql->stmts)
{
- /* Free any open prepared statements */
- LIST *element, *next_element;
- for (element= mysql->stmts; element; element= next_element)
+ /* Reset connection handle in all prepared statements. */
+ LIST *element;
+ for (element= mysql->stmts; element; element= element->next)
{
- next_element= element->next;
- stmt_close((MYSQL_STMT *)element->data, 1);
+ MYSQL_STMT *stmt= (MYSQL_STMT *) element->data;
+ stmt->mysql= 0;
+ /* No need to call list_delete for statement here */
}
mysql->stmts= 0;
}
@@ -2372,9 +2391,10 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql)
result->fields= mysql->fields;
result->field_alloc= mysql->field_alloc;
result->field_count= mysql->field_count;
- result->current_field=0;
- result->current_row=0; /* Must do a fetch first */
+ /* The rest of result members is bzeroed in malloc */
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 */
}
@@ -2423,6 +2443,7 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql)
result->current_row= 0;
mysql->fields=0; /* fields is now in result */
mysql->status=MYSQL_STATUS_USE_RESULT;
+ mysql->unbuffered_fetch_owner= &result->unbuffered_fetch_cancelled;
DBUG_RETURN(result); /* Data is read to be fetched */
}
@@ -2439,19 +2460,30 @@ mysql_fetch_row(MYSQL_RES *res)
{ /* Unbufferred fetch */
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++;
- DBUG_RETURN(res->current_row=res->row);
+ set_mysql_error(mysql,
+ 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->eof=1;
- res->handle->status=MYSQL_STATUS_READY;
- /* Don't clear handle in mysql_free_results */
- res->handle=0;
+ res->row_count++;
+ DBUG_RETURN(res->current_row=res->row);
}
+ 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);
}