diff options
-rw-r--r-- | include/config-win.h | 1 | ||||
-rw-r--r-- | include/my_global.h | 9 | ||||
-rw-r--r-- | include/mysql.h | 3 | ||||
-rw-r--r-- | include/mysql_com.h | 11 | ||||
-rw-r--r-- | libmysql/libmysql.c | 59 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 20 | ||||
-rw-r--r-- | tests/client_test.c | 163 |
7 files changed, 220 insertions, 46 deletions
diff --git a/include/config-win.h b/include/config-win.h index 518f445861a..0298c9012ce 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -252,6 +252,7 @@ inline double ulonglong2double(ulonglong value) #define doublestore(T,V) { *((long *) T) = *((long*) &V); \ *(((long *) T)+1) = *(((long*) &V)+1); } #define float4get(V,M) { *((long *) &(V)) = *((long*) (M)); } +#define floatstore(T,V) memcpy((byte*)(T), (byte*)(&V), sizeof(float)) #define float8get(V,M) doubleget((V),(M)) #define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float)) #define float8store(V,M) doublestore((V),(M)) diff --git a/include/my_global.h b/include/my_global.h index 6a5b7663c66..7540897e6f4 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -964,10 +964,15 @@ do { doubleget_union _tmp; \ #define float4get(V,M) do { *((long *) &(V)) = *((long*) (M)); } while(0) #define float8get(V,M) doubleget((V),(M)) #define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float)) +#define floatstore(T,V) memcpy((byte*)(T), (byte*)(&V), sizeof(float)) #define float8store(V,M) doublestore((V),(M)) #endif /* __i386__ */ #ifndef sint2korr +/* + We're here if it's not a IA-32 architecture (Win32 and UNIX IA-32 defines + were done before) +*/ #define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\ ((int16) ((int16) (A)[1]) << 8)) #define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ @@ -1121,6 +1126,7 @@ do { doubleget_union _tmp; \ *((T)+1)=(((A) >> 16));\ *((T)+0)=(((A) >> 24)); } while(0) +#define floatstore(T,V) memcpy_fixed((byte*)(T), (byte*)(&V), sizeof(float)) #define doubleget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(double)) #define doublestore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(double)) #define longlongget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(ulonglong)) @@ -1134,6 +1140,9 @@ do { doubleget_union _tmp; \ #define ulongget(V,M) do { V = uint4korr(M); } while(0) #define shortstore(T,V) int2store(T,V) #define longstore(T,V) int4store(T,V) +#ifndef floatstore +#define floatstore(T,V) memcpy_fixed((byte*)(T), (byte*)(&V), sizeof(float)) +#endif #ifndef doubleget #define doubleget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(double)) #define doublestore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(double)) diff --git a/include/mysql.h b/include/mysql.h index 852d633facf..b8476682bb3 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -547,9 +547,8 @@ typedef struct st_mysql_bind unsigned long offset; /* offset position for char/binary fetch */ unsigned long internal_length; /* Used if length is 0 */ unsigned int param_number; /* For null count and error messages */ + my_bool is_unsigned; /* set if integer type is unsigned */ my_bool long_data_used; /* If used with mysql_send_long_data */ - my_bool binary_data; /* data buffer is binary */ - my_bool null_field; /* NULL data cache flag */ my_bool internal_is_null; /* Used if is_null is 0 */ void (*store_param_func)(NET *net, struct st_mysql_bind *param); void (*fetch_result)(struct st_mysql_bind *, unsigned char **row); diff --git a/include/mysql_com.h b/include/mysql_com.h index 578ef964d8d..ada2bd1f679 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -223,17 +223,6 @@ enum enum_field_types { MYSQL_TYPE_DECIMAL, 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 - /* options for mysql_set_option */ enum enum_mysql_set_option { diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 4447ece308e..eefb50fc830 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -2131,17 +2131,6 @@ mysql_stmt_param_metadata(MYSQL_STMT *stmt) Prepare-execute, and param handling *********************************************************************/ -/* - Store the buffer type -*/ - -static void store_param_type(NET *net, uint type) -{ - int2store(net->write_pos, type); - net->write_pos+=2; -} - - /**************************************************************************** Functions to store parameter data from a prepared statement. @@ -2389,7 +2378,11 @@ int cli_stmt_execute(MYSQL_STMT *stmt) that is sent to the server. */ for (param= stmt->params; param < param_end ; param++) - store_param_type(net, (uint) param->buffer_type); + { + uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0); + int2store(net->write_pos, typecode); + net->write_pos+= 2; + } } for (param= stmt->params; param < param_end; param++) @@ -3217,28 +3210,28 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row) static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row) { - *param->buffer= (uchar) **row; + *param->buffer= **row; (*row)++; } static void fetch_result_short(MYSQL_BIND *param, uchar **row) { short value = (short)sint2korr(*row); - int2store(param->buffer, value); + shortstore(param->buffer, value); *row+= 2; } static void fetch_result_int32(MYSQL_BIND *param, uchar **row) { int32 value= (int32)sint4korr(*row); - int4store(param->buffer, value); + longstore(param->buffer, value); *row+= 4; } static void fetch_result_int64(MYSQL_BIND *param, uchar **row) { longlong value= (longlong)sint8korr(*row); - int8store(param->buffer, value); + longlongstore(param->buffer, value); *row+= 8; } @@ -3246,7 +3239,7 @@ static void fetch_result_float(MYSQL_BIND *param, uchar **row) { float value; float4get(value,*row); - float4store(param->buffer, value); + floatstore(param->buffer, value); *row+= 4; } @@ -3254,7 +3247,7 @@ static void fetch_result_double(MYSQL_BIND *param, uchar **row) { double value; float8get(value,*row); - float8store(param->buffer, value); + doublestore(param->buffer, value); *row+= 8; } @@ -3325,8 +3318,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) stmt->bind was initialized in mysql_stmt_prepare */ - memcpy((char*) stmt->bind, (char*) bind, - sizeof(MYSQL_BIND)*bind_count); + memcpy((char*) stmt->bind, (char*) bind, sizeof(MYSQL_BIND) * bind_count); for (param= stmt->bind, end= param+bind_count; param < end ; param++) { @@ -3443,10 +3435,20 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) bind++, field++) { if (*null_ptr & bit) - *bind->is_null= bind->null_field= 1; + { + /* + We should set both inter_buffer 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->is_null= 1; + } else { - *bind->is_null= bind->null_field= 0; + *bind->is_null= 0; bind->inter_buffer= row; if (field->type == bind->buffer_type) (*bind->fetch_result)(bind, &row); @@ -3530,14 +3532,8 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, DBUG_RETURN(1); } - - if (param->null_field) + if (param->inter_buffer) { - if (bind->is_null) - *bind->is_null= 1; - } - else - { MYSQL_FIELD *field= stmt->fields+column; uchar *row= param->inter_buffer; bind->offset= offset; @@ -3549,6 +3545,11 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, bind->length= ¶m->internal_length; /* Needed for fetch_result() */ fetch_results(bind, field, &row); } + else + { + if (bind->is_null) + *bind->is_null= 1; + } DBUG_RETURN(0); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 69c5be69210..ead74c2a7bc 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -246,7 +246,9 @@ void set_param_tiny(Item_param *param, uchar **pos, ulong len) if (len < 1) return; #endif - param->set_int((longlong)(**pos)); + int8 value= (int8) **pos; + param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) : + (longlong) value); *pos+= 1; } @@ -256,7 +258,9 @@ void set_param_short(Item_param *param, uchar **pos, ulong len) if (len < 2) return; #endif - param->set_int((longlong)sint2korr(*pos)); + int16 value= sint2korr(*pos); + param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) : + (longlong) value); *pos+= 2; } @@ -266,7 +270,9 @@ void set_param_int32(Item_param *param, uchar **pos, ulong len) if (len < 4) return; #endif - param->set_int((longlong)sint4korr(*pos)); + int32 value= sint4korr(*pos); + param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) : + (longlong) value); *pos+= 4; } @@ -535,10 +541,16 @@ static bool setup_conversion_functions(Prepared_statement *stmt, Item_param **end= it + stmt->param_count; for (; it < end; ++it) { + ushort typecode; + const uint signed_bit= 1 << 15; + if (read_pos >= data_end) DBUG_RETURN(1); - setup_one_conversion_function(*it, *read_pos); + + typecode= sint2korr(read_pos); read_pos+= 2; + (**it).unsigned_flag= test(typecode & signed_bit); + setup_one_conversion_function(*it, (uchar) (typecode & ~signed_bit)); } } *data= read_pos; diff --git a/tests/client_test.c b/tests/client_test.c index 000b55a202e..99dbeb2afa9 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -9031,6 +9031,168 @@ static void test_xjoin() myquery(rc); } +static void test_bug3035() +{ + MYSQL_STMT *stmt; + int rc; + + MYSQL_BIND bind_array[8]; + int8 int8_val; + uint8 uint8_val; + int16 int16_val; + uint16 uint16_val; + int32 int32_val; + uint32 uint32_val; + longlong int64_val; + ulonglong uint64_val; + + /* mins and maxes */ + const int8 int8_min= -128; + const int8 int8_max= 127; + const uint8 uint8_min= 0; + const uint8 uint8_max= 255; + + const int16 int16_min= -32768; + const int16 int16_max= 32767; + const uint16 uint16_min= 0; + const uint16 uint16_max= 65535; + + const int32 int32_max= 2147483647L; + const int32 int32_min= -int32_max - 1; + const uint32 uint32_min= 0; + const uint32 uint32_max= 4294967295U; + + /* it might not work okay everyplace */ + const longlong int64_max= 9223372036854775807LL; + const longlong int64_min= -int64_max - 1; + + const ulonglong uint64_min= 0U; + const ulonglong uint64_max= 18446744073709551615ULL; + + const char *stmt_text; + + myheader("test_bug3035"); + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text)); + myquery(rc); + + stmt_text= "CREATE TABLE t1 (i8 TINYINT, ui8 TINYINT UNSIGNED, " + "i16 SMALLINT, ui16 SMALLINT UNSIGNED, " + "i32 INT, ui32 INT UNSIGNED, " + "i64 BIGINT, ui64 BIGINT UNSIGNED, " + "id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT)"; + rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text)); + myquery(rc); + + bzero(bind_array, sizeof(bind_array)); + + bind_array[0].buffer_type= MYSQL_TYPE_TINY; + bind_array[0].buffer= (char*) &int8_val; + + bind_array[1].buffer_type= MYSQL_TYPE_TINY; + bind_array[1].buffer= (char*) &uint8_val; + bind_array[1].is_unsigned= 1; + + bind_array[2].buffer_type= MYSQL_TYPE_SHORT; + bind_array[2].buffer= (char*) &int16_val; + + bind_array[3].buffer_type= MYSQL_TYPE_SHORT; + bind_array[3].buffer= (char*) &uint16_val; + bind_array[3].is_unsigned= 1; + + bind_array[4].buffer_type= MYSQL_TYPE_LONG; + bind_array[4].buffer= (char*) &int32_val; + + bind_array[5].buffer_type= MYSQL_TYPE_LONG; + bind_array[5].buffer= (char*) &uint32_val; + bind_array[5].is_unsigned= 1; + + bind_array[6].buffer_type= MYSQL_TYPE_LONGLONG; + bind_array[6].buffer= (char*) &int64_val; + + bind_array[7].buffer_type= MYSQL_TYPE_LONGLONG; + bind_array[7].buffer= (char*) &uint64_val; + bind_array[7].is_unsigned= 1; + + stmt= mysql_stmt_init(mysql); + + mystmt_init(stmt); + + stmt_text= "INSERT INTO t1 (i8, ui8, i16, ui16, i32, ui32, i64, ui64) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + mystmt(stmt, rc); + + mysql_stmt_bind_param(stmt, bind_array); + + int8_val= int8_min; + uint8_val= uint8_min; + int16_val= int16_min; + uint16_val= uint16_min; + int32_val= int32_min; + uint32_val= uint32_min; + int64_val= int64_min; + uint64_val= uint64_min; + + rc= mysql_stmt_execute(stmt); + mystmt(stmt, rc); + + int8_val= int8_max; + uint8_val= uint8_max; + int16_val= int16_max; + uint16_val= uint16_max; + int32_val= int32_max; + uint32_val= uint32_max; + int64_val= int64_max; + uint64_val= uint64_max; + + mysql_stmt_execute(stmt); + mystmt(stmt, rc); + + stmt_text= "SELECT i8, ui8, i16, ui16, i32, ui32, i64, ui64 " + "FROM t1 ORDER BY id ASC"; + + mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + mystmt(stmt, rc); + + mysql_stmt_execute(stmt); + mystmt(stmt, rc); + + mysql_stmt_bind_result(stmt, bind_array); + + rc= mysql_stmt_fetch(stmt); + mystmt(stmt, rc); + + assert(int8_val == int8_min); + assert(uint8_val == uint8_min); + assert(int16_val == int16_min); + assert(uint16_val == uint16_min); + assert(int32_val == int32_min); + assert(uint32_val == uint32_min); + assert(int64_val == int64_min); + assert(uint64_val == uint64_min); + + rc= mysql_stmt_fetch(stmt); + mystmt(stmt, rc); + + assert(int8_val == int8_max); + assert(uint8_val == uint8_max); + assert(int16_val == int16_max); + assert(uint16_val == uint16_max); + assert(int32_val == int32_max); + assert(uint32_val == uint32_max); + assert(int64_val == int64_max); + assert(uint64_val == uint64_max); + + rc= mysql_stmt_fetch(stmt); + assert(rc == MYSQL_NO_DATA); + + mysql_stmt_close(stmt); + + stmt_text= "DROP TABLE t1"; + mysql_real_query(mysql, stmt_text, strlen(stmt_text)); +} /* Read and parse arguments and MySQL options from my.cnf @@ -9303,6 +9465,7 @@ int main(int argc, char **argv) test_bind_nagative(); /* bind negative to unsigned BUG#3223 */ test_derived(); /* derived table with parameter BUG#3020 */ test_xjoin(); /* complex join test */ + test_bug3035(); /* inserts of INT32_MAX/UINT32_MAX */ end_time= time((time_t *)0); total_time+= difftime(end_time, start_time); |