summaryrefslogtreecommitdiff
path: root/libmysql/libmysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r--libmysql/libmysql.c1290
1 files changed, 1255 insertions, 35 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index b5e013c8e67..052907d8c28 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -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,6 +56,8 @@
#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;
@@ -64,7 +66,10 @@ ulong max_allowed_packet=16*1024*1024L;
ulong net_read_timeout= NET_READ_TIMEOUT;
ulong net_write_timeout= NET_WRITE_TIMEOUT;
-#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
@@ -80,6 +85,13 @@ ulong net_write_timeout= NET_WRITE_TIMEOUT;
#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);
@@ -885,7 +897,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;
@@ -896,29 +908,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);
@@ -1312,9 +1353,9 @@ STDCALL mysql_rpl_query_type(const char* q, int len)
for (; q < q_end; ++q)
{
char c;
- if (isalpha(c=*q))
+ if (my_isalpha(system_charset_info, (c= *q)))
{
- switch(tolower(c)) {
+ switch (my_tolower(system_charset_info,c)) {
case 'i': /* insert */
case 'u': /* update or unlock tables */
case 'l': /* lock tables or load data infile */
@@ -1322,9 +1363,11 @@ STDCALL mysql_rpl_query_type(const char* q, int len)
case 'a': /* alter */
return MYSQL_RPL_MASTER;
case 'c': /* create or check */
- return tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_MASTER ;
+ return my_tolower(system_charset_info,q[1]) == 'h' ? MYSQL_RPL_ADMIN :
+ MYSQL_RPL_MASTER;
case 's': /* select or show */
- return tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_SLAVE;
+ return my_tolower(system_charset_info,q[1]) == 'h' ? MYSQL_RPL_ADMIN :
+ MYSQL_RPL_SLAVE;
case 'f': /* flush */
case 'r': /* repair */
case 'g': /* grant */
@@ -2262,12 +2305,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;
@@ -2632,7 +2675,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);
@@ -2646,9 +2689,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);
}
@@ -2669,11 +2710,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;
@@ -3124,3 +3164,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));
+}