Commit 2c30359a authored by dlenev@brandersnatch.localdomain's avatar dlenev@brandersnatch.localdomain

Manual merge of bugfix for Bug #1664.

parents af25c817 c7fbcbca
......@@ -605,12 +605,19 @@ Item_param::Item_param(unsigned position) :
set_param_func(default_set_param_func)
{
name= (char*) "?";
/*
Since we can't say whenever this item can be NULL or cannot be NULL
before mysql_stmt_execute(), so we assuming that it can be NULL until
value is set.
*/
maybe_null= 1;
}
void Item_param::set_null()
{
DBUG_ENTER("Item_param::set_null");
maybe_null= null_value= value_is_set= 1;
/* These are cleared after each execution by reset() method */
null_value= value_is_set= 1;
DBUG_VOID_RETURN;
}
......@@ -620,6 +627,7 @@ void Item_param::set_int(longlong i)
int_value= (longlong)i;
item_type= INT_ITEM;
value_is_set= 1;
maybe_null= 0;
DBUG_PRINT("info", ("integer: %lld", int_value));
DBUG_VOID_RETURN;
}
......@@ -630,6 +638,7 @@ void Item_param::set_double(double value)
real_value=value;
item_type= REAL_ITEM;
value_is_set= 1;
maybe_null= 0;
DBUG_PRINT("info", ("double: %lg", real_value));
DBUG_VOID_RETURN;
}
......@@ -641,6 +650,7 @@ void Item_param::set_value(const char *str, uint length)
str_value.copy(str,length,default_charset());
item_type= STRING_ITEM;
value_is_set= 1;
maybe_null= 0;
DBUG_PRINT("info", ("string: %s", str_value.ptr()));
DBUG_VOID_RETURN;
}
......@@ -665,6 +675,7 @@ void Item_param::set_time(TIME *tm, timestamp_type type)
item_is_time= TRUE;
item_type= STRING_ITEM;
value_is_set= 1;
maybe_null= 0;
}
......@@ -673,6 +684,27 @@ void Item_param::set_longdata(const char *str, ulong length)
str_value.append(str,length);
long_data_supplied= 1;
value_is_set= 1;
maybe_null= 0;
}
/*
Resets parameter after execution.
SYNOPSIS
Item_param::reset()
NOTES
We clear null_value here instead of setting it in set_* methods,
because we want more easily handle case for long data.
*/
void Item_param::reset()
{
str_value.set("", 0, &my_charset_bin);
value_is_set= long_data_supplied= 0;
maybe_null= 1;
null_value= 0;
}
......
......@@ -419,7 +419,7 @@ class Item_param :public Item
void set_long_end();
void set_time(TIME *tm, timestamp_type type);
bool get_time(TIME *tm);
void reset() {}
void reset();
/*
Assign placeholder value from bind data.
Note, that 'len' has different semantics in embedded library (as we
......
......@@ -91,7 +91,6 @@ class Prepared_statement: public Statement
uint last_errno;
char last_error[MYSQL_ERRMSG_SIZE];
bool get_longdata_error;
bool long_data_used;
bool log_full_query;
#ifndef EMBEDDED_LIBRARY
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
......@@ -471,12 +470,11 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
{
if (is_param_null(null_array, it - begin))
{
param->maybe_null= param->null_value= param->value_is_set= 1;
param->set_null();
res= &my_null_string;
}
else
{
param->maybe_null= param->null_value= 0;
if (read_pos >= data_end)
DBUG_RETURN(1);
param->set_param_func(param, &read_pos, data_end - read_pos);
......@@ -509,10 +507,9 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array,
if (!param->long_data_supplied)
{
if (is_param_null(null_array, it - begin))
param->maybe_null= param->null_value= param->value_is_set= 1;
param->set_null();
else
{
param->maybe_null= param->null_value= 0;
if (read_pos >= data_end)
DBUG_RETURN(1);
param->set_param_func(param, &read_pos, data_end - read_pos);
......@@ -574,11 +571,10 @@ static bool emb_insert_params(Prepared_statement *stmt)
if (!param->long_data_supplied)
{
if (*client_param->is_null)
param->maybe_null= param->null_value= 1;
param->set_null();
else
{
uchar *buff= (uchar*)client_param->buffer;
param->maybe_null= param->null_value= 0;
param->set_param_func(param, &buff,
client_param->length ?
*client_param->length :
......@@ -616,13 +612,12 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
{
if (*client_param->is_null)
{
param->maybe_null= param->null_value= 1;
param->set_null();
res= &my_null_string;
}
else
{
uchar *buff= (uchar*)client_param->buffer;
param->maybe_null= param->null_value= 0;
param->set_param_func(param, &buff,
client_param->length ?
*client_param->length :
......@@ -1451,6 +1446,24 @@ static void reset_stmt_for_execute(Prepared_statement *stmt)
}
}
/*
Clears parameters from data left from previous execution or long data
SYNOPSIS
reset_stmt_params()
stmt - prepared statement for which parameters should be reset
*/
static void reset_stmt_params(Prepared_statement *stmt)
{
Item_param **item= stmt->param_array;
Item_param **end= item + stmt->param_count;
for (;item < end ; ++item)
(**item).reset();
}
/*
Executes previously prepared query.
If there is any parameters, then replace markers with the data supplied
......@@ -1524,11 +1537,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
cleanup_items(stmt->free_list);
reset_stmt_params(stmt);
close_thread_tables(thd); // to close derived tables
thd->set_statement(&thd->stmt_backup);
DBUG_VOID_RETURN;
set_params_data_err:
reset_stmt_params(stmt);
thd->set_statement(&thd->stmt_backup);
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
send_error(thd);
......@@ -1564,15 +1579,12 @@ void mysql_stmt_reset(THD *thd, char *packet)
stmt->get_longdata_error= 0;
/* Free long data if used */
if (stmt->long_data_used)
{
Item_param **item= stmt->param_array;
Item_param **end= item + stmt->param_count;
stmt->long_data_used= 0;
for (; item < end ; item++)
(**item).reset();
}
/*
Clear parameters from data which could be set by
mysql_stmt_send_long_data() call.
*/
reset_stmt_params(stmt);
DBUG_VOID_RETURN;
}
......@@ -1658,7 +1670,6 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
#else
param->set_longdata(thd->extra_data, thd->extra_length);
#endif
stmt->long_data_used= 1;
DBUG_VOID_RETURN;
}
......@@ -1670,7 +1681,6 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
param_count(0),
last_errno(0),
get_longdata_error(0),
long_data_used(0),
log_full_query(0)
{
*last_error= '\0';
......
......@@ -9432,6 +9432,152 @@ select col1 FROM t1 where col1=2");
}
/*
This tests for various mysql_send_long_data bugs described in #1664
*/
static void test_bug1664()
{
MYSQL_STMT *stmt;
int rc, int_data;
const char *data;
const char *str_data= "Simple string";
MYSQL_BIND bind[2];
const char *query= "INSERT INTO test_long_data(col2, col1) VALUES(?,?)";
myheader("test_bug1664");
rc= mysql_query(mysql,"DROP TABLE IF EXISTS test_long_data");
myquery(rc);
rc= mysql_query(mysql,"CREATE TABLE test_long_data(col1 int, col2 long varchar)");
myquery(rc);
stmt= mysql_stmt_init(mysql);
mystmt_init(stmt);
rc= mysql_stmt_prepare(stmt, query, strlen(query));
mystmt(stmt, rc);
verify_param_count(stmt, 2);
bzero(&bind, sizeof(bind));
bind[0].buffer_type= FIELD_TYPE_STRING;
bind[0].buffer= (char *)str_data;
bind[0].buffer_length= strlen(str_data);
bind[1].buffer= (char *)&int_data;
bind[1].buffer_type= FIELD_TYPE_LONG;
rc= mysql_stmt_bind_param(stmt,bind);
mystmt(stmt, rc);
int_data= 1;
/*
Let us supply empty long_data. This should work and should
not break following execution.
*/
data= "";
rc= mysql_stmt_send_long_data(stmt,0,data,strlen(data));
mystmt(stmt,rc);
rc= mysql_stmt_execute(stmt);
fprintf(stdout," mysql_execute() returned %d\n",rc);
mystmt(stmt,rc);
verify_col_data("test_long_data","col1","1");
verify_col_data("test_long_data","col2","");
rc= mysql_query(mysql,"DELETE FROM test_long_data");
myquery(rc);
/* This should pass OK */
data= (char *)"Data";
rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
mystmt(stmt, rc);
rc= mysql_stmt_execute(stmt);
fprintf(stdout," mysql_execute() returned %d\n",rc);
mystmt(stmt, rc);
verify_col_data("test_long_data", "col1", "1");
verify_col_data("test_long_data", "col2", "Data");
/* clean up */
rc= mysql_query(mysql, "DELETE FROM test_long_data");
myquery(rc);
/*
Now we are changing int parameter and don't do anything
with first parameter. Second mysql_execute() should run
OK treating this first parameter as string parameter.
*/
int_data= 2;
/* execute */
rc = mysql_stmt_execute(stmt);
fprintf(stdout, " mysql_execute() returned %d\n", rc);
mystmt(stmt, rc);
verify_col_data("test_long_data", "col1", "2");
verify_col_data("test_long_data", "col2", str_data);
/* clean up */
rc= mysql_query(mysql, "DELETE FROM test_long_data");
myquery(rc);
/*
Now we are sending other long data. It should not be
concatened to previous.
*/
data= (char *)"SomeOtherData";
rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
mystmt(stmt, rc);
rc= mysql_stmt_execute(stmt);
fprintf(stdout, " mysql_execute() returned %d\n", rc);
mystmt(stmt, rc);
verify_col_data("test_long_data", "col1", "2");
verify_col_data("test_long_data", "col2", "SomeOtherData");
mysql_stmt_close(stmt);
/* clean up */
rc= mysql_query(mysql, "DELETE FROM test_long_data");
myquery(rc);
/* Now let us test how mysql_stmt_reset works. */
stmt= mysql_stmt_init(mysql);
mystmt_init(stmt);
rc= mysql_stmt_prepare(stmt, query, strlen(query));
mystmt(stmt, rc);
rc= mysql_bind_param(stmt, bind);
mystmt(stmt, rc);
data= (char *)"SomeData";
rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
mystmt(stmt, rc);
rc= mysql_stmt_reset(stmt);
mystmt(stmt, rc);
rc= mysql_stmt_execute(stmt);
fprintf(stdout," mysql_execute() returned %d\n",rc);
mystmt(stmt, rc);
verify_col_data("test_long_data", "col1", "2");
verify_col_data("test_long_data", "col2", str_data);
mysql_stmt_close(stmt);
/* Final clean up */
rc= mysql_query(mysql, "DROP TABLE test_long_data");
myquery(rc);
}
/*
Read and parse arguments and MySQL options from my.cnf
*/
......@@ -9712,6 +9858,8 @@ int main(int argc, char **argv)
test_xjoin(); /* complex join test */
test_bug3035(); /* inserts of INT32_MAX/UINT32_MAX */
test_union2(); /* repeatable execution of union (Bug #3577) */
test_bug1664(); /* test for bugs in mysql_stmt_send_long_data()
call (Bug #1664) */
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