1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

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
This commit is contained in:
unknown
2004-03-16 01:04:04 +03:00
parent 4c0d915652
commit 321422c86e
7 changed files with 517 additions and 248 deletions

View File

@ -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
Also update the total parameter count along with resultset
metadata information by reading from server
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}.
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)
{
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;
/*
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);
result->methods= stmt->mysql->methods;
result->eof=1; /* Marker for buffered */
if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result),
MYF(MY_WME | MY_ZEROFILL))))
{
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->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));
@ -2102,22 +2169,146 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
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
*/
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)
{
MYSQL *mysql;
DBUG_ENTER("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)
{
char buff[4];
int4store(buff, stmt->stmt_id);
if (skip_free || 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->mysql= NULL; /* connection isn't valid anymore */
DBUG_RETURN(1);
}
}
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_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
{
return stmt_close(stmt, 0);
MYSQL *mysql= stmt->mysql;
int rc= 0;
DBUG_ENTER("mysql_stmt_close");
mysql_free_result(stmt->result); /* if result is buffered */
free_root(&stmt->mem_root, MYF(0));
if (mysql)
{
mysql->stmts= list_delete(mysql->stmts, &stmt->list);
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
{
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);
}
}
}
my_free((gptr) stmt, MYF(MY_WME));
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 */