mirror of
https://github.com/MariaDB/server.git
synced 2025-09-02 09:41:40 +03:00
Merge bk-internal.mysql.com:/home/bk/mysql-4.1
into mysql.com:/media/sda1/mysql/mysql-4.1-5748-anew
This commit is contained in:
@@ -297,3 +297,14 @@ execute stmt;
|
|||||||
'abc' like convert('abc' using utf8)
|
'abc' like convert('abc' using utf8)
|
||||||
1
|
1
|
||||||
deallocate prepare stmt;
|
deallocate prepare stmt;
|
||||||
|
create table t1 ( a bigint );
|
||||||
|
prepare stmt from 'select a from t1 where a between ? and ?';
|
||||||
|
set @a=1;
|
||||||
|
execute stmt using @a, @a;
|
||||||
|
a
|
||||||
|
execute stmt using @a, @a;
|
||||||
|
a
|
||||||
|
execute stmt using @a, @a;
|
||||||
|
a
|
||||||
|
drop table t1;
|
||||||
|
deallocate prepare stmt;
|
||||||
|
@@ -314,3 +314,18 @@ prepare stmt from "select 'abc' like convert('abc' using utf8)";
|
|||||||
execute stmt;
|
execute stmt;
|
||||||
execute stmt;
|
execute stmt;
|
||||||
deallocate prepare stmt;
|
deallocate prepare stmt;
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#5748 "Prepared statement with BETWEEN and bigint values crashes
|
||||||
|
# mysqld". Just another place where an item tree modification must be
|
||||||
|
# rolled back.
|
||||||
|
#
|
||||||
|
create table t1 ( a bigint );
|
||||||
|
prepare stmt from 'select a from t1 where a between ? and ?';
|
||||||
|
set @a=1;
|
||||||
|
execute stmt using @a, @a;
|
||||||
|
execute stmt using @a, @a;
|
||||||
|
execute stmt using @a, @a;
|
||||||
|
drop table t1;
|
||||||
|
deallocate prepare stmt;
|
||||||
|
|
||||||
|
17
sql/item.cc
17
sql/item.cc
@@ -106,7 +106,7 @@ void Item::print_item_w_name(String *str)
|
|||||||
Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
|
Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
|
||||||
const char *field_name_par)
|
const char *field_name_par)
|
||||||
:orig_db_name(db_name_par), orig_table_name(table_name_par),
|
:orig_db_name(db_name_par), orig_table_name(table_name_par),
|
||||||
orig_field_name(field_name_par), changed_during_fix_field(0),
|
orig_field_name(field_name_par),
|
||||||
db_name(db_name_par), table_name(table_name_par),
|
db_name(db_name_par), table_name(table_name_par),
|
||||||
field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX),
|
field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX),
|
||||||
cached_table(0), depended_from(0)
|
cached_table(0), depended_from(0)
|
||||||
@@ -120,7 +120,6 @@ Item_ident::Item_ident(THD *thd, Item_ident *item)
|
|||||||
orig_db_name(item->orig_db_name),
|
orig_db_name(item->orig_db_name),
|
||||||
orig_table_name(item->orig_table_name),
|
orig_table_name(item->orig_table_name),
|
||||||
orig_field_name(item->orig_field_name),
|
orig_field_name(item->orig_field_name),
|
||||||
changed_during_fix_field(0),
|
|
||||||
db_name(item->db_name),
|
db_name(item->db_name),
|
||||||
table_name(item->table_name),
|
table_name(item->table_name),
|
||||||
field_name(item->field_name),
|
field_name(item->field_name),
|
||||||
@@ -137,11 +136,6 @@ void Item_ident::cleanup()
|
|||||||
table_name, orig_table_name,
|
table_name, orig_table_name,
|
||||||
field_name, orig_field_name));
|
field_name, orig_field_name));
|
||||||
Item::cleanup();
|
Item::cleanup();
|
||||||
if (changed_during_fix_field)
|
|
||||||
{
|
|
||||||
*changed_during_fix_field= this;
|
|
||||||
changed_during_fix_field= 0;
|
|
||||||
}
|
|
||||||
db_name= orig_db_name;
|
db_name= orig_db_name;
|
||||||
table_name= orig_table_name;
|
table_name= orig_table_name;
|
||||||
field_name= orig_field_name;
|
field_name= orig_field_name;
|
||||||
@@ -1340,10 +1334,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
|||||||
|
|
||||||
Item_ref *rf;
|
Item_ref *rf;
|
||||||
*ref= rf= new Item_ref(last->ref_pointer_array + counter,
|
*ref= rf= new Item_ref(last->ref_pointer_array + counter,
|
||||||
ref,
|
0,
|
||||||
(char *)table_name,
|
(char *)table_name,
|
||||||
(char *)field_name);
|
(char *)field_name);
|
||||||
register_item_tree_changing(ref);
|
thd->register_item_tree_change(ref, this, &thd->mem_root);
|
||||||
if (!rf)
|
if (!rf)
|
||||||
return 1;
|
return 1;
|
||||||
/*
|
/*
|
||||||
@@ -1362,7 +1356,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
|||||||
if (last->having_fix_field)
|
if (last->having_fix_field)
|
||||||
{
|
{
|
||||||
Item_ref *rf;
|
Item_ref *rf;
|
||||||
*ref= rf= new Item_ref(ref, *ref,
|
thd->register_item_tree_change(ref, *ref, &thd->mem_root);
|
||||||
|
*ref= rf= new Item_ref(ref, 0,
|
||||||
(where->db[0]?where->db:0),
|
(where->db[0]?where->db:0),
|
||||||
(char *)where->alias,
|
(char *)where->alias,
|
||||||
(char *)field_name);
|
(char *)field_name);
|
||||||
@@ -2003,7 +1998,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
|
|||||||
Item_field* fld;
|
Item_field* fld;
|
||||||
if (!((*reference)= fld= new Item_field(tmp)))
|
if (!((*reference)= fld= new Item_field(tmp)))
|
||||||
return 1;
|
return 1;
|
||||||
register_item_tree_changing(reference);
|
thd->register_item_tree_change(reference, this, &thd->mem_root);
|
||||||
mark_as_dependent(thd, last, thd->lex->current_select, fld);
|
mark_as_dependent(thd, last, thd->lex->current_select, fld);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -317,7 +317,6 @@ class Item_ident :public Item
|
|||||||
const char *orig_db_name;
|
const char *orig_db_name;
|
||||||
const char *orig_table_name;
|
const char *orig_table_name;
|
||||||
const char *orig_field_name;
|
const char *orig_field_name;
|
||||||
Item **changed_during_fix_field;
|
|
||||||
public:
|
public:
|
||||||
const char *db_name;
|
const char *db_name;
|
||||||
const char *table_name;
|
const char *table_name;
|
||||||
@@ -340,8 +339,6 @@ public:
|
|||||||
Item_ident(THD *thd, Item_ident *item);
|
Item_ident(THD *thd, Item_ident *item);
|
||||||
const char *full_name() const;
|
const char *full_name() const;
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void register_item_tree_changing(Item **ref)
|
|
||||||
{ changed_during_fix_field= ref; }
|
|
||||||
bool remove_dependence_processor(byte * arg);
|
bool remove_dependence_processor(byte * arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -145,7 +145,7 @@ void Item_func_not_all::print(String *str)
|
|||||||
1 Item was replaced with an integer version of the item
|
1 Item was replaced with an integer version of the item
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool convert_constant_item(Field *field, Item **item)
|
static bool convert_constant_item(THD *thd, Field *field, Item **item)
|
||||||
{
|
{
|
||||||
if ((*item)->const_item())
|
if ((*item)->const_item())
|
||||||
{
|
{
|
||||||
@@ -153,7 +153,10 @@ static bool convert_constant_item(Field *field, Item **item)
|
|||||||
{
|
{
|
||||||
Item *tmp=new Item_int_with_ref(field->val_int(), *item);
|
Item *tmp=new Item_int_with_ref(field->val_int(), *item);
|
||||||
if (tmp)
|
if (tmp)
|
||||||
|
{
|
||||||
|
thd->register_item_tree_change(item, *item, &thd->mem_root);
|
||||||
*item=tmp;
|
*item=tmp;
|
||||||
|
}
|
||||||
return 1; // Item was replaced
|
return 1; // Item was replaced
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +167,7 @@ static bool convert_constant_item(Field *field, Item **item)
|
|||||||
void Item_bool_func2::fix_length_and_dec()
|
void Item_bool_func2::fix_length_and_dec()
|
||||||
{
|
{
|
||||||
max_length= 1; // Function returns 0 or 1
|
max_length= 1; // Function returns 0 or 1
|
||||||
|
THD *thd= current_thd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
As some compare functions are generated after sql_yacc,
|
As some compare functions are generated after sql_yacc,
|
||||||
@@ -199,7 +203,6 @@ void Item_bool_func2::fix_length_and_dec()
|
|||||||
!coll.set(args[0]->collation, args[1]->collation, TRUE))
|
!coll.set(args[0]->collation, args[1]->collation, TRUE))
|
||||||
{
|
{
|
||||||
Item* conv= 0;
|
Item* conv= 0;
|
||||||
THD *thd= current_thd;
|
|
||||||
Item_arena *arena= thd->current_arena, backup;
|
Item_arena *arena= thd->current_arena, backup;
|
||||||
strong= coll.strong;
|
strong= coll.strong;
|
||||||
weak= strong ? 0 : 1;
|
weak= strong ? 0 : 1;
|
||||||
@@ -245,7 +248,7 @@ void Item_bool_func2::fix_length_and_dec()
|
|||||||
Field *field=((Item_field*) args[0])->field;
|
Field *field=((Item_field*) args[0])->field;
|
||||||
if (field->can_be_compared_as_longlong())
|
if (field->can_be_compared_as_longlong())
|
||||||
{
|
{
|
||||||
if (convert_constant_item(field,&args[1]))
|
if (convert_constant_item(thd, field,&args[1]))
|
||||||
{
|
{
|
||||||
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
|
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
|
||||||
INT_RESULT); // Works for all types.
|
INT_RESULT); // Works for all types.
|
||||||
@@ -258,7 +261,7 @@ void Item_bool_func2::fix_length_and_dec()
|
|||||||
Field *field=((Item_field*) args[1])->field;
|
Field *field=((Item_field*) args[1])->field;
|
||||||
if (field->can_be_compared_as_longlong())
|
if (field->can_be_compared_as_longlong())
|
||||||
{
|
{
|
||||||
if (convert_constant_item(field,&args[0]))
|
if (convert_constant_item(thd, field,&args[0]))
|
||||||
{
|
{
|
||||||
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
|
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
|
||||||
INT_RESULT); // Works for all types.
|
INT_RESULT); // Works for all types.
|
||||||
@@ -836,6 +839,7 @@ longlong Item_func_interval::val_int()
|
|||||||
void Item_func_between::fix_length_and_dec()
|
void Item_func_between::fix_length_and_dec()
|
||||||
{
|
{
|
||||||
max_length= 1;
|
max_length= 1;
|
||||||
|
THD *thd= current_thd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
As some compare functions are generated after sql_yacc,
|
As some compare functions are generated after sql_yacc,
|
||||||
@@ -858,9 +862,9 @@ void Item_func_between::fix_length_and_dec()
|
|||||||
Field *field=((Item_field*) args[0])->field;
|
Field *field=((Item_field*) args[0])->field;
|
||||||
if (field->can_be_compared_as_longlong())
|
if (field->can_be_compared_as_longlong())
|
||||||
{
|
{
|
||||||
if (convert_constant_item(field,&args[1]))
|
if (convert_constant_item(thd, field,&args[1]))
|
||||||
cmp_type=INT_RESULT; // Works for all types.
|
cmp_type=INT_RESULT; // Works for all types.
|
||||||
if (convert_constant_item(field,&args[2]))
|
if (convert_constant_item(thd, field,&args[2]))
|
||||||
cmp_type=INT_RESULT; // Works for all types.
|
cmp_type=INT_RESULT; // Works for all types.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -697,6 +697,54 @@ void THD::close_active_vio()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct Item_change_record: public ilink
|
||||||
|
{
|
||||||
|
Item **place;
|
||||||
|
Item *old_value;
|
||||||
|
/* Placement new was hidden by `new' in ilink (TODO: check): */
|
||||||
|
static void *operator new(unsigned int size, void *mem) { return mem; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Register an item tree tree transformation, performed by the query
|
||||||
|
optimizer. We need a pointer to runtime_memroot because it may be !=
|
||||||
|
thd->mem_root (due to possible set_n_backup_item_arena called for thd).
|
||||||
|
*/
|
||||||
|
|
||||||
|
void THD::nocheck_register_item_tree_change(Item **place, Item *old_value,
|
||||||
|
MEM_ROOT *runtime_memroot)
|
||||||
|
{
|
||||||
|
Item_change_record *change;
|
||||||
|
/*
|
||||||
|
Now we use one node per change, which adds some memory overhead,
|
||||||
|
but still is rather fast as we use alloc_root for allocations.
|
||||||
|
A list of item tree changes of an average query should be short.
|
||||||
|
*/
|
||||||
|
void *change_mem= alloc_root(runtime_memroot, sizeof(*change));
|
||||||
|
if (change_mem == 0)
|
||||||
|
{
|
||||||
|
fatal_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change= new (change_mem) Item_change_record;
|
||||||
|
change->place= place;
|
||||||
|
change->old_value= old_value;
|
||||||
|
change_list.push_back(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void THD::rollback_item_tree_changes()
|
||||||
|
{
|
||||||
|
I_List_iterator<Item_change_record> it(change_list);
|
||||||
|
Item_change_record *change;
|
||||||
|
while ((change= it++))
|
||||||
|
*change->place= change->old_value;
|
||||||
|
/* We can forget about changes memory: it's allocated in runtime memroot */
|
||||||
|
change_list.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
** Functions to provide a interface to select results
|
** Functions to provide a interface to select results
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@@ -461,6 +461,8 @@ public:
|
|||||||
|
|
||||||
inline bool is_stmt_prepare() const { return (int)state < (int)PREPARED; }
|
inline bool is_stmt_prepare() const { return (int)state < (int)PREPARED; }
|
||||||
inline bool is_first_stmt_execute() const { return state == PREPARED; }
|
inline bool is_first_stmt_execute() const { return state == PREPARED; }
|
||||||
|
inline bool is_conventional_execution() const
|
||||||
|
{ return state == CONVENTIONAL_EXECUTION; }
|
||||||
inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); }
|
inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); }
|
||||||
inline gptr calloc(unsigned int size)
|
inline gptr calloc(unsigned int size)
|
||||||
{
|
{
|
||||||
@@ -640,6 +642,17 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
A registry for item tree transformations performed during
|
||||||
|
query optimization. We register only those changes which require
|
||||||
|
a rollback to re-execute a prepared statement or stored procedure
|
||||||
|
yet another time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Item_change_record;
|
||||||
|
typedef I_List<Item_change_record> Item_change_list;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For each client connection we create a separate thread with THD serving as
|
For each client connection we create a separate thread with THD serving as
|
||||||
a thread/connection descriptor
|
a thread/connection descriptor
|
||||||
@@ -808,6 +821,14 @@ public:
|
|||||||
#ifdef SIGNAL_WITH_VIO_CLOSE
|
#ifdef SIGNAL_WITH_VIO_CLOSE
|
||||||
Vio* active_vio;
|
Vio* active_vio;
|
||||||
#endif
|
#endif
|
||||||
|
/*
|
||||||
|
This is to track items changed during execution of a prepared
|
||||||
|
statement/stored procedure. It's created by
|
||||||
|
register_item_tree_change() in memory root of THD, and freed in
|
||||||
|
rollback_item_tree_changes(). For conventional execution it's always 0.
|
||||||
|
*/
|
||||||
|
Item_change_list change_list;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Current prepared Item_arena if there one, or 0
|
Current prepared Item_arena if there one, or 0
|
||||||
*/
|
*/
|
||||||
@@ -1032,6 +1053,16 @@ public:
|
|||||||
}
|
}
|
||||||
inline CHARSET_INFO *charset() { return variables.character_set_client; }
|
inline CHARSET_INFO *charset() { return variables.character_set_client; }
|
||||||
void update_charset();
|
void update_charset();
|
||||||
|
|
||||||
|
void register_item_tree_change(Item **place, Item *old_value,
|
||||||
|
MEM_ROOT *runtime_memroot)
|
||||||
|
{
|
||||||
|
if (!current_arena->is_conventional_execution())
|
||||||
|
nocheck_register_item_tree_change(place, old_value, runtime_memroot);
|
||||||
|
}
|
||||||
|
void nocheck_register_item_tree_change(Item **place, Item *old_value,
|
||||||
|
MEM_ROOT *runtime_memroot);
|
||||||
|
void rollback_item_tree_changes();
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Flags for the THD::system_thread (bitmap) variable */
|
/* Flags for the THD::system_thread (bitmap) variable */
|
||||||
|
@@ -4056,6 +4056,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
|||||||
}
|
}
|
||||||
thd->proc_info="freeing items";
|
thd->proc_info="freeing items";
|
||||||
thd->end_statement();
|
thd->end_statement();
|
||||||
|
DBUG_ASSERT(thd->change_list.is_empty());
|
||||||
}
|
}
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
@@ -1614,6 +1614,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
|
|||||||
cleanup_items(stmt->free_list);
|
cleanup_items(stmt->free_list);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
free_items(thd->free_list);
|
free_items(thd->free_list);
|
||||||
|
thd->rollback_item_tree_changes();
|
||||||
thd->free_list= 0;
|
thd->free_list= 0;
|
||||||
thd->current_arena= thd;
|
thd->current_arena= thd;
|
||||||
|
|
||||||
@@ -1870,7 +1871,6 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
|
|||||||
transformations of the query tree (i.e. negations elimination).
|
transformations of the query tree (i.e. negations elimination).
|
||||||
This should be done permanently on the parse tree of this statement.
|
This should be done permanently on the parse tree of this statement.
|
||||||
*/
|
*/
|
||||||
if (stmt->state == Item_arena::PREPARED)
|
|
||||||
thd->current_arena= stmt;
|
thd->current_arena= stmt;
|
||||||
|
|
||||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||||
@@ -1884,11 +1884,10 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
|
|||||||
free_items(thd->free_list);
|
free_items(thd->free_list);
|
||||||
thd->free_list= 0;
|
thd->free_list= 0;
|
||||||
if (stmt->state == Item_arena::PREPARED)
|
if (stmt->state == Item_arena::PREPARED)
|
||||||
{
|
|
||||||
thd->current_arena= thd;
|
|
||||||
stmt->state= Item_arena::EXECUTED;
|
stmt->state= Item_arena::EXECUTED;
|
||||||
}
|
thd->current_arena= thd;
|
||||||
cleanup_items(stmt->free_list);
|
cleanup_items(stmt->free_list);
|
||||||
|
thd->rollback_item_tree_changes();
|
||||||
reset_stmt_params(stmt);
|
reset_stmt_params(stmt);
|
||||||
close_thread_tables(thd); // to close derived tables
|
close_thread_tables(thd); // to close derived tables
|
||||||
thd->set_statement(&thd->stmt_backup);
|
thd->set_statement(&thd->stmt_backup);
|
||||||
|
@@ -8442,7 +8442,7 @@ static void test_subqueries_ref()
|
|||||||
int rc, i;
|
int rc, i;
|
||||||
const char *query= "SELECT a as ccc from t1 where a+1=(SELECT 1+ccc from t1 where ccc+1=a+1 and a=1)";
|
const char *query= "SELECT a as ccc from t1 where a+1=(SELECT 1+ccc from t1 where ccc+1=a+1 and a=1)";
|
||||||
|
|
||||||
myheader("test_subquery_ref");
|
myheader("test_subqueries_ref");
|
||||||
|
|
||||||
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
||||||
myquery(rc);
|
myquery(rc);
|
||||||
|
Reference in New Issue
Block a user