diff options
Diffstat (limited to 'libmysql')
-rw-r--r-- | libmysql/Makefile.am | 3 | ||||
-rw-r--r-- | libmysql/Makefile.shared | 8 | ||||
-rw-r--r-- | libmysql/client_settings.h | 6 | ||||
-rw-r--r-- | libmysql/errmsg.c | 28 | ||||
-rw-r--r-- | libmysql/libmysql.c | 815 | ||||
-rw-r--r-- | libmysql/libmysql.def | 5 |
6 files changed, 624 insertions, 241 deletions
diff --git a/libmysql/Makefile.am b/libmysql/Makefile.am index 72ff44ecef3..0670a0befa8 100644 --- a/libmysql/Makefile.am +++ b/libmysql/Makefile.am @@ -23,7 +23,8 @@ target = libmysqlclient.la target_defs = -DUNDEF_THREADS_HACK -DDONT_USE_RAID @LIB_EXTRA_CCFLAGS@ LIBS = @CLIENT_LIBS@ -INCLUDES = -I$(top_srcdir)/include $(openssl_includes) @ZLIB_INCLUDES@ +INCLUDES = -I$(top_srcdir)/include $(openssl_includes) @ZLIB_INCLUDES@ \ + -I$(top_builddir)/include include $(srcdir)/Makefile.shared diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index 23a8201cbf6..204c833dd21 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -42,7 +42,7 @@ mystringsobjects = strmov.lo strxmov.lo strxnmov.lo strnmov.lo \ bchange.lo bmove.lo bmove_upp.lo longlong2str.lo \ strtoull.lo strtoll.lo llstr.lo my_vsnprintf.lo \ ctype.lo ctype-simple.lo ctype-bin.lo ctype-mb.lo \ - ctype-big5.lo ctype-czech.lo ctype-euc_kr.lo \ + ctype-big5.lo ctype-czech.lo ctype-cp932.lo ctype-eucjpms.lo ctype-euc_kr.lo \ ctype-win1250ch.lo ctype-utf8.lo ctype-extra.lo \ ctype-ucs2.lo ctype-gb2312.lo ctype-gbk.lo \ ctype-sjis.lo ctype-tis620.lo ctype-ujis.lo \ @@ -65,8 +65,9 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.lo \ my_compress.lo array.lo my_once.lo list.lo my_net.lo \ charset.lo charset-def.lo hash.lo mf_iocache.lo \ mf_iocache2.lo my_seek.lo my_sleep.lo \ - my_pread.lo mf_cache.lo md5.lo sha1.lo\ - my_getopt.lo my_gethostbyname.lo my_port.lo + my_pread.lo mf_cache.lo md5.lo sha1.lo \ + my_getopt.lo my_gethostbyname.lo my_port.lo \ + my_rename.lo sqlobjects = net.lo sql_cmn_objects = pack.lo client.lo my_time.lo @@ -81,6 +82,7 @@ CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ $(target) DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ + -DDEFAULT_HOME_ENV=MYSQL_HOME \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" $(target_defs) # The automatic dependencies miss this diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index f53f054a28d..e0f093aa7c9 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -21,7 +21,7 @@ extern my_string mysql_unix_port; CLIENT_TRANSACTIONS | \ CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION) -sig_handler pipe_sig_handler(int sig); +sig_handler my_pipe_sig_handler(int sig); void read_user_name(char *name); my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); @@ -32,7 +32,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); #if !defined(__WIN__) && defined(SIGPIPE) && !defined(THREAD) #define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0; -#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE,pipe_sig_handler) +#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE, my_pipe_sig_handler) #define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); #else #define init_sigpipe_variables @@ -43,7 +43,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group); void mysql_detach_stmt_list(LIST **stmt_list); -MYSQL * STDCALL +MYSQL * cli_mysql_real_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket,ulong client_flag); diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 5fa94e5ff0d..90ad3aefaca 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -205,7 +205,33 @@ const char *client_errors[]= #endif +/* + Register client error messages for use with my_error(). + + SYNOPSIS + init_client_errs() + + RETURN + void +*/ + void init_client_errs(void) { - my_errmsg[CLIENT_ERRMAP] = &client_errors[0]; + (void) my_error_register(client_errors, CR_ERROR_FIRST, CR_ERROR_LAST); +} + + +/* + Unregister client error messages. + + SYNOPSIS + finish_client_errs() + + RETURN + void +*/ + +void finish_client_errs(void) +{ + (void) my_error_unregister(CR_ERROR_FIRST, CR_ERROR_LAST); } diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index c663dab7476..bf797dfd580 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -92,7 +92,7 @@ my_bool net_flush(NET *net); #define unsigned_field(A) ((A)->flags & UNSIGNED_FLAG) static void append_wild(char *to,char *end,const char *wild); -sig_handler pipe_sig_handler(int sig); +sig_handler my_pipe_sig_handler(int sig); static my_bool mysql_client_init= 0; static my_bool org_my_init_done= 0; @@ -186,6 +186,7 @@ void STDCALL mysql_server_end() } else mysql_thread_end(); + finish_client_errs(); free_charsets(); mysql_client_init= org_my_init_done= 0; } @@ -294,11 +295,11 @@ mysql_debug(const char *debug __attribute__((unused))) **************************************************************************/ sig_handler -pipe_sig_handler(int sig __attribute__((unused))) +my_pipe_sig_handler(int sig __attribute__((unused))) { DBUG_PRINT("info",("Hit by signal %d",sig)); #ifdef DONT_REMEMBER_SIGNAL - (void) signal(SIGPIPE,pipe_sig_handler); + (void) signal(SIGPIPE, my_pipe_sig_handler); #endif } @@ -319,6 +320,7 @@ my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, DBUG_ENTER("mysql_master_send_query"); if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0)) DBUG_RETURN(1); + master->reconnect= 1; mysql->last_used_con = master; DBUG_RETURN(simple_command(master, COM_QUERY, q, length, 1)); } @@ -353,6 +355,7 @@ my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, if (!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0, 0,0,0,0)) DBUG_RETURN(1); + slave_to_use->reconnect= 1; DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, q, length, 1)); } @@ -450,6 +453,7 @@ static my_bool get_slaves_from_master(MYSQL* mysql) expand_error(mysql, CR_PROBE_MASTER_CONNECT); DBUG_RETURN(1); } + mysql->reconnect= 1; if (mysql_query(mysql, "SHOW SLAVE HOSTS") || !(res = mysql_store_result(mysql))) @@ -617,6 +621,7 @@ mysql_connect(MYSQL *mysql,const char *host, if (mysql->free_me) my_free((gptr) mysql,MYF(0)); } + mysql->reconnect= 1; DBUG_RETURN(res); } } @@ -1112,25 +1117,6 @@ mysql_fetch_field(MYSQL_RES *result) /************************************************************************** - Get column lengths of the current row - If one uses mysql_use_result, res->lengths contains the length information, - else the lengths are calculated from the offset between pointers. -**************************************************************************/ - -ulong * STDCALL -mysql_fetch_lengths(MYSQL_RES *res) -{ - MYSQL_ROW column; - - if (!(column=res->current_row)) - return 0; /* Something is wrong */ - if (res->data) - (*res->methods->fetch_lengths)(res->lengths, column, res->field_count); - return res->lengths; -} - - -/************************************************************************** Move to a specific row and column **************************************************************************/ @@ -1590,14 +1576,14 @@ mysql_hex_string(char *to, const char *from, ulong length) ulong STDCALL mysql_escape_string(char *to,const char *from,ulong length) { - return escape_string_for_mysql(default_charset_info, to, from, length); + return escape_string_for_mysql(default_charset_info, to, 0, from, length); } ulong STDCALL mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, ulong length) { - return escape_string_for_mysql(mysql->charset, to, from, length); + return escape_string_for_mysql(mysql->charset, to, 0, from, length); } @@ -1728,6 +1714,7 @@ myodbc_remove_escape(MYSQL *mysql,char *name) static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row); static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row); +static int stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row); static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); /* @@ -1735,6 +1722,7 @@ static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); STMT_ATTR_UPDATE_MAX_LENGTH attribute is set. */ static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data); +static my_bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field); /* Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME @@ -1758,6 +1746,20 @@ static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data); #define MAX_DOUBLE_STRING_REP_LENGTH 331 +/* A macro to check truncation errors */ + +#define IS_TRUNCATED(value, is_unsigned, min, max, umax) \ + ((is_unsigned) ? (((value) > (umax) || (value) < 0) ? 1 : 0) : \ + (((value) > (max) || (value) < (min)) ? 1 : 0)) + +#define BIND_RESULT_DONE 1 +/* + We report truncations only if at least one of MYSQL_BIND::error + pointers is set. In this case stmt->bind_result_done |-ed with + this flag. +*/ +#define REPORT_DATA_TRUNCATION 2 + /**************** Misc utility functions ****************************/ /* @@ -1858,12 +1860,14 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) { uchar *pos; uint field_count, param_count; + ulong packet_length; MYSQL_DATA *fields_data; - DBUG_ENTER("read_prepare_result"); + DBUG_ENTER("cli_read_prepare_result"); mysql= mysql->last_used_con; - if (net_safe_read(mysql) == packet_error) + if ((packet_length= net_safe_read(mysql)) == packet_error) DBUG_RETURN(1); + mysql->warning_count= 0; pos= (uchar*) mysql->net.read_pos; stmt->stmt_id= uint4korr(pos+1); pos+= 5; @@ -1871,6 +1875,8 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) field_count= uint2korr(pos); pos+= 2; /* Number of placeholders in the statement */ param_count= uint2korr(pos); pos+= 2; + if (packet_length >= 12) + mysql->warning_count= uint2korr(pos+1); if (param_count != 0) { @@ -1887,7 +1893,6 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) 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= (*mysql->methods->read_rows)(mysql,(MYSQL_FIELD*)0,7))) DBUG_RETURN(1); if (!(stmt->fields= unpack_fields(fields_data,&stmt->mem_root, @@ -1895,9 +1900,10 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) mysql->server_capabilities))) DBUG_RETURN(1); } - stmt->field_count= (uint) field_count; + stmt->field_count= field_count; stmt->param_count= (ulong) param_count; - mysql->warning_count= 0; + DBUG_PRINT("exit",("field_count: %u param_count: %u warning_count: %u", + field_count, param_count, (uint) mysql->warning_count)); DBUG_RETURN(0); } @@ -2131,6 +2137,7 @@ static void update_stmt_fields(MYSQL_STMT *stmt) MYSQL_FIELD *field= stmt->mysql->fields; MYSQL_FIELD *field_end= field + stmt->field_count; MYSQL_FIELD *stmt_field= stmt->fields; + MYSQL_BIND *bind= stmt->bind_result_done ? stmt->bind : 0; DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count); @@ -2141,6 +2148,11 @@ static void update_stmt_fields(MYSQL_STMT *stmt) stmt_field->type = field->type; stmt_field->flags = field->flags; stmt_field->decimals = field->decimals; + if (bind) + { + /* Ignore return value: it should be 0 if bind_result succeeded. */ + (void) setup_one_fetch_function(bind++, stmt_field); + } } } @@ -2443,11 +2455,11 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) char buff[4 /* size of stmt id */ + 5 /* execution flags */]; DBUG_ENTER("execute"); - DBUG_PRINT("enter",("packet: %s, length :%d",packet ? packet :" ", length)); + DBUG_DUMP("packet", packet, length); mysql->last_used_con= mysql; int4store(buff, stmt->stmt_id); /* Send stmt id to server */ - buff[4]= (char) 0; /* no flags */ + buff[4]= (char) stmt->flags; int4store(buff+5, 1); /* iteration count */ if (cli_advanced_command(mysql, COM_EXECUTE, buff, sizeof(buff), packet, length, 1) || @@ -2457,6 +2469,7 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) DBUG_RETURN(1); } stmt->affected_rows= mysql->affected_rows; + stmt->server_status= mysql->server_status; stmt->insert_id= mysql->insert_id; DBUG_RETURN(0); } @@ -2627,6 +2640,59 @@ error: return rc; } + +/* + Fetch statement row using server side cursor. + + SYNOPSIS + stmt_read_row_from_cursor() + + RETURN VALUE + 0 success + 1 error + MYSQL_NO_DATA end of data +*/ + +static int +stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row) +{ + if (stmt->data_cursor) + return stmt_read_row_buffered(stmt, row); + if (stmt->server_status & SERVER_STATUS_LAST_ROW_SENT) + stmt->server_status &= ~SERVER_STATUS_LAST_ROW_SENT; + else + { + MYSQL *mysql= stmt->mysql; + NET *net= &mysql->net; + MYSQL_DATA *result= &stmt->result; + char buff[4 /* statement id */ + + 4 /* number of rows to fetch */]; + + free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= NULL; + result->rows= 0; + /* Send row request to the server */ + int4store(buff, stmt->stmt_id); + int4store(buff + 4, 1); /* number of rows to fetch */ + if (cli_advanced_command(mysql, COM_FETCH, buff, sizeof(buff), + NullS, 0, 1)) + { + set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + return 1; + } + stmt->server_status= mysql->server_status; + if (cli_read_binary_rows(stmt)) + return 1; + stmt->server_status= mysql->server_status; + + stmt->data_cursor= result->data; + return stmt_read_row_buffered(stmt, row); + } + *row= 0; + return MYSQL_NO_DATA; +} + + /* Default read row function to not SIGSEGV in client in case of wrong sequence of API calls. @@ -2668,6 +2734,9 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, case STMT_ATTR_UPDATE_MAX_LENGTH: stmt->update_max_length= value ? *(const my_bool*) value : 0; break; + case STMT_ATTR_CURSOR_TYPE: + stmt->flags= value ? *(const unsigned long *) value : 0; + break; default: return TRUE; } @@ -2683,6 +2752,9 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, case STMT_ATTR_UPDATE_MAX_LENGTH: *(unsigned long *) value= stmt->update_max_length; break; + case STMT_ATTR_CURSOR_TYPE: + *(unsigned long *) value= stmt->flags; + break; default: return TRUE; } @@ -2786,9 +2858,28 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) stmt->state= MYSQL_STMT_EXECUTE_DONE; if (stmt->field_count) { - stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; - stmt->unbuffered_fetch_cancelled= FALSE; - stmt->read_row_func= stmt_read_row_unbuffered; + if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) + { + mysql->status= MYSQL_STATUS_READY; + stmt->read_row_func= stmt_read_row_from_cursor; + } + else if (stmt->flags & CURSOR_TYPE_READ_ONLY) + { + /* + This is a single-row result set, a result set with no rows, EXPLAIN, + SHOW VARIABLES, or some other command which either a) bypasses the + cursors framework in the server and writes rows directly to the + network or b) is more efficient if all (few) result set rows are + precached on client and server's resources are freed. + */ + DBUG_RETURN(mysql_stmt_store_result(stmt)); + } + else + { + stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; + stmt->unbuffered_fetch_cancelled= FALSE; + stmt->read_row_func= stmt_read_row_unbuffered; + } } DBUG_RETURN(0); } @@ -3102,8 +3193,11 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: param->store_param_func= store_param_str; /* For variable length types user must set either length or @@ -3353,6 +3447,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, { char *buffer= (char *)param->buffer; int err= 0; + char *endptr= value + length; /* This function should support all target buffer types: the rest @@ -3363,45 +3458,48 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, break; case MYSQL_TYPE_TINY: { - uchar data= (uchar) my_strntol(&my_charset_latin1, value, length, 10, - NULL, &err); - *buffer= data; + longlong data= my_strtoll10(value, &endptr, &err); + *param->error= (IS_TRUNCATED(data, param->is_unsigned, + INT_MIN8, INT_MAX8, UINT_MAX8) || err > 0); + *buffer= (uchar) data; break; } case MYSQL_TYPE_SHORT: { - short data= (short) my_strntol(&my_charset_latin1, value, length, 10, - NULL, &err); - shortstore(buffer, data); + longlong data= my_strtoll10(value, &endptr, &err); + *param->error= (IS_TRUNCATED(data, param->is_unsigned, + INT_MIN16, INT_MAX16, UINT_MAX16) || err > 0); + shortstore(buffer, (short) data); break; } case MYSQL_TYPE_LONG: { - int32 data= (int32)my_strntol(&my_charset_latin1, value, length, 10, - NULL, &err); - longstore(buffer, data); + longlong data= my_strtoll10(value, &endptr, &err); + *param->error= (IS_TRUNCATED(data, param->is_unsigned, + INT_MIN32, INT_MAX32, UINT_MAX32) || err > 0); + longstore(buffer, (int32) data); break; } case MYSQL_TYPE_LONGLONG: { - longlong data= my_strntoll(&my_charset_latin1, value, length, 10, - NULL, &err); + longlong data= my_strtoll10(value, &endptr, &err); + *param->error= param->is_unsigned ? err != 0 : + (err > 0 || (err == 0 && data < 0)); longlongstore(buffer, data); break; } case MYSQL_TYPE_FLOAT: { - char *end_not_used; - float data = (float) my_strntod(&my_charset_latin1, value, length, - &end_not_used, &err); - floatstore(buffer, data); + double data= my_strntod(&my_charset_latin1, value, length, &endptr, &err); + float fdata= (float) data; + *param->error= (fdata != data) | test(err); + floatstore(buffer, fdata); break; } case MYSQL_TYPE_DOUBLE: { - char *end_not_used; - double data= my_strntod(&my_charset_latin1, value, length, &end_not_used, - &err); + double data= my_strntod(&my_charset_latin1, value, length, &endptr, &err); + *param->error= test(err); doublestore(buffer, data); break; } @@ -3409,6 +3507,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, { MYSQL_TIME *tm= (MYSQL_TIME *)buffer; str_to_time(value, length, tm, &err); + *param->error= test(err); break; } case MYSQL_TYPE_DATE: @@ -3416,13 +3515,17 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, case MYSQL_TYPE_TIMESTAMP: { MYSQL_TIME *tm= (MYSQL_TIME *)buffer; - str_to_datetime(value, length, tm, 0, &err); + (void) str_to_datetime(value, length, tm, 0, &err); + *param->error= test(err) && (param->buffer_type == MYSQL_TYPE_DATE && + tm->time_type != MYSQL_TIMESTAMP_DATE); break; } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: default: { /* @@ -3443,6 +3546,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, copy_length= 0; if (copy_length < param->buffer_length) buffer[copy_length]= '\0'; + *param->error= copy_length > param->buffer_length; /* param->length will always contain length of entire column; number of copied bytes may be way different: @@ -3465,44 +3569,78 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, */ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, - longlong value) + longlong value, my_bool is_unsigned) { char *buffer= (char *)param->buffer; - uint field_is_unsigned= field->flags & UNSIGNED_FLAG; switch (param->buffer_type) { case MYSQL_TYPE_NULL: /* do nothing */ break; case MYSQL_TYPE_TINY: + *param->error= IS_TRUNCATED(value, param->is_unsigned, + INT_MIN8, INT_MAX8, UINT_MAX8); *(uchar *)param->buffer= (uchar) value; break; case MYSQL_TYPE_SHORT: - shortstore(buffer, value); + *param->error= IS_TRUNCATED(value, param->is_unsigned, + INT_MIN16, INT_MAX16, UINT_MAX16); + shortstore(buffer, (short) value); break; case MYSQL_TYPE_LONG: - longstore(buffer, value); + *param->error= IS_TRUNCATED(value, param->is_unsigned, + INT_MIN32, INT_MAX32, UINT_MAX32); + longstore(buffer, (int32) value); break; case MYSQL_TYPE_LONGLONG: longlongstore(buffer, value); + *param->error= param->is_unsigned != is_unsigned && value < 0; break; case MYSQL_TYPE_FLOAT: { - float data= field_is_unsigned ? (float) ulonglong2double(value) : - (float) value; + /* + We need to store data in the buffer before the truncation check to + workaround Intel FPU executive precision feature. + (See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for details) + AFAIU it does not guarantee to work. + */ + float data; + if (is_unsigned) + data= (float) ulonglong2double(value); + else + data= (float) value; floatstore(buffer, data); + *param->error= is_unsigned ? + ((ulonglong) value) != ((ulonglong) (*(float*) buffer)) : + ((longlong) value) != ((longlong) (*(float*) buffer)); break; } case MYSQL_TYPE_DOUBLE: { - double data= field_is_unsigned ? ulonglong2double(value) : - (double) value; + double data; + if (is_unsigned) + data= ulonglong2double(value); + else + data= (double)value; doublestore(buffer, data); + *param->error= is_unsigned ? + ((ulonglong) value) != ((ulonglong) (*(double*) buffer)) : + ((longlong) value) != ((longlong) (*(double*) buffer)); + break; + } + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + { + int error; + value= number_to_datetime(value, (MYSQL_TIME *) buffer, 1, &error); + *param->error= test(error); break; } default: { char buff[22]; /* Enough for longlong */ - char *end= longlong10_to_str(value, buff, field_is_unsigned ? 10: -10); + char *end= longlong10_to_str(value, buff, is_unsigned ? 10: -10); /* Resort to string conversion which supports all typecodes */ uint length= (uint) (end-buff); @@ -3519,7 +3657,6 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, } } - /* Convert double/float column to supplied buffer of any type. @@ -3536,29 +3673,73 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, double value, int width) { char *buffer= (char *)param->buffer; + double val64 = (value < 0 ? -floor(-value) : floor(value)); switch (param->buffer_type) { case MYSQL_TYPE_NULL: /* do nothing */ break; case MYSQL_TYPE_TINY: - *buffer= (uchar)value; + /* + We need to _store_ data in the buffer before the truncation check to + workaround Intel FPU executive precision feature. + (See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for details) + Sic: AFAIU it does not guarantee to work. + */ + if (param->is_unsigned) + *buffer= (uint8) value; + else + *buffer= (int8) value; + *param->error= val64 != (param->is_unsigned ? (double)((uint8) *buffer) : + (double)((int8) *buffer)); break; case MYSQL_TYPE_SHORT: - shortstore(buffer, (short)value); + if (param->is_unsigned) + { + ushort data= (ushort) value; + shortstore(buffer, data); + } + else + { + short data= (short) value; + shortstore(buffer, data); + } + *param->error= val64 != (param->is_unsigned ? (double) (*(ushort*) buffer): + (double) (*(short*) buffer)); break; case MYSQL_TYPE_LONG: - longstore(buffer, (long)value); - break; + if (param->is_unsigned) + { + uint32 data= (uint32) value; + longstore(buffer, data); + } + else + { + int32 data= (int32) value; + longstore(buffer, data); + } + *param->error= val64 != (param->is_unsigned ? (double) (*(uint32*) buffer): + (double) (*(int32*) buffer)); + break; case MYSQL_TYPE_LONGLONG: - { - longlong val= (longlong) value; - longlongstore(buffer, val); + if (param->is_unsigned) + { + ulonglong data= (ulonglong) value; + longlongstore(buffer, data); + } + else + { + longlong data= (longlong) value; + longlongstore(buffer, data); + } + *param->error= val64 != (param->is_unsigned ? + ulonglong2double(*(ulonglong*) buffer) : + (double) (*(longlong*) buffer)); break; - } case MYSQL_TYPE_FLOAT: { float data= (float) value; floatstore(buffer, data); + *param->error= (*(float*) buffer) != value; break; } case MYSQL_TYPE_DOUBLE: @@ -3612,18 +3793,47 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, */ static void fetch_datetime_with_conversion(MYSQL_BIND *param, + MYSQL_FIELD *field, MYSQL_TIME *time) { switch (param->buffer_type) { case MYSQL_TYPE_NULL: /* do nothing */ break; case MYSQL_TYPE_DATE: + *(MYSQL_TIME *)(param->buffer)= *time; + *param->error= time->time_type != MYSQL_TIMESTAMP_DATE; + break; case MYSQL_TYPE_TIME: + *(MYSQL_TIME *)(param->buffer)= *time; + *param->error= time->time_type != MYSQL_TIMESTAMP_TIME; + break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: - /* XXX: should we copy only relevant members here? */ *(MYSQL_TIME *)(param->buffer)= *time; + /* No error: time and date are compatible with datetime */ + break; + case MYSQL_TYPE_YEAR: + shortstore(param->buffer, time->year); + *param->error= 1; break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + { + ulonglong value= TIME_to_ulonglong(time); + fetch_float_with_conversion(param, field, + ulonglong2double(value), DBL_DIG); + break; + } + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + { + longlong value= (longlong) TIME_to_ulonglong(time); + fetch_long_with_conversion(param, field, value, TRUE); + break; + } default: { /* @@ -3669,7 +3879,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, /* sic: we need to cast to 'signed char' as 'char' may be unsigned */ longlong data= field_is_unsigned ? (longlong) value : (longlong) (signed char) value; - fetch_long_with_conversion(param, field, data); + fetch_long_with_conversion(param, field, data, 0); *row+= 1; break; } @@ -3679,7 +3889,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, short value= sint2korr(*row); longlong data= field_is_unsigned ? (longlong) (unsigned short) value : (longlong) value; - fetch_long_with_conversion(param, field, data); + fetch_long_with_conversion(param, field, data, 0); *row+= 2; break; } @@ -3689,14 +3899,15 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, int32 value= sint4korr(*row); longlong data= field_is_unsigned ? (longlong) (uint32) value : (longlong) value; - fetch_long_with_conversion(param, field, data); + fetch_long_with_conversion(param, field, data, 0); *row+= 4; break; } case MYSQL_TYPE_LONGLONG: { longlong value= (longlong)sint8korr(*row); - fetch_long_with_conversion(param, field, value); + fetch_long_with_conversion(param, field, value, + field->flags & UNSIGNED_FLAG); *row+= 8; break; } @@ -3721,7 +3932,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, MYSQL_TIME tm; read_binary_date(&tm, row); - fetch_datetime_with_conversion(param, &tm); + fetch_datetime_with_conversion(param, field, &tm); break; } case MYSQL_TYPE_TIME: @@ -3729,7 +3940,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, MYSQL_TIME tm; read_binary_time(&tm, row); - fetch_datetime_with_conversion(param, &tm); + fetch_datetime_with_conversion(param, field, &tm); break; } case MYSQL_TYPE_DATETIME: @@ -3738,7 +3949,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, MYSQL_TIME tm; read_binary_datetime(&tm, row); - fetch_datetime_with_conversion(param, &tm); + fetch_datetime_with_conversion(param, field, &tm); break; } default: @@ -3771,34 +3982,51 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, none */ -static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row) +static void fetch_result_tinyint(MYSQL_BIND *param, MYSQL_FIELD *field, + uchar **row) { - *(uchar *)param->buffer= **row; + my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG); + uchar data= **row; + *(uchar *)param->buffer= data; + *param->error= param->is_unsigned != field_is_unsigned && data > INT_MAX8; (*row)++; } -static void fetch_result_short(MYSQL_BIND *param, uchar **row) +static void fetch_result_short(MYSQL_BIND *param, MYSQL_FIELD *field, + uchar **row) { - short value = (short)sint2korr(*row); - shortstore(param->buffer, value); + my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG); + ushort data= (ushort) sint2korr(*row); + shortstore(param->buffer, data); + *param->error= param->is_unsigned != field_is_unsigned && data > INT_MAX16; *row+= 2; } -static void fetch_result_int32(MYSQL_BIND *param, uchar **row) +static void fetch_result_int32(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { - int32 value= (int32)sint4korr(*row); - longstore(param->buffer, value); + my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG); + uint32 data= (uint32) sint4korr(*row); + longstore(param->buffer, data); + *param->error= param->is_unsigned != field_is_unsigned && data > INT_MAX32; *row+= 4; } -static void fetch_result_int64(MYSQL_BIND *param, uchar **row) +static void fetch_result_int64(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { - longlong value= (longlong)sint8korr(*row); - longlongstore(param->buffer, value); + my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG); + ulonglong data= (ulonglong) sint8korr(*row); + *param->error= param->is_unsigned != field_is_unsigned && data > LONGLONG_MAX; + longlongstore(param->buffer, data); *row+= 8; } -static void fetch_result_float(MYSQL_BIND *param, uchar **row) +static void fetch_result_float(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { float value; float4get(value,*row); @@ -3806,7 +4034,9 @@ static void fetch_result_float(MYSQL_BIND *param, uchar **row) *row+= 4; } -static void fetch_result_double(MYSQL_BIND *param, uchar **row) +static void fetch_result_double(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { double value; float8get(value,*row); @@ -3814,34 +4044,45 @@ static void fetch_result_double(MYSQL_BIND *param, uchar **row) *row+= 8; } -static void fetch_result_time(MYSQL_BIND *param, uchar **row) +static void fetch_result_time(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; read_binary_time(tm, row); } -static void fetch_result_date(MYSQL_BIND *param, uchar **row) +static void fetch_result_date(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; read_binary_date(tm, row); } -static void fetch_result_datetime(MYSQL_BIND *param, uchar **row) +static void fetch_result_datetime(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; read_binary_datetime(tm, row); } -static void fetch_result_bin(MYSQL_BIND *param, uchar **row) +static void fetch_result_bin(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { ulong length= net_field_length(row); ulong copy_length= min(length, param->buffer_length); memcpy(param->buffer, (char *)*row, copy_length); *param->length= length; + *param->error= copy_length < length; *row+= length; } -static void fetch_result_str(MYSQL_BIND *param, uchar **row) +static void fetch_result_str(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) { ulong length= net_field_length(row); ulong copy_length= min(length, param->buffer_length); @@ -3850,6 +4091,7 @@ static void fetch_result_str(MYSQL_BIND *param, uchar **row) if (copy_length != param->buffer_length) ((uchar *)param->buffer)[copy_length]= '\0'; *param->length= length; /* return total length */ + *param->error= copy_length < length; *row+= length; } @@ -3891,6 +4133,220 @@ static void skip_result_string(MYSQL_BIND *param __attribute__((unused)), /* + Check that two field types are binary compatible i. e. + have equal representation in the binary protocol and + require client-side buffers of the same type. + + SYNOPSIS + is_binary_compatible() + type1 parameter type supplied by user + type2 field type, obtained from result set metadata + + RETURN + TRUE or FALSE +*/ + +static my_bool is_binary_compatible(enum enum_field_types type1, + enum enum_field_types type2) +{ + static const enum enum_field_types + range1[]= { MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR, MYSQL_TYPE_NULL }, + range2[]= { MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, MYSQL_TYPE_NULL }, + range3[]= { MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_NULL }, + range4[]= { MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB, + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB, + MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY, + MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NULL }; + static const enum enum_field_types + *range_list[]= { range1, range2, range3, range4 }, + **range_list_end= range_list + sizeof(range_list)/sizeof(*range_list); + const enum enum_field_types **range, *type; + + if (type1 == type2) + return TRUE; + for (range= range_list; range != range_list_end; ++range) + { + /* check that both type1 and type2 are in the same range */ + bool type1_found= FALSE, type2_found= FALSE; + for (type= *range; *type != MYSQL_TYPE_NULL; type++) + { + type1_found|= type1 == *type; + type2_found|= type2 == *type; + } + if (type1_found || type2_found) + return type1_found && type2_found; + } + return FALSE; +} + + +/* + Setup a fetch function for one column of a result set. + + SYNOPSIS + setup_one_fetch_function() + param output buffer descriptor + field column descriptor + + DESCRIPTION + When user binds result set buffers or when result set + metadata is changed, we need to setup fetch (and possibly + conversion) functions for all columns of the result set. + In addition to that here we set up skip_result function, used + to update result set metadata in case when + STMT_ATTR_UPDATE_MAX_LENGTH attribute is set. + Notice that while fetch_result is chosen depending on both + field->type and param->type, skip_result depends on field->type + only. + + RETURN + TRUE fetch function for this typecode was not found (typecode + is not supported by the client library) + FALSE success +*/ + +static my_bool setup_one_fetch_function(MYSQL_BIND *param, MYSQL_FIELD *field) +{ + /* Setup data copy functions for the different supported types */ + switch (param->buffer_type) { + case MYSQL_TYPE_NULL: /* for dummy binds */ + /* + It's not binary compatible with anything the server can return: + no need to setup fetch_result, as it'll be reset anyway + */ + *param->length= 0; + break; + case MYSQL_TYPE_TINY: + param->fetch_result= fetch_result_tinyint; + *param->length= 1; + break; + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + param->fetch_result= fetch_result_short; + *param->length= 2; + break; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + param->fetch_result= fetch_result_int32; + *param->length= 4; + break; + case MYSQL_TYPE_LONGLONG: + param->fetch_result= fetch_result_int64; + *param->length= 8; + break; + case MYSQL_TYPE_FLOAT: + param->fetch_result= fetch_result_float; + *param->length= 4; + break; + case MYSQL_TYPE_DOUBLE: + param->fetch_result= fetch_result_double; + *param->length= 8; + break; + case MYSQL_TYPE_TIME: + param->fetch_result= fetch_result_time; + *param->length= sizeof(MYSQL_TIME); + break; + case MYSQL_TYPE_DATE: + param->fetch_result= fetch_result_date; + *param->length= sizeof(MYSQL_TIME); + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + param->fetch_result= fetch_result_datetime; + *param->length= sizeof(MYSQL_TIME); + break; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BIT: + DBUG_ASSERT(param->buffer_length != 0); + param->fetch_result= fetch_result_bin; + break; + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + DBUG_ASSERT(param->buffer_length != 0); + param->fetch_result= fetch_result_str; + break; + default: + return TRUE; + } + if (! is_binary_compatible(param->buffer_type, field->type)) + param->fetch_result= fetch_result_with_conversion; + + /* Setup skip_result functions (to calculate max_length) */ + param->skip_result= skip_result_fixed; + switch (field->type) { + case MYSQL_TYPE_NULL: /* for dummy binds */ + param->pack_length= 0; + field->max_length= 0; + break; + case MYSQL_TYPE_TINY: + param->pack_length= 1; + field->max_length= 4; /* as in '-127' */ + break; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + param->pack_length= 2; + field->max_length= 6; /* as in '-32767' */ + break; + case MYSQL_TYPE_INT24: + field->max_length= 9; /* as in '16777216' or in '-8388607' */ + param->pack_length= 4; + break; + case MYSQL_TYPE_LONG: + field->max_length= 11; /* '-2147483647' */ + param->pack_length= 4; + break; + case MYSQL_TYPE_LONGLONG: + field->max_length= 21; /* '18446744073709551616' */ + param->pack_length= 8; + break; + case MYSQL_TYPE_FLOAT: + param->pack_length= 4; + field->max_length= MAX_DOUBLE_STRING_REP_LENGTH; + break; + case MYSQL_TYPE_DOUBLE: + param->pack_length= 8; + field->max_length= MAX_DOUBLE_STRING_REP_LENGTH; + break; + case MYSQL_TYPE_TIME: + field->max_length= 15; /* 19:23:48.123456 */ + param->skip_result= skip_result_with_length; + case MYSQL_TYPE_DATE: + field->max_length= 10; /* 2003-11-11 */ + param->skip_result= skip_result_with_length; + break; + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + param->skip_result= skip_result_with_length; + field->max_length= MAX_DATE_STRING_REP_LENGTH; + break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_BIT: + param->skip_result= skip_result_string; + break; + default: + return TRUE; + } + return FALSE; +} + + +/* Setup the bind buffers for resultset processing */ @@ -3929,142 +4385,30 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) This is to make the execute code easier */ if (!param->is_null) - param->is_null= ¶m->internal_is_null; + param->is_null= ¶m->is_null_value; if (!param->length) - param->length= ¶m->internal_length; + param->length= ¶m->length_value; + + if (!param->error) + param->error= ¶m->error_value; param->param_number= param_count++; param->offset= 0; - /* Setup data copy functions for the different supported types */ - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: /* for dummy binds */ - *param->length= 0; - break; - case MYSQL_TYPE_TINY: - param->fetch_result= fetch_result_tinyint; - *param->length= 1; - break; - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_YEAR: - param->fetch_result= fetch_result_short; - *param->length= 2; - break; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - param->fetch_result= fetch_result_int32; - *param->length= 4; - break; - case MYSQL_TYPE_LONGLONG: - param->fetch_result= fetch_result_int64; - *param->length= 8; - break; - case MYSQL_TYPE_FLOAT: - param->fetch_result= fetch_result_float; - *param->length= 4; - break; - case MYSQL_TYPE_DOUBLE: - param->fetch_result= fetch_result_double; - *param->length= 8; - break; - case MYSQL_TYPE_TIME: - param->fetch_result= fetch_result_time; - *param->length= sizeof(MYSQL_TIME); - break; - case MYSQL_TYPE_DATE: - param->fetch_result= fetch_result_date; - *param->length= sizeof(MYSQL_TIME); - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - param->fetch_result= fetch_result_datetime; - *param->length= sizeof(MYSQL_TIME); - break; - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - DBUG_ASSERT(param->buffer_length != 0); - param->fetch_result= fetch_result_bin; - break; - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - DBUG_ASSERT(param->buffer_length != 0); - param->fetch_result= fetch_result_str; - break; - default: - strmov(stmt->sqlstate, unknown_sqlstate); - sprintf(stmt->last_error, - ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), - param->buffer_type, param_count); - DBUG_RETURN(1); - } - - /* Setup skip_result functions (to calculate max_length) */ - param->skip_result= skip_result_fixed; - switch (field->type) { - case MYSQL_TYPE_NULL: /* for dummy binds */ - param->pack_length= 0; - field->max_length= 0; - break; - case MYSQL_TYPE_TINY: - param->pack_length= 1; - field->max_length= 4; /* as in '-127' */ - break; - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_SHORT: - param->pack_length= 2; - field->max_length= 6; /* as in '-32767' */ - break; - case MYSQL_TYPE_INT24: - field->max_length= 9; /* as in '16777216' or in '-8388607' */ - param->pack_length= 4; - break; - case MYSQL_TYPE_LONG: - field->max_length= 11; /* '-2147483647' */ - param->pack_length= 4; - break; - case MYSQL_TYPE_LONGLONG: - field->max_length= 21; /* '18446744073709551616' */ - param->pack_length= 8; - break; - case MYSQL_TYPE_FLOAT: - param->pack_length= 4; - field->max_length= MAX_DOUBLE_STRING_REP_LENGTH; - break; - case MYSQL_TYPE_DOUBLE: - param->pack_length= 8; - field->max_length= MAX_DOUBLE_STRING_REP_LENGTH; - break; - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - param->skip_result= skip_result_with_length; - field->max_length= MAX_DATE_STRING_REP_LENGTH; - break; - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_GEOMETRY: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - param->skip_result= skip_result_string; - break; - default: + if (setup_one_fetch_function(param, field)) + { strmov(stmt->sqlstate, unknown_sqlstate); sprintf(stmt->last_error, - ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), - field->type, param_count); + ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), + field->type, param_count); DBUG_RETURN(1); } } - stmt->bind_result_done= TRUE; + stmt->bind_result_done= BIND_RESULT_DONE; + if (stmt->mysql->options.report_data_truncation) + stmt->bind_result_done|= REPORT_DATA_TRUNCATION; + DBUG_RETURN(0); } @@ -4078,6 +4422,7 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) MYSQL_BIND *bind, *end; MYSQL_FIELD *field; uchar *null_ptr, bit; + int truncation_count= 0; /* Precondition: if stmt->field_count is zero or row is NULL, read_row_* function must return no data. @@ -4100,26 +4445,25 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) bind < end ; bind++, field++) { + *bind->error= 0; if (*null_ptr & bit) { /* - We should set both inter_buffer and is_null to be able to see + We should set both row_ptr and is_null to be able to see nulls in mysql_stmt_fetch_column. This is because is_null may point to user data which can be overwritten between mysql_stmt_fetch and mysql_stmt_fetch_column, and in this case nullness of column will be lost. See mysql_stmt_fetch_column for details. */ - bind->inter_buffer= NULL; + bind->row_ptr= NULL; *bind->is_null= 1; } else { *bind->is_null= 0; - bind->inter_buffer= row; - if (field->type == bind->buffer_type) - (*bind->fetch_result)(bind, &row); - else - fetch_result_with_conversion(bind, field, &row); + bind->row_ptr= row; + (*bind->fetch_result)(bind, field, &row); + truncation_count+= *bind->error; } if (!((bit<<=1) & 255)) { @@ -4127,6 +4471,8 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) null_ptr++; } } + if (truncation_count && (stmt->bind_result_done & REPORT_DATA_TRUNCATION)) + return MYSQL_DATA_TRUNCATED; return 0; } @@ -4153,7 +4499,7 @@ int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) DBUG_ENTER("mysql_stmt_fetch"); if ((rc= (*stmt->read_row_func)(stmt, &row)) || - (rc= stmt_fetch_row(stmt, row))) + ((rc= stmt_fetch_row(stmt, row)) && rc != MYSQL_DATA_TRUNCATED)) { stmt->state= MYSQL_STMT_PREPARE_DONE; /* XXX: this is buggy */ stmt->read_row_func= stmt_read_row_no_data; @@ -4200,17 +4546,20 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, DBUG_RETURN(1); } - if (param->inter_buffer) + if (!bind->error) + bind->error= &bind->error_value; + *bind->error= 0; + if (param->row_ptr) { MYSQL_FIELD *field= stmt->fields+column; - uchar *row= param->inter_buffer; + uchar *row= param->row_ptr; bind->offset= offset; if (bind->is_null) *bind->is_null= 0; if (bind->length) /* Set the length if non char/binary types */ *bind->length= *param->length; else - bind->length= ¶m->internal_length; /* Needed for fetch_result() */ + bind->length= ¶m->length_value; /* Needed for fetch_result() */ fetch_result_with_conversion(bind, field, &row); } else diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index c5579e9ec2b..414ac4afd85 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -146,4 +146,9 @@ EXPORTS mysql_rpl_query_type mysql_slave_query mysql_embedded + mysql_server_init + mysql_server_end get_defaults_files + get_charset_by_csname + get_charsets_dir + charsets_dir |