Commit aec85225 authored by konstantin@mysql.com's avatar konstantin@mysql.com

Fix for Bug#3035 "Prepared statement integer inserts": now unsigned

flag is sent to server with placeholder types.
There were no need to extend the protocol as one additional byte
was reserved for placeholder code, when placeholder code is in range 0-255.
So this byte is now used for flags. Post-review fixes added.
parent 32d0b695
......@@ -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))
......
......@@ -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))
......
......@@ -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);
......
......@@ -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
{
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment