summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/config-win.h1
-rw-r--r--include/my_global.h9
-rw-r--r--include/mysql.h3
-rw-r--r--include/mysql_com.h11
-rw-r--r--libmysql/libmysql.c59
-rw-r--r--sql/sql_prepare.cc20
-rw-r--r--tests/client_test.c163
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= &param->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);