Commit 625371f3 authored by dlenev@dlenev.mshome's avatar dlenev@dlenev.mshome

Fix for bug #1500 "Server crash with mysql_prepare"

We treat Item_param whose value is not set as non-const.
This allows us to avoid use of Item_param's value (not yet existing) in 
those fix_fields and fix_length_and_dec that do calculations if their 
Items arguments are const. So we can call fix_fields for such items from
mysql_prepare safely.
parent ba659679
...@@ -512,7 +512,7 @@ String *Item_null::val_str(String *str) ...@@ -512,7 +512,7 @@ String *Item_null::val_str(String *str)
void Item_param::set_null() void Item_param::set_null()
{ {
DBUG_ENTER("Item_param::set_null"); DBUG_ENTER("Item_param::set_null");
maybe_null= null_value= 1; maybe_null= null_value= value_is_set= 1;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -521,6 +521,7 @@ void Item_param::set_int(longlong i) ...@@ -521,6 +521,7 @@ void Item_param::set_int(longlong i)
DBUG_ENTER("Item_param::set_int"); DBUG_ENTER("Item_param::set_int");
int_value= (longlong)i; int_value= (longlong)i;
item_type= INT_ITEM; item_type= INT_ITEM;
value_is_set= 1;
DBUG_PRINT("info", ("integer: %lld", int_value)); DBUG_PRINT("info", ("integer: %lld", int_value));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -530,6 +531,7 @@ void Item_param::set_double(double value) ...@@ -530,6 +531,7 @@ void Item_param::set_double(double value)
DBUG_ENTER("Item_param::set_double"); DBUG_ENTER("Item_param::set_double");
real_value=value; real_value=value;
item_type= REAL_ITEM; item_type= REAL_ITEM;
value_is_set= 1;
DBUG_PRINT("info", ("double: %lg", real_value)); DBUG_PRINT("info", ("double: %lg", real_value));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -540,6 +542,7 @@ void Item_param::set_value(const char *str, uint length) ...@@ -540,6 +542,7 @@ void Item_param::set_value(const char *str, uint length)
DBUG_ENTER("Item_param::set_value"); DBUG_ENTER("Item_param::set_value");
str_value.copy(str,length,default_charset()); str_value.copy(str,length,default_charset());
item_type= STRING_ITEM; item_type= STRING_ITEM;
value_is_set= 1;
DBUG_PRINT("info", ("string: %s", str_value.ptr())); DBUG_PRINT("info", ("string: %s", str_value.ptr()));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -561,6 +564,7 @@ void Item_param::set_time(TIME *tm, timestamp_type type) ...@@ -561,6 +564,7 @@ void Item_param::set_time(TIME *tm, timestamp_type type)
item_is_time= true; item_is_time= true;
item_type= STRING_ITEM; item_type= STRING_ITEM;
value_is_set= 1;
} }
...@@ -568,6 +572,7 @@ void Item_param::set_longdata(const char *str, ulong length) ...@@ -568,6 +572,7 @@ void Item_param::set_longdata(const char *str, ulong length)
{ {
str_value.append(str,length); str_value.append(str,length);
long_data_supplied= 1; long_data_supplied= 1;
value_is_set= 1;
} }
......
...@@ -173,7 +173,17 @@ public: ...@@ -173,7 +173,17 @@ public:
virtual cond_result eq_cmp_result() const { return COND_OK; } virtual cond_result eq_cmp_result() const { return COND_OK; }
inline uint float_length(uint decimals_par) const inline uint float_length(uint decimals_par) const
{ return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
/*
Returns true if this is constant (during query execution, i.e. its value
will not change until next fix_fields) and its value is known.
*/
virtual bool const_item() const { return used_tables() == 0; } virtual bool const_item() const { return used_tables() == 0; }
/*
Returns true if this is constant but its value may be not known yet.
(Can be used for parameters of prep. stmts or of stored procedures.)
*/
virtual bool const_during_execution() const
{ return (used_tables() & ~PARAM_TABLE_BIT) == 0; }
virtual void print(String *str_arg) { str_arg->append(full_name()); } virtual void print(String *str_arg) { str_arg->append(full_name()); }
void print_item_w_name(String *); void print_item_w_name(String *);
virtual void update_used_tables() {} virtual void update_used_tables() {}
...@@ -318,6 +328,7 @@ public: ...@@ -318,6 +328,7 @@ public:
class Item_param :public Item class Item_param :public Item
{ {
public: public:
bool value_is_set;
longlong int_value; longlong int_value;
double real_value; double real_value;
TIME ltime; TIME ltime;
...@@ -336,6 +347,7 @@ public: ...@@ -336,6 +347,7 @@ public:
item_result_type = STRING_RESULT; item_result_type = STRING_RESULT;
item_is_time= false; item_is_time= false;
long_data_supplied= false; long_data_supplied= false;
value_is_set= 0;
} }
enum Type type() const { return item_type; } enum Type type() const { return item_type; }
double val(); double val();
...@@ -363,6 +375,13 @@ public: ...@@ -363,6 +375,13 @@ public:
String *query_val_str(String *str); String *query_val_str(String *str);
enum_field_types field_type() const { return MYSQL_TYPE_STRING; } enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
Item *new_item() { return new Item_param(pos_in_query); } Item *new_item() { return new Item_param(pos_in_query); }
/*
If value for parameter was not set we treat it as non-const
so noone will use parameters value in fix_fields still
parameter is constant during execution.
*/
virtual table_map used_tables() const
{ return value_is_set ? (table_map)0 : PARAM_TABLE_BIT; }
void print(String *str) { str->append('?'); } void print(String *str) { str->append('?'); }
}; };
......
...@@ -2713,7 +2713,8 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) ...@@ -2713,7 +2713,8 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref)
modifications to find_best and auto_close as complement to auto_init code modifications to find_best and auto_close as complement to auto_init code
above. above.
*/ */
if (Item_func::fix_fields(thd, tlist, ref) || !args[0]->const_item()) if (Item_func::fix_fields(thd, tlist, ref) ||
!args[0]->const_during_execution())
{ {
my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST"); my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST");
return 1; return 1;
...@@ -2727,11 +2728,15 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) ...@@ -2727,11 +2728,15 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref)
args[i]= item= *((Item_ref *)item)->ref; args[i]= item= *((Item_ref *)item)->ref;
if (item->type() != Item::FIELD_ITEM) if (item->type() != Item::FIELD_ITEM)
key=NO_SUCH_KEY; key=NO_SUCH_KEY;
used_tables_cache|=item->used_tables();
} }
/* check that all columns come from the same table */ /*
if (my_count_bits(used_tables_cache) != 1) Check that all columns come from the same table.
We've already checked that columns in MATCH are fields so
PARAM_TABLE_BIT can only appear from AGAINST argument.
*/
if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables())
key=NO_SUCH_KEY; key=NO_SUCH_KEY;
if (key == NO_SUCH_KEY && !(flags & FT_BOOL)) if (key == NO_SUCH_KEY && !(flags & FT_BOOL))
{ {
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
......
...@@ -7953,6 +7953,117 @@ static void test_ts() ...@@ -7953,6 +7953,117 @@ static void test_ts()
} }
} }
/*
Test for bug #1500.
*/
static void test_bug1500()
{
MYSQL_STMT *stmt;
MYSQL_BIND bind[3];
int rc;
long int_data[3]= {2,3,4};
char *data;
myheader("test_bug1500");
rc= mysql_query(mysql,"DROP TABLE IF EXISTS test_bg1500");
myquery(rc);
rc= mysql_query(mysql,"CREATE TABLE test_bg1500 (i INT)");
myquery(rc);
rc= mysql_query(mysql,"INSERT INTO test_bg1500 VALUES (1),(2)");
myquery(rc);
rc= mysql_commit(mysql);
myquery(rc);
stmt= mysql_prepare(mysql,"SELECT i FROM test_bg1500 WHERE i IN (?,?,?)",44);
mystmt_init(stmt);
verify_param_count(stmt,3);
bind[0].buffer= (char *)int_data;
bind[0].buffer_type= FIELD_TYPE_LONG;
bind[0].is_null= 0;
bind[2]= bind[1]= bind[0];
bind[1].buffer= (char *)(int_data + 1);
bind[2].buffer= (char *)(int_data + 2);
rc= mysql_bind_param(stmt, bind);
mystmt(stmt,rc);
rc= mysql_execute(stmt);
mystmt(stmt,rc);
assert(1 == my_process_stmt_result(stmt));
/* FIXME If we comment out next string server will crash :( */
mysql_stmt_close(stmt);
rc= mysql_query(mysql,"DROP TABLE test_bg1500");
myquery(rc);
rc= mysql_query(mysql,"CREATE TABLE test_bg1500 (s VARCHAR(25), FULLTEXT(s))");
myquery(rc);
rc= mysql_query(mysql,
"INSERT INTO test_bg1500 VALUES ('Gravedigger'), ('Greed'),('Hollow Dogs')");
myquery(rc);
rc= mysql_commit(mysql);
myquery(rc);
stmt= mysql_prepare(mysql,
"SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (?)",53);
mystmt_init(stmt);
verify_param_count(stmt,1);
data= "Dogs";
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= data;
bind[0].buffer_length= strlen(data);
bind[0].is_null= 0;
bind[0].length= 0;
rc= mysql_bind_param(stmt, bind);
mystmt(stmt,rc);
rc= mysql_execute(stmt);
mystmt(stmt,rc);
assert(1 == my_process_stmt_result(stmt));
/*
FIXME If we comment out next string server will crash too :(
This is another manifestation of bug #1663
*/
mysql_stmt_close(stmt);
/* This should work too */
stmt= mysql_prepare(mysql,
"SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (CONCAT(?,'digger'))", 70);
mystmt_init(stmt);
verify_param_count(stmt,1);
data= "Grave";
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= data;
bind[0].buffer_length= strlen(data);
bind[0].is_null= 0;
bind[0].length= 0;
rc= mysql_bind_param(stmt, bind);
mystmt(stmt,rc);
rc= mysql_execute(stmt);
mystmt(stmt,rc);
assert(1 == my_process_stmt_result(stmt));
mysql_stmt_close(stmt);
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
...@@ -8194,6 +8305,7 @@ int main(int argc, char **argv) ...@@ -8194,6 +8305,7 @@ int main(int argc, char **argv)
test_ts(); /* test for timestamp BR#819 */ test_ts(); /* test for timestamp BR#819 */
test_bug1115(); /* BUG#1115 */ test_bug1115(); /* BUG#1115 */
test_bug1180(); /* BUG#1180 */ test_bug1180(); /* BUG#1180 */
test_bug1500(); /* BUG#1500 */
test_bug1644(); /* BUG#1644 */ test_bug1644(); /* BUG#1644 */
end_time= time((time_t *)0); end_time= time((time_t *)0);
......
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