Commit 049a8386 authored by unknown's avatar unknown

Client-Server Protocol 4.1 changes - Client side:

  - Support of prepared execution
  - Support of Original Table and Column names
  - Support of direct transactional API
  - And lot of misc handling

  (Note that, the pull will not work, unless you have the Server side changes also which will be followed by this commit)
  


BitKeeper/etc/logging_ok:
  Logging to logging@openlogging.org accepted
parent e413fd4c
......@@ -67,3 +67,4 @@ venu@work.mysql.com
worm@altair.is.lan
zak@balfor.local
zak@linux.local
venu@myvenu.com
......@@ -61,3 +61,17 @@ extern const char *client_errors[]; /* Error messages */
#define CR_PROBE_SLAVE_HOSTS 2023
#define CR_PROBE_SLAVE_CONNECT 2024
#define CR_PROBE_MASTER_CONNECT 2025
/* new 4.1 error codes */
#define CR_INVALID_CONN_HANDLE 2026
#define CR_NULL_POINTER 2027
#define CR_MEMORY_ERROR 2028
#define CR_NO_PREPARE_STMT 2029
#define CR_NOT_ALL_PARAMS_BOUND 2030
#define CR_DATA_TRUNCATED 2031
#define CR_NOT_ALL_BUFFERS_BOUND 2032
#define CR_FAILED_TO_SET_PARAM_DATA 2033
#define CR_NO_PARAMETERS_EXISTS 2033
#define CR_INVALID_PARAMETER_NO 2035
#define CR_INVALID_BUFFER_USE 2036
......@@ -68,16 +68,17 @@ extern char *mysql_unix_port;
#define INTERNAL_NUM_FIELD(f) (((f)->type <= FIELD_TYPE_INT24 && ((f)->type != FIELD_TYPE_TIMESTAMP || (f)->length == 14 || (f)->length == 8)) || (f)->type == FIELD_TYPE_YEAR)
typedef struct st_mysql_field {
char *name; /* Name of column */
char *table; /* Table of column if column was a field */
char *org_table; /* Org table name if table was an alias */
char *db; /* Database for table */
char *def; /* Default value (set by mysql_list_fields) */
unsigned long length; /* Width of column */
unsigned long max_length; /* Max width of selected set */
unsigned int flags; /* Div flags */
unsigned int decimals; /* Number of decimals in field */
enum enum_field_types type; /* Type of field. Se mysql_com.h for types */
char *name; /* Name of column */
char *org_name; /* Original column name, if column was an alias */
char *table; /* Table of column if column was a field */
char *org_table; /* Org table name, if table was an alias */
char *db; /* Database for table */
char *def; /* Default value (set by mysql_list_fields) */
unsigned long length; /* Width of column */
unsigned long max_length; /* Max width of selected set */
unsigned int flags; /* Div flags */
unsigned int decimals; /* Number of decimals in field */
enum enum_field_types type; /* Type of field. Se mysql_com.h for types */
} MYSQL_FIELD;
typedef char **MYSQL_ROW; /* return data as array of strings */
......@@ -101,6 +102,23 @@ typedef struct st_mysql_rows {
typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */
#include <my_alloc.h>
#ifndef ST_USED_MEM_DEFINED
#define ST_USED_MEM_DEFINED
typedef struct st_used_mem { /* struct for once_alloc */
struct st_used_mem *next; /* Next block in use */
unsigned int left; /* memory left in block */
unsigned int size; /* size of block */
} USED_MEM;
typedef struct st_mem_root {
USED_MEM *free;
USED_MEM *used;
USED_MEM *pre_alloc;
unsigned int min_malloc;
unsigned int block_size;
void (*error_handler)(void);
} MEM_ROOT;
#endif
typedef struct st_mysql_data {
my_ulonglong rows;
......@@ -391,6 +409,82 @@ int STDCALL mysql_manager_command(MYSQL_MANAGER* con,
int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con,
char* res_buf,
int res_buf_size);
/*
The following definations are added for the enhanced
client-server protocol
*/
/* statement state */
enum MY_STMT_STATE { MY_ST_UNKNOWN, MY_ST_PREPARE, MY_ST_EXECUTE };
/* bind structure */
typedef struct st_mysql_bind {
enum enum_field_types buffer_type; /* buffer type */
enum enum_field_types field_type; /* field type */
gptr buffer; /* buffer */
long *length; /* output length pointer */
ulong buffer_length; /* buffer length */
ulong bind_length; /* internal use */
my_bool is_null; /* NULL indicator */
my_bool is_long_data; /* long data indicator */
my_bool long_ended; /* internal use */
} 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 */
ulong param_count; /* parameters count */
ulong field_count; /* fields count */
ulong long_length; /* long buffer alloced length */
uint err_no; /* error code */
char error[MYSQL_ERRMSG_SIZE]; /* error message */
char *query; /* query buffer */
char *long_data; /* long buffer */
enum MY_STMT_STATE state; /* statement state */
my_bool long_alloced; /* flag to indicate long alloced */
my_bool types_supplied; /* to indicate types supply */
} MYSQL_STMT;
MYSQL_STMT * STDCALL mysql_prepare(MYSQL * mysql, const char *query);
int STDCALL mysql_execute(MYSQL_STMT * stmt);
ulong STDCALL mysql_param_count(MYSQL_STMT * stmt);
int STDCALL mysql_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bind);
int STDCALL mysql_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bind);
int STDCALL mysql_stmt_close(MYSQL_STMT * stmt);
uint STDCALL mysql_stmt_errno(MYSQL_STMT * stmt);
const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt);
int STDCALL mysql_commit(MYSQL * mysql);
int STDCALL mysql_rollback(MYSQL * mysql);
int STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
int STDCALL mysql_fetch(MYSQL_STMT *stmt);
my_bool STDCALL mysql_send_long_data(MYSQL_STMT *stmt,
uint param_number,gptr data, ulong length);
int STDCALL mysql_multi_query(MYSQL *mysql,const char *query,ulong length);
MYSQL_RES *STDCALL mysql_next_result(MYSQL *mysql);
MYSQL_RES * STDCALL mysql_prepare_result(MYSQL_STMT *stmt);
/* new status messages */
#define MYSQL_SUCCESS 0
#define MYSQL_WARNING 1
#define MYSQL_ERROR -1
#define MYSQL_NO_DATA 100
#define MYSQL_NEED_DATA 99
#define MYSQL_LONG_DATA_END 0xFF
#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT)
#ifdef USE_OLD_FUNCTIONS
......
......@@ -42,7 +42,8 @@ enum enum_server_command {COM_SLEEP,COM_QUIT,COM_INIT_DB,COM_QUERY,
COM_DEBUG,COM_PING,COM_TIME,COM_DELAYED_INSERT,
COM_CHANGE_USER, COM_BINLOG_DUMP,
COM_TABLE_DUMP, COM_CONNECT_OUT,
COM_REGISTER_SLAVE};
COM_REGISTER_SLAVE,
COM_PREPARE,COM_EXECUTE,COM_LONG_DATA };
#define NOT_NULL_FLAG 1 /* Field can't be NULL */
#define PRI_KEY_FLAG 2 /* Field is part of a primary key */
......@@ -96,6 +97,7 @@ enum enum_server_command {COM_SLEEP,COM_QUIT,COM_INIT_DB,COM_QUERY,
#define CLIENT_SSL 2048 /* Switch to SSL after handshake */
#define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */
#define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */
#define CLIENT_PROTOCOL_41 16384 /* New 4.1 protocol */
#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
......@@ -136,27 +138,64 @@ typedef struct st_net {
#define packet_error (~(unsigned long) 0)
enum enum_field_types { FIELD_TYPE_DECIMAL, FIELD_TYPE_TINY,
FIELD_TYPE_SHORT, FIELD_TYPE_LONG,
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE,
FIELD_TYPE_NULL, FIELD_TYPE_TIMESTAMP,
FIELD_TYPE_LONGLONG,FIELD_TYPE_INT24,
FIELD_TYPE_DATE, FIELD_TYPE_TIME,
FIELD_TYPE_DATETIME, FIELD_TYPE_YEAR,
FIELD_TYPE_NEWDATE,
FIELD_TYPE_ENUM=247,
FIELD_TYPE_SET=248,
FIELD_TYPE_TINY_BLOB=249,
FIELD_TYPE_MEDIUM_BLOB=250,
FIELD_TYPE_LONG_BLOB=251,
FIELD_TYPE_BLOB=252,
FIELD_TYPE_VAR_STRING=253,
FIELD_TYPE_STRING=254,
FIELD_TYPE_GEOMETRY=255
enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP,
MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,
MYSQL_TYPE_DATE, MYSQL_TYPE_TIME,
MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
MYSQL_TYPE_NEWDATE,
MYSQL_TYPE_ENUM=247,
MYSQL_TYPE_SET=248,
MYSQL_TYPE_TINY_BLOB=249,
MYSQL_TYPE_MEDIUM_BLOB=250,
MYSQL_TYPE_LONG_BLOB=251,
MYSQL_TYPE_BLOB=252,
MYSQL_TYPE_VAR_STRING=253,
MYSQL_TYPE_STRING=254,
MYSQL_TYPE_GEOMETRY=255
};
#define FIELD_TYPE_CHAR FIELD_TYPE_TINY /* For compability */
#define FIELD_TYPE_INTERVAL FIELD_TYPE_ENUM /* For compability */
/* For backward compatibility */
#define FIELD_TYPE_DECIMAL MYSQL_TYPE_DECIMAL
#define FIELD_TYPE_TINY MYSQL_TYPE_TINY
#define FIELD_TYPE_SHORT MYSQL_TYPE_SHORT
#define FIELD_TYPE_LONG MYSQL_TYPE_LONG
#define FIELD_TYPE_FLOAT MYSQL_TYPE_FLOAT
#define FIELD_TYPE_DOUBLE MYSQL_TYPE_DOUBLE
#define FIELD_TYPE_NULL MYSQL_TYPE_NULL
#define FIELD_TYPE_TIMESTAMP MYSQL_TYPE_TIMESTAMP
#define FIELD_TYPE_LONGLONG MYSQL_TYPE_LONGLONG
#define FIELD_TYPE_INT24 MYSQL_TYPE_INT24
#define FIELD_TYPE_DATE MYSQL_TYPE_DATE
#define FIELD_TYPE_TIME MYSQL_TYPE_TIME
#define FIELD_TYPE_DATETIME MYSQL_TYPE_DATETIME
#define FIELD_TYPE_YEAR MYSQL_TYPE_YEAR
#define FIELD_TYPE_NEWDATE MYSQL_TYPE_NEWDATE
#define FIELD_TYPE_ENUM MYSQL_TYPE_ENUM
#define FIELD_TYPE_SET MYSQL_TYPE_SET
#define FIELD_TYPE_TINY_BLOB MYSQL_TYPE_TINY_BLOB
#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB
#define FIELD_TYPE_LONG_BLOB MYSQL_TYPE_LONG_BLOB
#define FIELD_TYPE_BLOB MYSQL_TYPE_BLOB
#define FIELD_TYPE_VAR_STRING MYSQL_TYPE_VAR_STRING
#define FIELD_TYPE_STRING MYSQL_TYPE_STRING
#define FIELD_TYPE_CHAR MYSQL_TYPE_TINY
#define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM
#define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY
#if TO_BE_INCLUDED_LATER
/* For bind applications, to indicate unsigned buffers */
#define MYSQL_TYPE_UTINY -10
#define MYSQL_TYPE_USHORT -9
#define MYSQL_TYPE_ULONG -8
#define MYSQL_TYPE_UFLOAT -7
#define MYSQL_TYPE_UDOUBLE -6
#define MYSQL_TYPE_ULONGLONG -5
#define MYSQL_TYPE_UINT24 -4
#endif
#define net_new_transaction(net) ((net)->pkt_nr=0)
......@@ -251,5 +290,6 @@ void my_thread_end(void);
#endif
#define NULL_LENGTH ((unsigned long) ~0) /* For net_store_length */
#define MYSQL_LONG_DATA_END 0xFF /* For indication of long data ending */
#endif
......@@ -49,7 +49,18 @@ const char *client_errors[]=
"Error on SHOW SLAVE STATUS:",
"Error on SHOW SLAVE HOSTS:",
"Error connecting to slave:",
"Error connecting to master:"
"Error connecting to master:",
"Invalid connection handle",
"Invalid use of null pointer",
"Memory allocation error",
"Statement not prepared",
"Not all parameters data supplied",
"Data truncated",
"Not all parameters bound for the row fetch",
"Failed to send the parameter data",
"No parameters exists in the statement",
"Invalid parameter number",
"Can't send long data for non string or binary data types"
};
/* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */
......@@ -82,7 +93,18 @@ const char *client_errors[]=
"Error on SHOW SLAVE STATUS:",
"Error on SHOW SLAVE HOSTS:",
"Error connecting to slave:",
"Error connecting to master:"
"Error connecting to master:",
"Invalid connection handle",
"Invalid use of null pointer",
"Memory allocation error",
"Statement not prepared",
"Not all parameters data supplied",
"Data truncated",
"Not all parameters bound for the row fetch",
"Failed to send the parameter data",
"No parameters exists in the statement",
"Invalid parameter number",
"Can't send long data for non string or binary data types"
};
#else /* ENGLISH */
......@@ -113,7 +135,18 @@ const char *client_errors[]=
"Error on SHOW SLAVE STATUS:",
"Error on SHOW SLAVE HOSTS:",
"Error connecting to slave:",
"Error connecting to master:"
"Error connecting to master:",
"Invalid connection handle",
"Invalid use of null pointer",
"Memory allocation error",
"Statement not prepared",
"Not all parameters data supplied",
"Data truncated",
"Not all parameters bound for the row fetch",
"Failed to send the parameter data",
"No parameters exists in the statement",
"Invalid parameter number",
"Can't send long data for non string or binary data types"
};
#endif
......
......@@ -40,7 +40,7 @@
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SELECT_H
# include <select.h>
#include <select.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
......@@ -56,11 +56,16 @@
#define INADDR_NONE -1
#endif
#include <assert.h> /* for DBUG_ASSERT() */
static my_bool mysql_client_init=0;
uint mysql_port=0;
my_string mysql_unix_port=0;
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS)
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG\
| CLIENT_LOCAL_FILES | CLIENT_TRANSACTIONS\
| CLIENT_PROTOCOL_41)
#ifdef __WIN__
#define CONNECT_TIMEOUT 20
......@@ -76,6 +81,13 @@ my_string mysql_unix_port=0;
#define SOCKET_ERROR -1
#endif /* __WIN__ */
#define MAX_LONG_DATA_LENGTH 8192 /* if allowed through some
configuration, then this needs to
be changed
*/
#define protocol_41(A) ((A)->server_capabilities & CLIENT_PROTOCOL_41)
#define unsigned_field(A) ((A)->flags & UNSIGNED_FLAG)
static void mysql_once_init(void);
static MYSQL_DATA *read_rows (MYSQL *mysql,MYSQL_FIELD *fields,
uint field_count);
......@@ -884,7 +896,7 @@ static void mysql_read_default_options(struct st_mysql_options *options,
static MYSQL_FIELD *
unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
my_bool default_value, my_bool long_flag_protocol)
my_bool default_value, uint server_capabilities)
{
MYSQL_ROWS *row;
MYSQL_FIELD *field,*result;
......@@ -895,29 +907,58 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
if (!result)
DBUG_RETURN(0);
for (row=data->data; row ; row = row->next,field++)
if(server_capabilities & CLIENT_PROTOCOL_41)
{
field->org_table= field->table= strdup_root(alloc,(char*) row->data[0]);
field->name= strdup_root(alloc,(char*) row->data[1]);
field->length= (uint) uint3korr(row->data[2]);
field->type= (enum enum_field_types) (uchar) row->data[3][0];
if (long_flag_protocol)
/* server is 4.1, and returns the new field result format */
for (row=data->data; row ; row = row->next,field++)
{
field->flags= uint2korr(row->data[4]);
field->decimals=(uint) (uchar) row->data[4][2];
field->db = strdup_root(alloc,(char*) row->data[0]);
field->table = strdup_root(alloc,(char*) row->data[1]);
field->org_table= strdup_root(alloc,(char*) row->data[2]);
field->name = strdup_root(alloc,(char*) row->data[3]);
field->org_name = strdup_root(alloc,(char*) row->data[4]);
field->length = (uint) uint3korr(row->data[5]);
field->type = (enum enum_field_types) (uchar) row->data[6][0];
field->flags= uint2korr(row->data[7]);
field->decimals=(uint) (uchar) row->data[7][2];
if (INTERNAL_NUM_FIELD(field))
field->flags|= NUM_FLAG;
if (default_value && row->data[8])
field->def=strdup_root(alloc,(char*) row->data[8]);
else
field->def=0;
field->max_length= 0;
}
else
}
else /* old ones, for backward compatibility */
{
for (row=data->data; row ; row = row->next,field++)
{
field->flags= (uint) (uchar) row->data[4][0];
field->decimals=(uint) (uchar) row->data[4][1];
field->org_table= field->table= strdup_root(alloc,(char*) row->data[0]);
field->name= strdup_root(alloc,(char*) row->data[1]);
field->length= (uint) uint3korr(row->data[2]);
field->type= (enum enum_field_types) (uchar) row->data[3][0];
if (server_capabilities & CLIENT_LONG_FLAG)
{
field->flags= uint2korr(row->data[4]);
field->decimals=(uint) (uchar) row->data[4][2];
}
else
{
field->flags= (uint) (uchar) row->data[4][0];
field->decimals=(uint) (uchar) row->data[4][1];
}
if (INTERNAL_NUM_FIELD(field))
field->flags|= NUM_FLAG;
if (default_value && row->data[5])
field->def=strdup_root(alloc,(char*) row->data[5]);
else
field->def=0;
field->max_length= 0;
}
if (INTERNAL_NUM_FIELD(field))
field->flags|= NUM_FLAG;
if (default_value && row->data[5])
field->def=strdup_root(alloc,(char*) row->data[5]);
else
field->def=0;
field->max_length= 0;
}
free_rows(data); /* Free old data */
DBUG_RETURN(result);
......@@ -2203,12 +2244,12 @@ get_info:
mysql->server_status|= SERVER_STATUS_IN_TRANS;
mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */
if (!(fields=read_rows(mysql,(MYSQL_FIELD*) 0,5)))
if (!(fields=read_rows(mysql,(MYSQL_FIELD*)0,protocol_41(mysql) ? 8:5)))
DBUG_RETURN(-1);
if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,
(uint) field_count,0,
(my_bool) test(mysql->server_capabilities &
CLIENT_LONG_FLAG))))
mysql->server_capabilities)))
DBUG_RETURN(-1);
mysql->status=MYSQL_STATUS_GET_RESULT;
mysql->field_count= (uint) field_count;
......@@ -2573,7 +2614,7 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128);
if (simple_command(mysql,COM_FIELD_LIST,buff,(ulong) (end-buff),1) ||
!(query = read_rows(mysql,(MYSQL_FIELD*) 0,6)))
!(query = read_rows(mysql,(MYSQL_FIELD*)0,protocol_41(mysql) ? 9:6)))
DBUG_RETURN(NULL);
free_old_query(mysql);
......@@ -2587,9 +2628,7 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
mysql->fields=0;
result->field_count = (uint) query->rows;
result->fields= unpack_fields(query,&result->field_alloc,
result->field_count,1,
(my_bool) test(mysql->server_capabilities &
CLIENT_LONG_FLAG));
result->field_count,1,mysql->server_capabilities);
result->eof=1;
DBUG_RETURN(result);
}
......@@ -2610,11 +2649,10 @@ mysql_list_processes(MYSQL *mysql)
free_old_query(mysql);
pos=(uchar*) mysql->net.read_pos;
field_count=(uint) net_field_length(&pos);
if (!(fields = read_rows(mysql,(MYSQL_FIELD*) 0,5)))
if (!(fields = read_rows(mysql,(MYSQL_FIELD*)0,protocol_41(mysql) ? 8: 5)))
DBUG_RETURN(NULL);
if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0,
(my_bool) test(mysql->server_capabilities &
CLIENT_LONG_FLAG))))
mysql->server_capabilities)))
DBUG_RETURN(0);
mysql->status=MYSQL_STATUS_GET_RESULT;
mysql->field_count=field_count;
......@@ -3052,3 +3090,1183 @@ myodbc_remove_escape(MYSQL *mysql,char *name)
}
*to=0;
}
/********************************************************************
Implementation of new client-server prototypes for 4.1 version
starts from here ..
my_* and genaral function names are internal implementations
mysql_* are real prototypes used by applications
*********************************************************************/
/********************************************************************
Misc Utility functions
********************************************************************/
/*
Set the internal stmt error messages
*/
static void set_stmt_error(MYSQL_STMT * stmt, int errcode)
{
DBUG_ENTER("set_stmt_error");
DBUG_PRINT("enter", ("error :[%d][%s]", errcode, ER(errcode)));
DBUG_ASSERT(stmt != 0);
stmt->err_no = errcode;
strmov(stmt->error, ER(errcode));
DBUG_VOID_RETURN;
}
/*
Copy error message to statement handler
*/
static void set_stmt_errmsg(MYSQL_STMT * stmt, char *err, int errcode)
{
DBUG_ENTER("set_stmt_error_msg");
DBUG_PRINT("enter", ("error :[%d][%s]", errcode, err));
DBUG_ASSERT(stmt != 0);
stmt->err_no = errcode;
if (err && err[0])
strmov(stmt->error, err);
DBUG_VOID_RETURN;
}
/*
Set the internal error message to mysql handler
*/
static void set_mysql_error(MYSQL * mysql, int errcode)
{
DBUG_ENTER("set_mysql_error");
DBUG_PRINT("enter", ("error :[%d][%s]", errcode, ER(errcode)));
DBUG_ASSERT(mysql != 0);
mysql->net.last_errno = errcode;
strmov(mysql->net.last_error, ER(errcode));
}
/*
Return the duplicate string by allocating the memory
*/
static char *my_dupp_str(const char *from, int length)
{
char *str;
if ((str = my_malloc(length + 1, MYF(MY_WME))))
{
memcpy(str, from, length);
str[length] = 0;
}
return str;
}
/*
Return the reallocted string
*/
static char *my_realloc_str(NET *net, int length)
{
char *from = net->buff;
ulong nead = net->buf_length + length;
DBUG_ENTER("my_realloc_str");
if ( nead > net->max_packet )
{
char *new_buff;
ulong pkt_length = nead + 8192;
if (pkt_length > max_allowed_packet)
{
DBUG_PRINT("error",("Needed %ld but max_allowed_packet is %ld",
pkt_length,max_allowed_packet));
DBUG_RETURN(0);
}
if (!(new_buff = (char *)my_realloc(from, pkt_length, MYF(MY_WME))))
DBUG_RETURN(0);
net->max_packet = pkt_length;
DBUG_RETURN(new_buff);
}
DBUG_RETURN(from);
}
/*
Return the type length
*/
static ulong return_result_length(uint field_type, ulong length)
{
switch (field_type) {
case MYSQL_TYPE_TINY:
return 1;
case MYSQL_TYPE_SHORT:
return 2;
case MYSQL_TYPE_INT24:
return 3;
case MYSQL_TYPE_LONG:
return 4;
case MYSQL_TYPE_LONGLONG:
return 8;
case MYSQL_TYPE_FLOAT:
return 4;
case MYSQL_TYPE_DOUBLE:
return 8;
default:
return length;
}
}
/*
Get the parameter count value
*/
static ulong my_get_param_count(MYSQL *mysql)
{
uchar *pos;
mysql = mysql->last_used_con;
if (net_safe_read(mysql) == packet_error)
return 0;
pos=(uchar*) mysql->net.read_pos;
if (!net_field_length(&pos))
{
return (ulong)net_field_length(&pos);
}
return 0;
}
/********************************************************************
Prepare related implementations
********************************************************************/
/*
Read the prepare statement results ..
*/
int my_read_prepare_result(MYSQL *mysql,MYSQL_STMT *stmt)
{
uchar *pos;
ulong field_count;
MYSQL_DATA *fields_data;
ulong length;
DBUG_ENTER("my_read_prepare_result");
mysql = mysql->last_used_con;
if ((length = net_safe_read(mysql)) == packet_error)
DBUG_RETURN(-1);
if (stmt->fields)
free_root(&stmt->mem_root,MYF(0));
init_alloc_root(&stmt->mem_root,8192,0);
stmt->fields=0;
stmt->field_count=0;
pos=(uchar*) mysql->net.read_pos;
if ((field_count= net_field_length(&pos)) == 0)
{
stmt->param_count= (ulong)net_field_length_ll(&pos);
DBUG_RETURN(0);
}
if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
mysql->server_status|= SERVER_STATUS_IN_TRANS;
mysql->extra_info= net_field_length_ll(&pos);
if (!(fields_data=read_rows(mysql,(MYSQL_FIELD*)0,protocol_41(mysql) ? 8:5)))
DBUG_RETURN(-1);
if (!(stmt->fields=unpack_fields(fields_data,&stmt->mem_root,
(uint) field_count,0,
mysql->server_capabilities)))
DBUG_RETURN(-1);
mysql->status = MYSQL_STATUS_READY;
stmt->field_count= (uint) field_count;
DBUG_RETURN(0);
}
/*
Prepare the query and return the new statement handle to
caller.
Also update the total parameter count along with resultset
metadata information by reading from server
*/
MYSQL_STMT *STDCALL
mysql_real_prepare(MYSQL *mysql, const char *query, ulong length)
{
MYSQL_STMT *stmt;
DBUG_ENTER("mysql_real_prepare");
DBUG_ASSERT(mysql != 0);
#ifdef EXTRA_CHECK_ARGUMENTS
if (!query)
{
set_mysql_error(mysql, CR_NULL_POINTER);
DBUG_RETURN(0);
}
#endif
if (simple_command(mysql, COM_PREPARE, query, length, 1))
DBUG_RETURN(0);
if (!(stmt = (MYSQL_STMT *) my_malloc(sizeof(MYSQL_STMT),
MYF(MY_WME | MY_ZEROFILL))) ||
!(stmt->query = my_dupp_str((char *)query, length)))
{
my_free((gptr)stmt, MY_ALLOW_ZERO_PTR);
set_mysql_error(mysql, CR_MEMORY_ERROR);
DBUG_RETURN(0);
}
DBUG_ASSERT(stmt != 0);
if (my_read_prepare_result(mysql,stmt))
{
my_free((gptr)stmt, MYF(MY_WME));
DBUG_RETURN(0);
}
if (stmt->fields)
stmt->param_count = my_get_param_count(mysql);
stmt->mysql = mysql;
stmt->state = MY_ST_PREPARE;
stmt->err_no = 0;
stmt->types_supplied = 0;
DBUG_PRINT("info", ("Parameter count: %ld", stmt->param_count));
DBUG_RETURN(stmt);
}
/*
Prepare the query and return the new statement handle to
caller.
Also update the total parameter count along with resultset
metadata information by reading from server
*/
MYSQL_STMT *STDCALL mysql_prepare(MYSQL *mysql, const char *query)
{
DBUG_ENTER("mysql_prepare");
DBUG_ASSERT(query != 0);
DBUG_RETURN(mysql_real_prepare(mysql,query,strlen(query)));
}
/*
Returns prepared meta information in the form of resultset
to client..
*/
MYSQL_RES * STDCALL
mysql_prepare_result(MYSQL_STMT *stmt)
{
MYSQL_RES *result;
DBUG_ENTER("mysql_prepare_result");
if (!stmt->fields)
DBUG_RETURN(0);
/* Will be freed during mysql_stmt_close as the prepare
meta info should live till the stmt life
*/
if (!(result=(MYSQL_RES*) alloc_root(&stmt->mem_root,
(uint) (sizeof(MYSQL_RES)+ sizeof(ulong) *
stmt->field_count))))
{
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(0);
}
bzero((char*) result, sizeof(MYSQL_RES));
result->eof=1; /* Marker for buffered */
result->lengths=(ulong*) (result+1);
result->row_count= 0;
result->fields= stmt->fields;
result->field_count= stmt->field_count;
DBUG_RETURN(result);
}
/********************************************************************
Prepare-execute, and param handling
*********************************************************************/
/*
Store the buffer type
*/
static void my_store_param_type(NET *net, uint type)
{
int2store(net->buff+net->buf_length, type);
net->buf_length+=2;
}
/*
Store the length of parameter data
*/
static char * my_store_param_length(char *pkg, ulong length)
{
uchar *packet=(uchar*) pkg;
if (length < 251)
{
*packet=(uchar) length;
return (char*) packet+1;
}
*packet++ = 252;
int2store(packet,(uint) length);
return (char*) packet+2;
}
/*
Store the integer data
*/
static bool my_store_param_long(NET *net, longlong *data, uint length)
{
char *packet;
if (!(net->buff = my_realloc_str(net, net->buf_length + length + 1)))
return 1;
packet = (char *)net->buff+net->buf_length;
switch(length) {
case 1:
{
char value = (char)*data;
*packet = value;
}
break;
case 2:
{
short value = (short)*data;
int2store(packet,value);
}
break;
case 3:
{
int value = (int)*data;
int3store(packet,value);
}
break;
case 4:
{
long value = (long)*data;
int4store(packet, value);
}
break;
default:
{
longlong value = (longlong)*data;
int8store(packet, value);
}
}
net->buf_length += length;
return 0;
}
/*
Store the float data
*/
static bool my_store_param_double(NET *net, double *data, uint length)
{
char *packet;
if (!(net->buff = my_realloc_str(net, net->buf_length + length + 1)))
return 1;
packet = (char *)net->buff+net->buf_length;
if (length == 4)
{
float value = (float)*data;
float4store(packet, value);
}
else
{
double value = (double)*data;
float8store(packet, value);
}
net->buf_length += length;
return 0;
}
/*
Store NULL data
*/
static my_bool my_store_param_null(NET * net)
{
if (!(net->buff = my_realloc_str(net, net->buf_length + 1)))
return 1; /* Signal error */
net->buff[net->buf_length++] = (char)251;
return 0;
}
/*
Store string and binary data
*/
static my_bool my_store_param_str(NET * net, const char *from, uint length)
{
char *to;
if (!(net->buff = my_realloc_str(net, net->buf_length + 5 + length)))
return 1;
to = (char *)my_store_param_length((char *)net->buff + net->buf_length,
(ulong) length);
memcpy(to, from, length);
net->buf_length = (ulong) ((char *)(to + length) - (char *)(net->buff));
return 0;
}
/*
Set parameter data by reading from input buffers from the
client application
*/
static my_bool my_store_param(MYSQL_STMT * stmt, MYSQL_BIND * param)
{
MYSQL *mysql = stmt->mysql;
NET *net = &mysql->net;
DBUG_ENTER("my_store_param");
DBUG_PRINT("enter",("type : %d, buffer :%lx", param->buffer_type,
param->buffer));
if (!stmt->types_supplied)
my_store_param_type(net, (uint)param->buffer_type);
if (!param->buffer)
{
if (my_store_param_null(net))
goto err;
DBUG_RETURN(0);
}
switch (param->buffer_type)
{
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
if (my_store_param_long(net,(longlong *)param->buffer,param->bind_length))
goto err;
break;
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE:
if (my_store_param_double(net,(double *)param->buffer,param->bind_length))
goto err;
break;
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
{
/*
Binary handling, application should supply valid length else
will lead into problems
*/
ulong length = param->buffer_length;
if(param->length) /* ovverite, if length pointer exists */
length = *param->length;
if (my_store_param_str(net, (char *)param->buffer, length))
goto err;
}
break;
default:
{
/* All other conversions should fall through to string type .. */
char *data = param->buffer;
ulong length;
if (!param->length)
{
/*
This case is not supose to happen, application should
supply the length for strings and binary data
*/
if (data)
length = param->buffer_length ? strnlen(data,param->buffer_length) :
strlen(data);
else
{
DBUG_PRINT("warning",("data is a null pointer"));
length=0;
}
}
else
length= *param->length;
if (my_store_param_str(net,data,length))
goto err;
}
}
DBUG_RETURN(0);
err:
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(1);
}
/*
Send the prepare query to server for execution
*/
static int my_execute(MYSQL_STMT * stmt, char *packet, ulong length)
{
MYSQL *mysql = stmt->mysql;
NET *net = &mysql->net;
DBUG_ENTER("my_execute");
DBUG_PRINT("enter",("packet: %s, length :%d",packet ? packet :" ", length));
mysql->last_used_con = mysql;
if (simple_command(mysql, COM_EXECUTE, packet, length, 1) ||
mysql_read_query_result(mysql))
{
set_stmt_errmsg(stmt, net->last_error, net->last_errno);
DBUG_RETURN(-1);
}
stmt->state = MY_ST_EXECUTE;
if (stmt->bind)
{
mysql_free_result(stmt->result);
stmt->result = mysql_store_result(mysql);
}
DBUG_RETURN(0);
}
/*
Execute the prepare query
*/
int STDCALL mysql_execute(MYSQL_STMT * stmt)
{
DBUG_ENTER("mysql_execute");
DBUG_ASSERT(stmt != 0);
if (stmt->state == MY_ST_UNKNOWN)
{
set_stmt_error(stmt, CR_NO_PREPARE_STMT);
DBUG_RETURN(-1);
}
stmt->mysql->fields = stmt->fields;
if (stmt->param_count)
{
NET *net = &stmt->mysql->net;
MYSQL_BIND *param;
ulong param_count;
char *param_data;
if (!stmt->params)
{
/* Parameters exists, but no bound buffers */
set_stmt_error(stmt, CR_NOT_ALL_PARAMS_BOUND);
DBUG_RETURN(-1);
}
net_clear(net);
net->buf_length = 0; /* clear net */
/* In case if buffers (type) alterned, indicate to server */
net->buff[net->buf_length++] = (char)stmt->types_supplied;
for (param=stmt->params, param_count = 0;
param_count < stmt->param_count; param_count++, param++)
{
/* Check if any long data, run-time supply exists */
if (param->is_long_data && !param->long_ended)
DBUG_RETURN(MYSQL_NEED_DATA);
if (my_store_param(stmt, param))
DBUG_RETURN(-1);
}
if (!(param_data = my_memdup((byte *) net->buff,
net->buf_length, MYF(MY_WME))))
{
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(-1);
}
if (my_execute(stmt,param_data,net->buf_length))
{
my_free(param_data, MYF(MY_WME));
DBUG_RETURN(-1);
}
my_free(param_data, MYF(MY_WME));
stmt->types_supplied = 1;
DBUG_RETURN(0);
}
DBUG_RETURN(my_execute(stmt,0,0));
}
/*
Return total parameters count in the statement
*/
ulong STDCALL mysql_param_count(MYSQL_STMT * stmt)
{
DBUG_ENTER("mysql_param_count");
DBUG_ASSERT(stmt != 0);
DBUG_RETURN(stmt->param_count);
}
/*
Setup the parameter data buffers from application
*/
int STDCALL mysql_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bind)
{
MYSQL_BIND *param;
ulong count;
DBUG_ENTER("mysql_bind_param");
DBUG_ASSERT(stmt != 0);
#ifdef CHECK_EXTRA_ARGUMENTS
if (!bind)
{
set_stmt_error(stmt, CR_NULL_POINTER);
DBUG_RETURN(-1);
}
if (stmt->state == MY_ST_UNKNOWN)
{
set_stmt_error(stmt, CR_NO_PREPARE_STMT);
DBUG_RETURN(-1);
}
if (!stmt->param_count)
{
set_stmt_error(stmt, CR_NO_PARAMETERS_EXISTS);
DBUG_RETURN(-1);
}
#endif
/* In case if buffers bounded previously, free it */
my_free((gptr) stmt->params, MY_ALLOW_ZERO_PTR);
if (!(stmt->params=(MYSQL_BIND *)my_memdup((byte *) bind,
sizeof(MYSQL_BIND) * stmt->param_count, MYF(MY_WME))))
{
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(0);
}
for (param=stmt->params, count=0;
count < stmt->param_count; count++, param++)
{
if (!param)
{
/* Not all parameters bound by the application */
my_free((gptr) stmt->params, MY_ALLOW_ZERO_PTR);
set_stmt_error(stmt, CR_NOT_ALL_PARAMS_BOUND);
DBUG_RETURN(-1);
}
if (param->is_long_data &&
(param->buffer_type < MYSQL_TYPE_TINY_BLOB ||
param->buffer_type > MYSQL_TYPE_STRING))
{
/*
Long data handling should be used only for string/binary
types only
*/
my_free((gptr) stmt->params, MY_ALLOW_ZERO_PTR);
set_stmt_error(stmt, CR_INVALID_BUFFER_USE);
DBUG_RETURN(-1);
}
/* setup default buffer_length for int and double types */
param->bind_length = return_result_length(param->buffer_type,
param->buffer_length);
}
stmt->types_supplied = 0;
DBUG_RETURN(0);
}
/********************************************************************
Long data implementation
*********************************************************************/
/*
Store long data buffer type, to distinguish string and binary
*/
static char* my_store_long_type(MYSQL_STMT *stmt, char *packet, uint type)
{
*packet++ = (char)stmt->types_supplied;
if (!stmt->types_supplied)
{
int2store(packet, type);
packet += 2;
}
return(packet);
}
/*
Long data in pieces, if buffured successfully send '0' as
status else send '1'
if length == MYSQL_END_OF_DATA, then thats the last data
piece for the parameter
*/
my_bool STDCALL
mysql_send_long_data(MYSQL_STMT *stmt, uint param_number,
gptr data, ulong length)
{
MYSQL_BIND *param;
MYSQL *mysql;
char *packet;
ulong packet_length, long_length;
DBUG_ENTER("mysql_send_long_data");
DBUG_ASSERT( stmt != 0 );
DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld",
param_number, data, length));
if (!(param = (stmt->params+param_number)))
{
set_stmt_error(stmt, CR_INVALID_PARAMETER_NO);
DBUG_RETURN(1);
}
mysql = stmt->mysql;
if (length == MYSQL_LONG_DATA_END || param->long_ended)
{
if (!stmt->long_alloced)
{
stmt->long_length = MAX_LONG_DATA_LENGTH;
my_free(stmt->long_data, MY_ALLOW_ZERO_PTR);
if (!(stmt->long_data = (char*) my_malloc(stmt->long_length,MYF(0))))
{
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(1);
}
stmt->long_alloced = 1;
}
packet = stmt->long_data;
packet = my_store_param_length(packet, param_number);/* number */
packet = my_store_long_type(stmt, packet, param->buffer_type);/* type */
packet_length = (ulong) ((char *)packet - (char *)(stmt->long_data));
*packet = (char )MYSQL_LONG_DATA_END; /* data end indicator */
packet_length++;
if (simple_command(mysql,COM_LONG_DATA,stmt->long_data,packet_length,0))
goto err;
stmt->types_supplied = 1;
param->long_ended = 1;
DBUG_RETURN(0);
}
if (!stmt->long_alloced || stmt->long_length < length+7)
{
stmt->long_length = ( MAX_LONG_DATA_LENGTH > (length + 7)) ?
MAX_LONG_DATA_LENGTH : length + 7;
my_free(stmt->long_data, MY_ALLOW_ZERO_PTR);
if (!(stmt->long_data = (char*) my_malloc(stmt->long_length ,MYF(0))))
{
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(1);
}
stmt->long_alloced = 1;
}
packet = stmt->long_data;
long_length = stmt->long_length;
packet = my_store_param_length(packet, param_number);/* number */
packet = my_store_long_type(stmt, packet, param->buffer_type);/* type */
{
char *to = my_store_param_length(packet, length); /* data length */
memcpy(to, data, length); /* data */
packet_length = (ulong)((char *)(to + length) - (char *)(stmt->long_data));
}
/*
Send the data to server directly for buffering ..
TO_BE_DELETED : cross check this with Monty, becuase over the phone
Monty said not to have check for max_packet_length for long data to
have a local storage, instead send it to server directlty, but in
one of his e-mail says to store upto max_packet_size locally..
*/
if (simple_command(mysql, COM_LONG_DATA, stmt->long_data, packet_length, 0))
goto err;
stmt->types_supplied = 1;
DBUG_RETURN(0);
err:
set_stmt_errmsg(stmt,(char *)mysql->net.last_error, mysql->net.last_errno);
DBUG_RETURN(-1);
}
/********************************************************************
Fetch-bind related implementations
*********************************************************************/
/*
Setup the bind buffers for resultset processing
*/
int STDCALL mysql_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bind)
{
MYSQL_BIND *param;
ulong count,bind_count, *length;
DBUG_ENTER("mysql_bind_result");
DBUG_ASSERT(stmt != 0);
if (!bind)
{
set_stmt_error(stmt, CR_NULL_POINTER);
DBUG_RETURN(-1);
}
count = sizeof(*bind) / sizeof(MYSQL_BIND) + 1;
bind_count = stmt->result ? stmt->result->field_count : count;
/* bind_count now will have total fields, if no resultset
exists, then this points to sizeof(bind)
*/
if (bind_count < count)
{
set_stmt_error(stmt, CR_NOT_ALL_BUFFERS_BOUND);
DBUG_RETURN(-1);
}
/* In case previously bound, free it */
my_free((gptr)stmt->bind, MY_ALLOW_ZERO_PTR);
if (!(stmt->bind=(MYSQL_BIND *)my_memdup((byte *) bind,
sizeof(MYSQL_BIND) * bind_count, MYF(MY_WME))))
{
set_stmt_error(stmt, CR_MEMORY_ERROR);
DBUG_RETURN(0);
}
/* set the lengths */
for (param=stmt->bind,count=0; count<bind_count; count++,param++)
{
if((length=bind->length))
*length = return_result_length(bind->buffer_type, bind->buffer_length);
}
DBUG_RETURN(0);
}
/*
Copy a row of data to bound buffers, used in myql_fetch
*/
static int
my_fetch_record(MYSQL_BIND *bind, MYSQL_FIELD *field,
char *value)
{
gptr buff = bind->buffer;
ulong buff_length = bind->buffer_length;
long *length = bind->length , tmp_length;
enum enum_field_types buff_type = bind->buffer_type;
DBUG_ENTER("my_fetch_record");
if (!value)
{
buff = NullS;
DBUG_RETURN(0);
}
if (!length)
length= &tmp_length;
/* Copy the data which is in string format to application buffers
based on its type .. no cross conversion needed as everything
is in string buffers in result->row.
When the server supports the sending of data in its own format,
then this needs to be cleaned to have all cross combinations
*/
switch (buff_type)
{
case MYSQL_TYPE_TINY:
if (unsigned_field(field))
*((uchar *) buff) = ((uchar) (uint) atoi(value));
else
*((char *) buff) = ((char)atoi(value));
break;
case MYSQL_TYPE_SHORT:
if (unsigned_field(field))
*((ushort *) buff) = (ushort) (uint) atol(value);
else
*((short *) buff) = (short)atoi(value);
break;
case MYSQL_TYPE_LONG:
if (unsigned_field(field))
{
char *end_ptr;
*((ulong *) buff) = strtoul(value, &end_ptr, 10);
}
else
{
uint actual_length = strlen(value);
if (actual_length >= 10 && value[4] == '-' && value[7] == '-' &&
(!value[10] || value[10] == ' '))
{
*((long *) buff) = ((long)atol(value) * 10000L +
(long)atol(value + 5) * 100L +
(long)atol(value + 8));
}
else
*((long *) buff) = (long)atol(value);
}
break;
case MYSQL_TYPE_LONGLONG:
if (unsigned_field(field))
*((ulonglong *) buff) = (ulonglong) strtoull(value, NULL, 10);
else
*((longlong *) buff) = (longlong) strtoll(value, NULL, 10);
break;
case MYSQL_TYPE_FLOAT:
*((float *) buff) = (float)atof(value);
break;
case MYSQL_TYPE_DOUBLE:
*((double *) buff) = (double)atof(value);
break;
/* TODO : for strings and blobs, Monty comments
For strings: We should also support letting the user specify
a pointer to a char * which we would update to where the
date is in the memory. In this case we would only update the
pointer to where we have the string in memory. This would make
things twice as fast for us as we would not have to move things
around and would also require much less memoryfrom the application.
so, just return the address with the length pointer updated ?
confirm with Monty and enble the following lines of code by
disbling the existing code which follows next to this.
If this is enabled, then there is no need of buffer_length in
MYSQL_BIND structure, and clean the code related to that
*/
#if TO_BE_IMPLEMENTED
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
*length = sizeof(value);/* will contain size */
(char *)bind->buffer = value; /* send the data address to application */
break;
default:
*length = strlen(value);/* will contain string length */
(char *)bind->buffer = value; /* send the data address to application */
break;
#endif
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
*length = sizeof(value);
memcpy((char *)buff, value, buff_length);
break;
default:
buff_length--;
*length = strlen(value);
strmake((char *)buff, value, buff_length);
}
DBUG_RETURN(0);
}
/*
Fetch row data to bind buffers
*/
static int my_fetch_row(MYSQL_STMT *stmt, MYSQL_RES *result, MYSQL_ROW values)
{
MYSQL_BIND *bind, *end;
MYSQL_FIELD *field;
ulong record=0;
result->current_row = values;
result->row_count++;
/* Copy complete row to application buffers */
for (bind = stmt->bind, end = (MYSQL_BIND *) bind + result->field_count;
bind < end;
bind++, values++)
{
field = &result->fields[record++];
if (my_fetch_record(bind, field, *values))
return -1;
}
return 0;
}
/*
Fetch and return row data to bound buffers, if any
*/
int STDCALL mysql_fetch(MYSQL_STMT * stmt)
{
MYSQL_RES *result;
DBUG_ENTER("mysql_fetch");
DBUG_ASSERT(stmt != 0);
result = stmt->result;
if (!result)
DBUG_RETURN(MYSQL_NO_DATA);
if (!result->data)
{
if (!result->eof)
{
if (!(read_one_row(result->handle, result->field_count,
result->row, result->lengths)))
{
DBUG_RETURN(my_fetch_row(stmt, result, result->row));
}
else
{
DBUG_PRINT("info", ("end of data"));
result->eof = 1;
result->handle->status = MYSQL_STATUS_READY;
/* Don't clear handle in mysql_free_results */
result->handle = 0;
}
}
DBUG_RETURN(MYSQL_NO_DATA); /* no more data */
}
{
MYSQL_ROW values;
if (!result->data_cursor)
{
DBUG_PRINT("info", ("end of data"));
result->current_row = (MYSQL_ROW) NULL;
DBUG_RETURN(MYSQL_NO_DATA);
}
values = result->data_cursor->data;
result->data_cursor = result->data_cursor->next;
DBUG_RETURN(my_fetch_row(stmt,result,values));
}
DBUG_RETURN(0);
}
/********************************************************************
Misc function implementations
*********************************************************************/
/*
Close the statement handle by freeing all resources
*/
int STDCALL mysql_stmt_close(MYSQL_STMT * stmt)
{
DBUG_ENTER("mysql_stmt_close");
DBUG_ASSERT(stmt != 0);
free_root(&stmt->mem_root,MY_ALLOW_ZERO_PTR);
my_free((gptr) stmt->query, MYF(MY_WME));
my_free((gptr) stmt->bind, MY_ALLOW_ZERO_PTR);
my_free((gptr) stmt->params, MY_ALLOW_ZERO_PTR);
my_free((gptr) stmt->long_data, MY_ALLOW_ZERO_PTR);
my_free((gptr) stmt, MYF(MY_WME));
DBUG_RETURN(0);
}
/*
Return statement error code
*/
uint STDCALL mysql_stmt_errno(MYSQL_STMT * stmt)
{
DBUG_ENTER("mysql_stmt_errno");
DBUG_ASSERT(stmt != 0);
DBUG_RETURN(stmt->err_no);
}
/*
Return statement error message
*/
const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt)
{
DBUG_ENTER("mysql_stmt_error");
DBUG_ASSERT(stmt != 0);
DBUG_RETURN(stmt->error);
}
/*
Commit the current transaction
*/
int STDCALL mysql_commit(MYSQL * mysql)
{
DBUG_ENTER("mysql_commit");
DBUG_ASSERT(mysql != 0);
DBUG_RETURN(mysql_real_query(mysql, "commit", 6));
}
/*
Rollback the current transaction
*/
int STDCALL mysql_rollback(MYSQL * mysql)
{
DBUG_ENTER("mysql_rollback");
DBUG_ASSERT(mysql != 0);
DBUG_RETURN(mysql_real_query(mysql, "rollback", 8));
}
/*
Set autocommit to either true or false
*/
int STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode)
{
DBUG_ENTER("mysql_autocommit");
DBUG_ASSERT(mysql != 0);
DBUG_PRINT("enter", ("mode : %d", auto_mode));
if (auto_mode) /* set to true */
DBUG_RETURN(mysql_real_query(mysql, "set autocommit=1", 16));
DBUG_RETURN(mysql_real_query(mysql, "set autocommit=0", 16));
}
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