diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index c066d743ee1..0ed14928cdb 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -67,3 +67,4 @@ venu@work.mysql.com worm@altair.is.lan zak@balfor.local zak@linux.local +venu@myvenu.com diff --git a/include/errmsg.h b/include/errmsg.h index 76a57f47611..175089ba371 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -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 + diff --git a/include/mysql.h b/include/mysql.h index 5bcdca636c0..d667647d46d 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -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 +#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 diff --git a/include/mysql_com.h b/include/mysql_com.h index c30eb30f779..c259259c9e2 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -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 diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 3fdb9c0ddc6..f1cf667f774 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -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 diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index e054b6777c9..0451e3a89c0 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -40,7 +40,7 @@ #include #include #ifdef HAVE_SELECT_H -# include +#include #endif #ifdef HAVE_SYS_SELECT_H #include @@ -56,11 +56,16 @@ #define INADDR_NONE -1 #endif +#include /* 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; countlength)) + *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)); +}