diff options
Diffstat (limited to 'sql')
35 files changed, 800 insertions, 284 deletions
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index ea20270b457..d9ca161f260 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -128,7 +128,6 @@ post_init_event_thread(THD *thd) thd->cleanup(); return TRUE; } - lex_start(thd); pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); diff --git a/sql/events.cc b/sql/events.cc index 8c9e60e73e8..d4efcdbdc4a 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -895,7 +895,6 @@ Events::init(my_bool opt_noacl_or_bootstrap) */ thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); /* We will need Event_db_repository anyway, even if the scheduler is diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 9501416f9be..b35746102c8 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -6956,7 +6956,6 @@ int ndb_create_table_from_engine(THD *thd, const char *db, LEX *old_lex= thd->lex, newlex; thd->lex= &newlex; newlex.current_select= NULL; - lex_start(thd); int res= ha_create_table_from_engine(thd, db, table_name); thd->lex= old_lex; return res; @@ -9317,7 +9316,6 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) thd->thread_stack= (char*)&thd; /* remember where our stack is */ if (thd->store_globals()) goto ndb_util_thread_fail; - lex_start(thd); thd->init_for_queries(); thd->version=refresh_version; thd->main_security_ctx.host_or_ip= ""; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 8b3764367c2..d178b16520c 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -3654,7 +3654,6 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) pthread_exit(0); return NULL; // Avoid compiler warnings } - lex_start(thd); thd->init_for_queries(); thd->command= COM_DAEMON; diff --git a/sql/item.cc b/sql/item.cc index 21e06eee589..7de32423927 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -5025,7 +5025,7 @@ Field *Item::make_string_field(TABLE *table) DBUG_ASSERT(collation.collation); if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) field= new Field_blob(max_length, maybe_null, name, - collation.collation); + collation.collation, TRUE); /* Item_type_holder holds the exact type, do not change it */ else if (max_length > 0 && (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING)) @@ -5766,6 +5766,67 @@ bool Item::send(Protocol *protocol, String *buffer) } +/** + Check if an item is a constant one and can be cached. + + @param arg [out] TRUE <=> Cache this item. + + @return TRUE Go deeper in item tree. + @return FALSE Don't go deeper in item tree. +*/ + +bool Item::cache_const_expr_analyzer(uchar **arg) +{ + bool *cache_flag= (bool*)*arg; + if (!*cache_flag) + { + Item *item= real_item(); + /* + Cache constant items unless it's a basic constant, constant field or + a subselect (they use their own cache). + */ + if (const_item() && + !(item->basic_const_item() || item->type() == Item::FIELD_ITEM || + item->type() == SUBSELECT_ITEM || + /* + Do not cache GET_USER_VAR() function as its const_item() may + return TRUE for the current thread but it still may change + during the execution. + */ + (item->type() == Item::FUNC_ITEM && + ((Item_func*)item)->functype() == Item_func::GUSERVAR_FUNC))) + *cache_flag= TRUE; + return TRUE; + } + return FALSE; +} + + +/** + Cache item if needed. + + @param arg TRUE <=> Cache this item. + + @return cache if cache needed. + @return this otherwise. +*/ + +Item* Item::cache_const_expr_transformer(uchar *arg) +{ + if (*(bool*)arg) + { + *((bool*)arg)= FALSE; + Item_cache *cache= Item_cache::get_cache(this); + if (!cache) + return NULL; + cache->setup(this); + cache->store(this); + return cache; + } + return this; +} + + bool Item_field::send(Protocol *protocol, String *buffer) { return protocol->store(result_field); @@ -7104,7 +7165,22 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) Item_cache* Item_cache::get_cache(const Item *item) { - switch (item->result_type()) { + return get_cache(item, item->result_type()); +} + + +/** + Get a cache item of given type. + + @param item value to be cached + @param type required type of cache + + @return cache item +*/ + +Item_cache* Item_cache::get_cache(const Item *item, const Item_result type) +{ + switch (type) { case INT_RESULT: return new Item_cache_int(); case REAL_RESULT: @@ -7112,6 +7188,10 @@ Item_cache* Item_cache::get_cache(const Item *item) case DECIMAL_RESULT: return new Item_cache_decimal(); case STRING_RESULT: + if (item->field_type() == MYSQL_TYPE_DATE || + item->field_type() == MYSQL_TYPE_DATETIME || + item->field_type() == MYSQL_TYPE_TIME) + return new Item_cache_datetime(item->field_type()); return new Item_cache_str(item); case ROW_RESULT: return new Item_cache_row(); @@ -7122,6 +7202,12 @@ Item_cache* Item_cache::get_cache(const Item *item) } } +void Item_cache::store(Item *item) +{ + if (item) + example= item; + value_cached= FALSE; +} void Item_cache::print(String *str, enum_query_type query_type) { @@ -7133,17 +7219,19 @@ void Item_cache::print(String *str, enum_query_type query_type) str->append(')'); } - -void Item_cache_int::store(Item *item) +void Item_cache_int::cache_value() { - value= item->val_int_result(); - null_value= item->null_value; - unsigned_flag= item->unsigned_flag; + value_cached= TRUE; + value= example->val_int_result(); + null_value= example->null_value; + unsigned_flag= example->unsigned_flag; } void Item_cache_int::store(Item *item, longlong val_arg) { + /* An explicit values is given, save it. */ + value_cached= TRUE; value= val_arg; null_value= item->null_value; unsigned_flag= item->unsigned_flag; @@ -7153,6 +7241,8 @@ void Item_cache_int::store(Item *item, longlong val_arg) String *Item_cache_int::val_str(String *str) { DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); str->set(value, default_charset()); return str; } @@ -7161,21 +7251,115 @@ String *Item_cache_int::val_str(String *str) my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) { DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val); return decimal_val; } +double Item_cache_int::val_real() +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); + return (double) value; +} + +longlong Item_cache_int::val_int() +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); + return value; +} + +void Item_cache_datetime::cache_value_int() +{ + value_cached= TRUE; + /* Assume here that the underlying item will do correct conversion.*/ + int_value= example->val_int_result(); + null_value= example->null_value; + unsigned_flag= example->unsigned_flag; +} -void Item_cache_real::store(Item *item) + +void Item_cache_datetime::cache_value() { - value= item->val_result(); + str_value_cached= TRUE; + /* Assume here that the underlying item will do correct conversion.*/ + String *res= example->str_result(&str_value); + if (res && res != &str_value) + str_value.copy(*res); + null_value= example->null_value; + unsigned_flag= example->unsigned_flag; +} + + +void Item_cache_datetime::store(Item *item, longlong val_arg) +{ + /* An explicit values is given, save it. */ + value_cached= TRUE; + int_value= val_arg; null_value= item->null_value; + unsigned_flag= item->unsigned_flag; +} + + +String *Item_cache_datetime::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + if (!str_value_cached) + cache_value(); + return &str_value; +} + + +my_decimal *Item_cache_datetime::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value_int(); + int2my_decimal(E_DEC_FATAL_ERROR, int_value, unsigned_flag, decimal_val); + return decimal_val; +} + +double Item_cache_datetime::val_real() +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value_int(); + return (double) int_value; } +longlong Item_cache_datetime::val_int() +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value_int(); + return int_value; +} + +void Item_cache_real::cache_value() +{ + value_cached= TRUE; + value= example->val_result(); + null_value= example->null_value; +} + + +double Item_cache_real::val_real() +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); + return value; +} longlong Item_cache_real::val_int() { DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); return (longlong) rint(value); } @@ -7183,6 +7367,8 @@ longlong Item_cache_real::val_int() String* Item_cache_real::val_str(String *str) { DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); str->set_real(value, decimals, default_charset()); return str; } @@ -7191,15 +7377,18 @@ String* Item_cache_real::val_str(String *str) my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val) { DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); return decimal_val; } -void Item_cache_decimal::store(Item *item) +void Item_cache_decimal::cache_value() { - my_decimal *val= item->val_decimal_result(&decimal_value); - if (!(null_value= item->null_value) && val != &decimal_value) + value_cached= TRUE; + my_decimal *val= example->val_decimal_result(&decimal_value); + if (!(null_value= example->null_value) && val != &decimal_value) my_decimal2decimal(val, &decimal_value); } @@ -7207,6 +7396,8 @@ double Item_cache_decimal::val_real() { DBUG_ASSERT(fixed); double res; + if (!value_cached) + cache_value(); my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res); return res; } @@ -7215,6 +7406,8 @@ longlong Item_cache_decimal::val_int() { DBUG_ASSERT(fixed); longlong res; + if (!value_cached) + cache_value(); my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res); return res; } @@ -7222,6 +7415,8 @@ longlong Item_cache_decimal::val_int() String* Item_cache_decimal::val_str(String *str) { DBUG_ASSERT(fixed); + if (!value_cached) + cache_value(); my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE, &decimal_value); my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str); @@ -7231,15 +7426,18 @@ String* Item_cache_decimal::val_str(String *str) my_decimal *Item_cache_decimal::val_decimal(my_decimal *val) { DBUG_ASSERT(fixed); + if (!value_cached) + cache_value(); return &decimal_value; } -void Item_cache_str::store(Item *item) +void Item_cache_str::cache_value() { - value_buff.set(buffer, sizeof(buffer), item->collation.collation); - value= item->str_result(&value_buff); - if ((null_value= item->null_value)) + value_cached= TRUE; + value_buff.set(buffer, sizeof(buffer), example->collation.collation); + value= example->str_result(&value_buff); + if ((null_value= example->null_value)) value= 0; else if (value != &value_buff) { @@ -7261,6 +7459,8 @@ double Item_cache_str::val_real() DBUG_ASSERT(fixed == 1); int err_not_used; char *end_not_used; + if (!value_cached) + cache_value(); if (value) return my_strntod(value->charset(), (char*) value->ptr(), value->length(), &end_not_used, &err_not_used); @@ -7272,6 +7472,8 @@ longlong Item_cache_str::val_int() { DBUG_ASSERT(fixed == 1); int err; + if (!value_cached) + cache_value(); if (value) return my_strntoll(value->charset(), value->ptr(), value->length(), 10, (char**) 0, &err); @@ -7279,9 +7481,21 @@ longlong Item_cache_str::val_int() return (longlong)0; } + +String* Item_cache_str::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); + return value; +} + + my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) { DBUG_ASSERT(fixed == 1); + if (!value_cached) + cache_value(); if (value) string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); else @@ -7292,6 +7506,8 @@ my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) int Item_cache_str::save_in_field(Field *field, bool no_conversions) { + if (!value_cached) + cache_value(); int res= Item_cache::save_in_field(field, no_conversions); return (is_varbinary && field->type() == MYSQL_TYPE_STRING && value->length() < field->field_length) ? 1 : res; @@ -7326,11 +7542,19 @@ bool Item_cache_row::setup(Item * item) void Item_cache_row::store(Item * item) { + for (uint i= 0; i < item_count; i++) + values[i]->store(item->element_index(i)); +} + + +void Item_cache_row::cache_value() +{ + value_cached= TRUE; null_value= 0; - item->bring_value(); + example->bring_value(); for (uint i= 0; i < item_count; i++) { - values[i]->store(item->element_index(i)); + values[i]->cache_value(); null_value|= values[i]->null_value; } } diff --git a/sql/item.h b/sql/item.h index 342b482c471..a963206a2dc 100644 --- a/sql/item.h +++ b/sql/item.h @@ -903,6 +903,9 @@ public: virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; } virtual bool is_expensive_processor(uchar *arg) { return 0; } virtual bool register_field_in_read_map(uchar *arg) { return 0; } + + virtual bool cache_const_expr_analyzer(uchar **arg); + virtual Item* cache_const_expr_transformer(uchar *arg); /* Check if a partition function is allowed SYNOPSIS @@ -1032,7 +1035,11 @@ class sp_head; class Item_basic_constant :public Item { + table_map used_table_map; public: + Item_basic_constant(): Item(), used_table_map(0) {}; + void set_used_tables(table_map map) { used_table_map= map; } + table_map used_tables() const { return used_table_map; } /* to prevent drop fixed flag (no need parent cleanup call) */ void cleanup() { @@ -2288,6 +2295,7 @@ public: if (ref && result_type() == ROW_RESULT) (*ref)->bring_value(); } + bool basic_const_item() { return (*ref)->basic_const_item(); } }; @@ -2918,15 +2926,25 @@ protected: */ Field *cached_field; enum enum_field_types cached_field_type; + /* + TRUE <=> cache holds value of the last stored item (i.e actual value). + store() stores item to be cached and sets this flag to FALSE. + On the first call of val_xxx function if this flag is set to FALSE the + cache_value() will be called to actually cache value of saved item. + cache_value() will set this flag to TRUE. + */ + bool value_cached; public: - Item_cache(): - example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING) + Item_cache(): + example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING), + value_cached(0) { fixed= 1; null_value= 1; } Item_cache(enum_field_types field_type_arg): - example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg) + example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg), + value_cached(0) { fixed= 1; null_value= 1; @@ -2946,10 +2964,10 @@ public: cached_field= ((Item_field *)item)->field; return 0; }; - virtual void store(Item *)= 0; enum Type type() const { return CACHE_ITEM; } enum_field_types field_type() const { return cached_field_type; } static Item_cache* get_cache(const Item *item); + static Item_cache* get_cache(const Item* item, const Item_result type); table_map used_tables() const { return used_table_map; } virtual void keep_array() {} virtual void print(String *str, enum_query_type query_type); @@ -2961,6 +2979,10 @@ public: { return this == item; } + virtual void store(Item *item); + virtual void cache_value()= 0; + bool basic_const_item() const + { return test(example && example->basic_const_item());} }; @@ -2969,18 +2991,19 @@ class Item_cache_int: public Item_cache protected: longlong value; public: - Item_cache_int(): Item_cache(), value(0) {} + Item_cache_int(): Item_cache(), + value(0) {} Item_cache_int(enum_field_types field_type_arg): Item_cache(field_type_arg), value(0) {} - void store(Item *item); void store(Item *item, longlong val_arg); - double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } - longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } + double val_real(); + longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return INT_RESULT; } bool result_as_longlong() { return TRUE; } + void cache_value(); }; @@ -2988,14 +3011,15 @@ class Item_cache_real: public Item_cache { double value; public: - Item_cache_real(): Item_cache(), value(0) {} + Item_cache_real(): Item_cache(), + value(0) {} - void store(Item *item); - double val_real() { DBUG_ASSERT(fixed == 1); return value; } + double val_real(); longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return REAL_RESULT; } + void cache_value(); }; @@ -3006,12 +3030,12 @@ protected: public: Item_cache_decimal(): Item_cache() {} - void store(Item *item); double val_real(); longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return DECIMAL_RESULT; } + void cache_value(); }; @@ -3029,14 +3053,14 @@ public: MYSQL_TYPE_VARCHAR && !((const Item_field *) item)->field->has_charset()) {} - void store(Item *item); double val_real(); longlong val_int(); - String* val_str(String *) { DBUG_ASSERT(fixed == 1); return value; } + String* val_str(String *); my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return STRING_RESULT; } CHARSET_INFO *charset() const { return value->charset(); }; int save_in_field(Field *field, bool no_conversions); + void cache_value(); }; class Item_cache_row: public Item_cache @@ -3046,7 +3070,8 @@ class Item_cache_row: public Item_cache bool save_array; public: Item_cache_row() - :Item_cache(), values(0), item_count(2), save_array(0) {} + :Item_cache(), values(0), item_count(2), + save_array(0) {} /* 'allocate' used only in row transformer, to preallocate space for row @@ -3104,6 +3129,39 @@ public: values= 0; DBUG_VOID_RETURN; } + void cache_value(); +}; + + +class Item_cache_datetime: public Item_cache +{ +protected: + String str_value; + ulonglong int_value; + bool str_value_cached; +public: + Item_cache_datetime(enum_field_types field_type_arg): + Item_cache(field_type_arg), int_value(0), str_value_cached(0) + { + cmp_context= STRING_RESULT; + } + + void store(Item *item, longlong val_arg); + double val_real(); + longlong val_int(); + String* val_str(String *str); + my_decimal *val_decimal(my_decimal *); + enum Item_result result_type() const { return STRING_RESULT; } + bool result_as_longlong() { return TRUE; } + /* + In order to avoid INT <-> STRING conversion of a DATETIME value + two cache_value functions are introduced. One (cache_value) caches STRING + value, another (cache_value_int) - INT value. Thus this cache item + completely relies on the ability of the underlying item to do the + correct conversion. + */ + void cache_value_int(); + void cache_value(); }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9e0fe0b2562..fabb9a64b0d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -882,13 +882,13 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, { enum enum_date_cmp_type cmp_type; ulonglong const_value= (ulonglong)-1; + thd= current_thd; + owner= owner_arg; a= a1; b= a2; if ((cmp_type= can_compare_as_dates(*a, *b, &const_value))) { - thd= current_thd; - owner= owner_arg; a_type= (*a)->field_type(); b_type= (*b)->field_type(); a_cache= 0; @@ -896,6 +896,10 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, if (const_value != (ulonglong)-1) { + /* + cache_converted_constant can't be used here because it can't + correctly convert a DATETIME value from string to int representation. + */ Item_cache_int *cache= new Item_cache_int(); /* Mark the cache as non-const to prevent re-caching. */ cache->set_used_tables(1); @@ -921,8 +925,6 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, (*b)->field_type() == MYSQL_TYPE_TIME) { /* Compare TIME values as integers. */ - thd= current_thd; - owner= owner_arg; a_cache= 0; b_cache= 0; is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); @@ -941,10 +943,46 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, return 1; } + a= cache_converted_constant(thd, a, &a_cache, type); + b= cache_converted_constant(thd, b, &b_cache, type); return set_compare_func(owner_arg, type); } +/** + Convert and cache a constant. + + @param value [in] An item to cache + @param cache_item [out] Placeholder for the cache item + @param type [in] Comparison type + + @details + When given item is a constant and its type differs from comparison type + then cache its value to avoid type conversion of this constant on each + evaluation. In this case the value is cached and the reference to the cache + is returned. + Original value is returned otherwise. + + @return cache item or original value. +*/ + +Item** Arg_comparator::cache_converted_constant(THD *thd, Item **value, + Item **cache_item, + Item_result type) +{ + /* Don't need cache if doing context analysis only. */ + if (!thd->is_context_analysis_only() && + (*value)->const_item() && type != (*value)->result_type()) + { + Item_cache *cache= Item_cache::get_cache(*value, type); + cache->store(*value); + *cache_item= cache; + return cache_item; + } + return value; +} + + void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) { thd= current_thd; @@ -1583,6 +1621,7 @@ longlong Item_in_optimizer::val_int() bool tmp; DBUG_ASSERT(fixed == 1); cache->store(args[0]); + cache->cache_value(); if (cache->null_value) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c7ee0ee9b91..488909ae761 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -97,6 +97,8 @@ public: ulonglong *const_val_arg); void set_datetime_cmp_func(Item **a1, Item **b1); + Item** cache_converted_constant(THD *thd, Item **value, Item **cache, + Item_result type); static arg_cmp_func comparator_matrix [5][2]; friend class Item_func; diff --git a/sql/item_func.cc b/sql/item_func.cc index 455bc5568e3..883476369f2 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3127,7 +3127,7 @@ String *udf_handler::val_str(String *str,String *save_str) if (res == str->ptr()) { str->length(res_length); - DBUG_PRINT("exit", ("str: %s", str->ptr())); + DBUG_PRINT("exit", ("str: %*.s", (int) str->length(), str->ptr())); DBUG_RETURN(str); } save_str->set(res, res_length, str->charset()); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a619827b2d3..267a4b38450 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -480,6 +480,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) void Item_singlerow_subselect::store(uint i, Item *item) { row[i]->store(item); + row[i]->cache_value(); } enum Item_result Item_singlerow_subselect::result_type() const @@ -1826,6 +1827,7 @@ void subselect_engine::set_row(List<Item> &item_list, Item_cache **row) if (!(row[i]= Item_cache::get_cache(sel_item))) return; row[i]->setup(sel_item); + row[i]->store(sel_item); } if (item_list.elements > 1) res_type= ROW_RESULT; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 1eff00027f2..3e20b90e68e 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -941,14 +941,16 @@ static Item *create_comparator(MY_XPATH *xpath, in a loop through all of the nodes in the node set. */ - Item *fake= new Item_string("", 0, xpath->cs); + Item_string *fake= new Item_string("", 0, xpath->cs); + /* Don't cache fake because its value will be changed during comparison.*/ + fake->set_used_tables(RAND_TABLE_BIT); Item_nodeset_func *nodeset; Item *scalar, *comp; if (a->type() == Item::XPATH_NODESET) { nodeset= (Item_nodeset_func*) a; scalar= b; - comp= eq_func(oper, fake, scalar); + comp= eq_func(oper, (Item*)fake, scalar); } else { diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 355a4ba6b68..56d79ac0d45 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -801,7 +801,7 @@ int partition_info::compare_column_values(const void *first_arg, if (first->max_value || second->max_value) { if (first->max_value && second->max_value) - continue; + return 0; if (second->max_value) return -1; else diff --git a/sql/protocol.cc b/sql/protocol.cc index ba1f770fea6..855a6842f1f 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -413,8 +413,8 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err, thd->variables.character_set_results, err, strlen(err), system_charset_info, &error); - length= (uint) (strmake((char*) pos, (char*)converted_err, MYSQL_ERRMSG_SIZE) - - (char*) buff); + length= (uint) (strmake((char*) pos, (char*)converted_err, + MYSQL_ERRMSG_SIZE - 1) - (char*) buff); err= (char*) buff; DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err, @@ -1012,8 +1012,8 @@ bool Protocol_text::store(const char *from, size_t length, { CHARSET_INFO *tocs= this->thd->variables.character_set_results; #ifndef DBUG_OFF - DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %s", field_pos, - field_count, from)); + DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %*.s", + field_pos, field_count, (int) length, from)); DBUG_ASSERT(field_pos < field_count); DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc index b8b82bc2f98..ebd6e4e0c0b 100644 --- a/sql/rpl_handler.cc +++ b/sql/rpl_handler.cc @@ -137,28 +137,53 @@ void delegates_destroy() */ #define FOREACH_OBSERVER(r, f, thd, args) \ param.server_id= thd->server_id; \ + /* + Use a struct to make sure that they are allocated adjacent, check + delete_dynamic(). + */ \ + struct { \ + DYNAMIC_ARRAY plugins; \ + /* preallocate 8 slots */ \ + plugin_ref plugins_buffer[8]; \ + } s; \ + DYNAMIC_ARRAY *plugins= &s.plugins; \ + plugin_ref *plugins_buffer= s.plugins_buffer; \ + my_init_dynamic_array2(plugins, sizeof(plugin_ref), \ + plugins_buffer, 8, 8); \ read_lock(); \ Observer_info_iterator iter= observer_info_iter(); \ Observer_info *info= iter++; \ for (; info; info= iter++) \ { \ plugin_ref plugin= \ - my_plugin_lock(thd, &info->plugin); \ + my_plugin_lock(0, &info->plugin); \ if (!plugin) \ { \ - r= 1; \ + /* plugin is not intialized or deleted, this is not an error */ \ + r= 0; \ break; \ } \ + insert_dynamic(plugins, (uchar *)&plugin); \ if (((Observer *)info->observer)->f \ && ((Observer *)info->observer)->f args) \ { \ r= 1; \ - plugin_unlock(thd, plugin); \ + sql_print_error("Run function '" #f "' in plugin '%s' failed", \ + info->plugin_int->name.str); \ break; \ } \ - plugin_unlock(thd, plugin); \ } \ - unlock() + unlock(); \ + /* + Unlock plugins should be done after we released the Delegate lock + to avoid possible deadlock when this is the last user of the + plugin, and when we unlock the plugin, it will try to + deinitialize the plugin, which will try to lock the Delegate in + order to remove the observers. + */ \ + plugin_unlock_list(0, (plugin_ref*)plugins->buffer, \ + plugins->elements); \ + delete_dynamic(plugins) int Trans_delegate::after_commit(THD *thd, bool all) diff --git a/sql/slave.cc b/sql/slave.cc index 0316ae2591d..62ddc8aaf21 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1933,7 +1933,6 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) thd->cleanup(); DBUG_RETURN(-1); } - lex_start(thd); if (thd_type == SLAVE_THD_SQL) thd_proc_info(thd, "Waiting for the next event in relay log"); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 7316bcb2a46..da99d9e0f6a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3997,7 +3997,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, /** - Simple function for adding an explicetly named (systems) table to + Simple function for adding an explicitly named (systems) table to the global table list, e.g. "mysql", "proc". */ diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 51b797fe088..d966de03b4e 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -669,7 +669,7 @@ sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr } m_case_expr_holders[case_expr_id]->store(case_expr_item); - + m_case_expr_holders[case_expr_id]->cache_value(); return FALSE; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 34680855337..4b9cee98211 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -277,7 +277,6 @@ my_bool acl_init(bool dont_read_acl_tables) DBUG_RETURN(1); /* purecov: inspected */ thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); /* It is safe to call acl_reload() since acl_* arrays and hashes which will be freed there are global static objects and thus are initialized @@ -3582,7 +3581,6 @@ my_bool grant_init() DBUG_RETURN(1); /* purecov: deadcode */ thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); return_val= grant_reload(thd); delete thd; /* Remember that we don't have a THD */ @@ -3804,11 +3802,11 @@ static my_bool grant_reload_procs_priv(THD *thd) DBUG_RETURN(TRUE); } + rw_wrlock(&LOCK_grant); /* Save a copy of the current hash if we need to undo the grant load */ old_proc_priv_hash= proc_priv_hash; old_func_priv_hash= func_priv_hash; - rw_wrlock(&LOCK_grant); if ((return_val= grant_load_procs_priv(table.table))) { /* Error; Reverting to old hash */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index cce376d86d1..99a0b33f213 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7748,7 +7748,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds) { SELECT_LEX *select_lex= thd->lex->current_select; - Query_arena *arena= thd->stmt_arena, backup; TABLE_LIST *table= NULL; // For HP compilers /* it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX @@ -7764,10 +7763,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, select_lex->is_item_list_lookup= 0; DBUG_ENTER("setup_conds"); - if (select_lex->conds_processed_with_permanent_arena || - arena->is_conventional()) - arena= 0; // For easier test - thd->mark_used_columns= MARK_COLUMNS_READ; DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); select_lex->cond_count= 0; @@ -7836,7 +7831,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, We do this ON -> WHERE transformation only once per PS/SP statement. */ select_lex->where= *conds; - select_lex->conds_processed_with_permanent_arena= 1; } thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; DBUG_RETURN(test(thd->is_error())); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d42a06b3811..aa647aada60 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1616,7 +1616,6 @@ void THD::rollback_item_tree_changes() select_result::select_result() { thd=current_thd; - nest_level= -1; } void select_result::send_error(uint errcode,const char *err) diff --git a/sql/sql_class.h b/sql/sql_class.h index b99c25782e0..ebea2041715 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2634,7 +2634,6 @@ class select_result :public Sql_alloc { protected: THD *thd; SELECT_LEX_UNIT *unit; - uint nest_level; public: select_result(); virtual ~select_result() {}; @@ -2671,12 +2670,6 @@ public: */ virtual void cleanup(); void set_thd(THD *thd_arg) { thd= thd_arg; } - /** - The nest level, if supported. - @return - -1 if nest level is undefined, otherwise a positive integer. - */ - int get_nest_level() { return nest_level; } #ifdef EMBEDDED_LIBRARY virtual void begin_dataset() {} #else @@ -2771,14 +2764,6 @@ class select_export :public select_to_file { CHARSET_INFO *write_cs; // output charset public: select_export(sql_exchange *ex) :select_to_file(ex) {} - /** - Creates a select_export to represent INTO OUTFILE <filename> with a - defined level of subquery nesting. - */ - select_export(sql_exchange *ex, uint nest_level_arg) :select_to_file(ex) - { - nest_level= nest_level_arg; - } ~select_export(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); @@ -2788,15 +2773,6 @@ public: class select_dump :public select_to_file { public: select_dump(sql_exchange *ex) :select_to_file(ex) {} - /** - Creates a select_export to represent INTO DUMPFILE <filename> with a - defined level of subquery nesting. - */ - select_dump(sql_exchange *ex, uint nest_level_arg) : - select_to_file(ex) - { - nest_level= nest_level_arg; - } int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); }; @@ -3267,16 +3243,6 @@ class select_dumpvar :public select_result_interceptor { public: List<my_var> var_list; select_dumpvar() { var_list.empty(); row_count= 0;} - /** - Creates a select_dumpvar to represent INTO <variable> with a defined - level of subquery nesting. - */ - select_dumpvar(uint nest_level_arg) - { - var_list.empty(); - row_count= 0; - nest_level= nest_level_arg; - } ~select_dumpvar() {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c443f092d9b..64eb1d2b1a7 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1672,7 +1672,6 @@ void st_select_lex::init_query() parent_lex->push_context(&context); cond_count= between_count= with_wild= 0; max_equal_elems= 0; - conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; select_n_where_fields= 0; select_n_having_items= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1b64df51a89..715e6b288f2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -403,6 +403,8 @@ public: struct LEX; class st_select_lex; class st_select_lex_unit; + + class st_select_lex_node { protected: st_select_lex_node *next, **prev, /* neighbor list */ @@ -440,8 +442,17 @@ public: { return (void*) alloc_root(mem_root, (uint) size); } static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); } static void operator delete(void *ptr, MEM_ROOT *mem_root) {} - st_select_lex_node(): linkage(UNSPECIFIED_TYPE) {} + + // Ensures that at least all members used during cleanup() are initialized. + st_select_lex_node() + : next(NULL), prev(NULL), + master(NULL), slave(NULL), + link_next(NULL), link_prev(NULL), + linkage(UNSPECIFIED_TYPE) + { + } virtual ~st_select_lex_node() {} + inline st_select_lex_node* get_master() { return master; } virtual void init_query(); virtual void init_select(); @@ -487,6 +498,8 @@ class select_result; class JOIN; class select_union; class Procedure; + + class st_select_lex_unit: public st_select_lex_node { protected: TABLE_LIST result_table_list; @@ -498,6 +511,14 @@ protected: bool saved_error; public: + // Ensures that at least all members used during cleanup() are initialized. + st_select_lex_unit() + : union_result(NULL), table(NULL), result(NULL), + cleaned(false), + fake_select_lex(NULL) + { + } + bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) executed, // already executed @@ -638,11 +659,6 @@ public: uint select_n_where_fields; enum_parsing_place parsing_place; /* where we are parsing expression */ bool with_sum_func; /* sum function indicator */ - /* - PS or SP cond natural joins was alredy processed with permanent - arena and all additional items which we need alredy stored in it - */ - bool conds_processed_with_permanent_arena; ulong table_join_options; uint in_sum_expr; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 15832b99192..7fbf04c751f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6465,7 +6465,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, { thd->thread_stack= (char*) &tmp_thd; thd->store_globals(); - lex_start(thd); } if (thd) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index bcf81a49f56..90fdbb45024 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -933,6 +933,85 @@ int check_signed_flag(partition_info *part_info) return error; } +/** + Initialize lex object for use in fix_fields and parsing. + + SYNOPSIS + init_lex_with_single_table() + @param thd The thread object + @param table The table object + @return Operation status + @retval TRUE An error occurred, memory allocation error + @retval FALSE Ok + + DESCRIPTION + This function is used to initialize a lex object on the + stack for use by fix_fields and for parsing. In order to + work properly it also needs to initialize the + Name_resolution_context object of the lexer. + Finally it needs to set a couple of variables to ensure + proper functioning of fix_fields. +*/ + +static int +init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) +{ + TABLE_LIST *table_list; + Table_ident *table_ident; + SELECT_LEX *select_lex= &lex->select_lex; + Name_resolution_context *context= &select_lex->context; + /* + We will call the parser to create a part_info struct based on the + partition string stored in the frm file. + We will use a local lex object for this purpose. However we also + need to set the Name_resolution_object for this lex object. We + do this by using add_table_to_list where we add the table that + we're working with to the Name_resolution_context. + */ + thd->lex= lex; + lex_start(thd); + context->init(); + if ((!(table_ident= new Table_ident(thd, + table->s->table_name, + table->s->db, TRUE))) || + (!(table_list= select_lex->add_table_to_list(thd, + table_ident, + NULL, + 0)))) + return TRUE; + context->resolve_in_table_list_only(table_list); + lex->use_only_table_context= TRUE; + select_lex->cur_pos_in_select_list= UNDEF_POS; + table->map= 1; //To ensure correct calculation of const item + table->get_fields_in_item_tree= TRUE; + table_list->table= table; + return FALSE; +} + +/** + End use of local lex with single table + + SYNOPSIS + end_lex_with_single_table() + @param thd The thread object + @param table The table object + @param old_lex The real lex object connected to THD + + DESCRIPTION + This function restores the real lex object after calling + init_lex_with_single_table and also restores some table + variables temporarily set. +*/ + +static void +end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex) +{ + LEX *lex= thd->lex; + table->map= 0; + table->get_fields_in_item_tree= FALSE; + lex_end(lex); + thd->lex= old_lex; +} /* The function uses a new feature in fix_fields where the flag @@ -973,55 +1052,18 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, bool is_sub_part) { partition_info *part_info= table->part_info; - uint dir_length, home_dir_length; bool result= TRUE; - TABLE_LIST tables; - TABLE_LIST *save_table_list, *save_first_table, *save_last_table; int error; - Name_resolution_context *context; const char *save_where; - char* db_name; - char db_name_string[FN_REFLEN]; - bool save_use_only_table_context; + LEX *old_lex= thd->lex; + LEX lex; DBUG_ENTER("fix_fields_part_func"); - /* - Set-up the TABLE_LIST object to be a list with a single table - Set the object to zero to create NULL pointers and set alias - and real name to table name and get database name from file name. - TODO: Consider generalizing or refactoring Lex::add_table_to_list() so - it can be used in all places where we create TABLE_LIST objects. - Also consider creating appropriate constructors for TABLE_LIST. - */ - - bzero((void*)&tables, sizeof(TABLE_LIST)); - tables.alias= tables.table_name= (char*) table->s->table_name.str; - tables.table= table; - tables.next_local= 0; - tables.next_name_resolution_table= 0; - /* - Cache the table in Item_fields. All the tables can be cached except - the trigger pseudo table. - */ - tables.cacheable_table= TRUE; - context= thd->lex->current_context(); - tables.select_lex= context->select_lex; - strmov(db_name_string, table->s->normalized_path.str); - dir_length= dirname_length(db_name_string); - db_name_string[dir_length - 1]= 0; - home_dir_length= dirname_length(db_name_string); - db_name= &db_name_string[home_dir_length]; - tables.db= db_name; + if (init_lex_with_single_table(thd, table, &lex)) + goto end; - table->map= 1; //To ensure correct calculation of const item - table->get_fields_in_item_tree= TRUE; - save_table_list= context->table_list; - save_first_table= context->first_name_resolution_table; - save_last_table= context->last_name_resolution_table; - context->table_list= &tables; - context->first_name_resolution_table= &tables; - context->last_name_resolution_table= NULL; - func_expr->walk(&Item::change_context_processor, 0, (uchar*) context); + func_expr->walk(&Item::change_context_processor, 0, + (uchar*) &lex.select_lex.context); save_where= thd->where; thd->where= "partition function"; /* @@ -1036,30 +1078,18 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, that does this during val_int must be disallowed as partition function. SEE Bug #21658 - */ - /* + This is a tricky call to prepare for since it can have a large number of interesting side effects, both desirable and undesirable. */ - - save_use_only_table_context= thd->lex->use_only_table_context; - thd->lex->use_only_table_context= TRUE; - thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; - error= func_expr->fix_fields(thd, (Item**)&func_expr); - thd->lex->use_only_table_context= save_use_only_table_context; - - context->table_list= save_table_list; - context->first_name_resolution_table= save_first_table; - context->last_name_resolution_table= save_last_table; if (unlikely(error)) { DBUG_PRINT("info", ("Field in partition function not part of table")); clear_field_flag(table); goto end; } - thd->where= save_where; if (unlikely(func_expr->const_item())) { my_error(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0)); @@ -1070,8 +1100,11 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, goto end; result= set_up_field_array(table, is_sub_part); end: - table->get_fields_in_item_tree= FALSE; - table->map= 0; //Restore old value + end_lex_with_single_table(thd, table, old_lex); +#if !defined(DBUG_OFF) + func_expr->walk(&Item::change_context_processor, 0, + (uchar*) 0); +#endif DBUG_RETURN(result); } @@ -4109,26 +4142,13 @@ bool mysql_unpack_partition(THD *thd, LEX lex; DBUG_ENTER("mysql_unpack_partition"); - thd->lex= &lex; thd->variables.character_set_client= system_charset_info; Parser_state parser_state(thd, part_buf, part_info_len); - lex_start(thd); - *work_part_info_used= false; - /* - We need to use the current SELECT_LEX since I need to keep the - Name_resolution_context object which is referenced from the - Item_field objects. - This is not a nice solution since if the parser uses current_select - for anything else it will corrupt the current LEX object. - Also, we need to make sure there even is a select -- if the statement - was a "USE ...", current_select will be NULL, but we may still end up - here if we try to log to a partitioned table. This is currently - unsupported, but should still fail rather than crash! - */ - if (!(thd->lex->current_select= old_lex->current_select)) + if (init_lex_with_single_table(thd, table, &lex)) goto end; + /* All Items created is put into a free list on the THD object. This list is used to free all Item objects after completing a query. We don't @@ -4138,6 +4158,7 @@ bool mysql_unpack_partition(THD *thd, Thus we move away the current list temporarily and start a new list that we then save in the partition info structure. */ + *work_part_info_used= FALSE; lex.part_info= new partition_info();/* Indicates MYSQLparse from this place */ if (!lex.part_info) { @@ -4234,8 +4255,7 @@ bool mysql_unpack_partition(THD *thd, result= FALSE; end: - lex_end(thd->lex); - thd->lex= old_lex; + end_lex_with_single_table(thd, table, old_lex); thd->variables.character_set_client= old_character_set_client; DBUG_RETURN(result); } diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 7dd7dd0d4b9..c5449a1dde4 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1359,7 +1359,6 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) } new_thd->thread_stack= (char*) &tables; new_thd->store_globals(); - lex_start(new_thd); new_thd->db= my_strdup("mysql", MYF(0)); new_thd->db_length= 5; tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3810eed2d2c..d51f72da9bf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1076,6 +1076,10 @@ JOIN::optimize() { conds=new Item_int((longlong) 0,1); // Always false } + + /* Cache constant expressions in WHERE, HAVING, ON clauses. */ + cache_const_exprs(); + if (make_join_select(this, select, conds)) { zero_result_cause= @@ -17149,5 +17153,40 @@ bool JOIN::change_result(select_result *res) } /** + Cache constant expressions in WHERE, HAVING, ON conditions. +*/ + +void JOIN::cache_const_exprs() +{ + bool cache_flag= FALSE; + bool *analyzer_arg= &cache_flag; + + /* No need in cache if all tables are constant. */ + if (const_tables == tables) + return; + + if (conds) + conds->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg, + &Item::cache_const_expr_transformer, (uchar *)&cache_flag); + cache_flag= FALSE; + if (having) + having->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg, + &Item::cache_const_expr_transformer, (uchar *)&cache_flag); + + for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++) + { + if (*tab->on_expr_ref) + { + cache_flag= FALSE; + (*tab->on_expr_ref)->compile(&Item::cache_const_expr_analyzer, + (uchar **)&analyzer_arg, + &Item::cache_const_expr_transformer, + (uchar *)&cache_flag); + } + } +} + + +/** @} (end of group Query_Optimizer) */ diff --git a/sql/sql_select.h b/sql/sql_select.h index e049e4ed765..bdca4b196bc 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -555,6 +555,7 @@ public: return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex)); } + void cache_const_exprs(); private: /** TRUE if the query contains an aggregate function but has no GROUP diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 8fafbbd8e70..464a70e4175 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -141,7 +141,6 @@ bool servers_init(bool dont_read_servers_table) DBUG_RETURN(TRUE); thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); /* It is safe to call servers_reload() since servers_* arrays and hashes which will be freed there are global static objects and thus are initialized diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 3907aa6a5ae..72b6e483d35 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -55,19 +55,18 @@ static const char *lock_descriptions[] = void print_where(COND *cond,const char *info, enum_query_type query_type) { + char buff[256]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + str.length(0); if (cond) - { - char buff[256]; - String str(buff,(uint32) sizeof(buff), system_charset_info); - str.length(0); cond->print(&str, query_type); - str.append('\0'); - DBUG_LOCK_FILE; - (void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info); - (void) fputs(str.ptr(),DBUG_FILE); - (void) fputc('\n',DBUG_FILE); - DBUG_UNLOCK_FILE; - } + str.append('\0'); + + DBUG_LOCK_FILE; + (void) fprintf(DBUG_FILE,"\nWHERE:(%s) %p ", info, cond); + (void) fputs(str.ptr(),DBUG_FILE); + (void) fputc('\n',DBUG_FILE); + DBUG_UNLOCK_FILE; } /* This is for debugging purposes */ diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index d27473c1959..81a7272a76b 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -135,7 +135,6 @@ void udf_init() initialized = 1; new_thd->thread_stack= (char*) &new_thd; new_thd->store_globals(); - lex_start(new_thd); new_thd->set_db(db, sizeof(db)-1); tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 17ac10ebfb9..51f80c589b2 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -818,7 +818,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, thd->variables.sql_mode|= sql_mode; } - DBUG_PRINT("info", ("View: %s", view_query.ptr())); + DBUG_PRINT("info", + ("View: %*.s", (int) view_query.length(), view_query.ptr())); /* fill structure */ view->source= thd->lex->create_view_select; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 738745b918c..886f961a515 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -466,6 +466,90 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, DBUG_RETURN(result); } +/** + @brief Creates a new SELECT_LEX for a UNION branch. + + Sets up and initializes a SELECT_LEX structure for a query once the parser + discovers a UNION token. The current SELECT_LEX is pushed on the stack and + the new SELECT_LEX becomes the current one. + + @param lex The parser state. + + @param is_union_distinct True if the union preceding the new select statement + uses UNION DISTINCT. + + @param is_top_level This should be @c TRUE if the newly created SELECT_LEX + is a non-nested statement. + + @return <code>false</code> if successful, <code>true</code> if an error was + reported. In the latter case parsing should stop. + */ +bool add_select_to_union_list(LEX *lex, bool is_union_distinct, + bool is_top_level) +{ + /* + Only the last SELECT can have INTO. Since the grammar won't allow INTO in + a nested SELECT, we make this check only when creating a top-level SELECT. + */ + if (is_top_level && lex->result) + { + my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); + return TRUE; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + return TRUE; + } + /* This counter shouldn't be incremented for UNION parts */ + lex->nest_level--; + if (mysql_new_select(lex, 0)) + return TRUE; + mysql_init_select(lex); + lex->current_select->linkage=UNION_TYPE; + if (is_union_distinct) /* UNION DISTINCT - remember position */ + lex->current_select->master_unit()->union_distinct= + lex->current_select; + return FALSE; +} + +/** + @brief Initializes a SELECT_LEX for a query within parentheses (aka + braces). + + @return false if successful, true if an error was reported. In the latter + case parsing should stop. + */ +bool setup_select_in_parentheses(LEX *lex) +{ + SELECT_LEX * sel= lex->current_select; + if (sel->set_braces(1)) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + return TRUE; + } + if (sel->linkage == UNION_TYPE && + !sel->master_unit()->first_select()->braces && + sel->master_unit()->first_select()->linkage == + UNION_TYPE) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + return TRUE; + } + if (sel->linkage == UNION_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE && + sel->master_unit()->fake_select_lex) + { + my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY"); + return TRUE; + } + /* select in braces, can't contain global parameters */ + if (sel->master_unit()->fake_select_lex) + sel->master_unit()->global_parameters= + sel->master_unit()->fake_select_lex; + return FALSE; +} + %} %union { int num; @@ -519,10 +603,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 167 shift/reduce conflicts. + Currently there are 173 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 167 +%expect 173 /* Comments for TOKENS. @@ -1234,6 +1318,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); join_table_list join_table table_factor table_ref esc_table_ref select_derived derived_table_list + select_derived_union %type <date_time_type> date_time_type; %type <interval> interval @@ -1269,8 +1354,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <variable> internal_variable_name -%type <select_lex> subselect take_first_select - get_select_lex +%type <select_lex> subselect + get_select_lex query_specification + query_expression_body %type <boolfunc2creator> comp_op @@ -4747,7 +4833,8 @@ create_table_option: ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; - Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + if ($3) + Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } | MAX_ROWS opt_equal ulonglong_num { @@ -6822,37 +6909,22 @@ select_init: select_paren: SELECT_SYM select_part2 { - LEX *lex= Lex; - SELECT_LEX * sel= lex->current_select; - if (sel->set_braces(1)) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); + if (setup_select_in_parentheses(Lex)) MYSQL_YYABORT; - } - if (sel->linkage == UNION_TYPE && - !sel->master_unit()->first_select()->braces && - sel->master_unit()->first_select()->linkage == - UNION_TYPE) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - if (sel->linkage == UNION_TYPE && - sel->olap != UNSPECIFIED_OLAP_TYPE && - sel->master_unit()->fake_select_lex) - { - my_error(ER_WRONG_USAGE, MYF(0), - "CUBE/ROLLUP", "ORDER BY"); - MYSQL_YYABORT; - } - /* select in braces, can't contain global parameters */ - if (sel->master_unit()->fake_select_lex) - sel->master_unit()->global_parameters= - sel->master_unit()->fake_select_lex; } | '(' select_paren ')' ; +/* The equivalent of select_paren for nested queries. */ +select_paren_derived: + SELECT_SYM select_part2_derived + { + if (setup_select_in_parentheses(Lex)) + MYSQL_YYABORT; + } + | '(' select_paren_derived ')' + ; + select_init2: select_part2 { @@ -8693,6 +8765,7 @@ when_list: } ; +/* Equivalent to <table reference> in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ table_ref: table_factor { $$=$1; } @@ -8720,6 +8793,7 @@ esc_table_ref: | '{' ident table_ref '}' { $$=$3; } ; +/* Equivalent to <table reference list> in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ derived_table_list: esc_table_ref { $$=$1; } @@ -8873,6 +8947,13 @@ normal_join: | CROSS JOIN_SYM {} ; +/* + This is a flattening of the rules <table factor> and <table primary> + in the SQL:2003 standard, since we don't have <sample clause> + + I.e. + <table factor> ::= <table primary> [ <sample clause> ] +*/ /* Warning - may return NULL in case of incomplete SELECT */ table_factor: { @@ -8910,12 +8991,29 @@ table_factor: /* incomplete derived tables return NULL, we must be nested in select_derived rule to be here. */ } - | '(' get_select_lex select_derived union_opt ')' opt_table_alias + /* + Represents a flattening of the following rules from the SQL:2003 + standard. This sub-rule corresponds to the sub-rule + <table primary> ::= ... | <derived table> [ AS ] <correlation name> + + The following rules have been flattened into query_expression_body + (since we have no <with clause>). + + <derived table> ::= <table subquery> + <table subquery> ::= <subquery> + <subquery> ::= <left paren> <query expression> <right paren> + <query expression> ::= [ <with clause> ] <query expression body> + + For the time being we use the non-standard rule + select_derived_union which is a compromise between the standard + and our parser. Possibly this rule could be replaced by our + query_expression_body. + */ + | '(' get_select_lex select_derived_union ')' opt_table_alias { /* Use $2 instead of Lex->current_select as derived table will alter value of Lex->current_select. */ - - if (!($3 || $6) && $2->embedding && + if (!($3 || $5) && $2->embedding && !$2->embedding->nested_join->join_list.elements) { /* we have a derived table ($3 == NULL) but no alias, @@ -8937,7 +9035,7 @@ table_factor: if (ti == NULL) MYSQL_YYABORT; if (!($$= sel->add_table_to_list(lex->thd, - ti, $6, 0, + new Table_ident(unit), $5, 0, TL_READ))) MYSQL_YYABORT; @@ -8945,7 +9043,8 @@ table_factor: lex->pop_context(); lex->nest_level--; } - else if ($4 || $6) + else if ($3->select_lex && + $3->select_lex->master_unit()->is_union() || $5) { /* simple nested joins cannot have aliases or unions */ my_parse_error(ER(ER_SYNTAX_ERROR)); @@ -8960,6 +9059,62 @@ table_factor: } ; +select_derived_union: + select_derived opt_order_clause opt_limit_clause + | select_derived_union + UNION_SYM + union_option + { + if (add_select_to_union_list(Lex, (bool)$3, FALSE)) + MYSQL_YYABORT; + } + query_specification + { + /* + Remove from the name resolution context stack the context of the + last select in the union. + */ + Lex->pop_context(); + } + opt_order_clause opt_limit_clause + ; + +/* The equivalent of select_init2 for nested queries. */ +select_init2_derived: + select_part2_derived + { + LEX *lex= Lex; + SELECT_LEX * sel= lex->current_select; + if (lex->current_select->set_braces(0)) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + if (sel->linkage == UNION_TYPE && + sel->master_unit()->first_select()->braces) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + } + ; + +/* The equivalent of select_part2 for nested queries. */ +select_part2_derived: + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + opt_select_from select_lock_type + ; + /* handle contents of parentheses in join expression */ select_derived: get_select_lex @@ -9574,8 +9729,7 @@ procedure_item: select_var_list_init: { LEX *lex=Lex; - if (!lex->describe && - (!(lex->result= new select_dumpvar(lex->nest_level)))) + if (!lex->describe && (!(lex->result= new select_dumpvar()))) MYSQL_YYABORT; } select_var_list @@ -9656,7 +9810,7 @@ into_destination: LEX *lex= Lex; lex->uncacheable(UNCACHEABLE_SIDEEFFECT); if (!(lex->exchange= new sql_exchange($2.str, 0)) || - !(lex->result= new select_export(lex->exchange, lex->nest_level))) + !(lex->result= new select_export(lex->exchange))) MYSQL_YYABORT; } opt_load_data_charset @@ -9670,7 +9824,7 @@ into_destination: lex->uncacheable(UNCACHEABLE_SIDEEFFECT); if (!(lex->exchange= new sql_exchange($2.str,1))) MYSQL_YYABORT; - if (!(lex->result= new select_dump(lex->exchange, lex->nest_level))) + if (!(lex->result= new select_dump(lex->exchange))) MYSQL_YYABORT; } } @@ -13220,33 +13374,8 @@ union_clause: union_list: UNION_SYM union_option { - LEX *lex=Lex; - if (lex->result && - (lex->result->get_nest_level() == -1 || - lex->result->get_nest_level() == lex->nest_level)) - { - /* - Only the last SELECT can have INTO unless the INTO and UNION - are at different nest levels. In version 5.1 and above, INTO - will onle be allowed at top level. - */ - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); - MYSQL_YYABORT; - } - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - /* This counter shouldn't be incremented for UNION parts */ - Lex->nest_level--; - if (mysql_new_select(lex, 0)) + if (add_select_to_union_list(Lex, (bool)$2, TRUE)) MYSQL_YYABORT; - mysql_init_select(lex); - lex->current_select->linkage=UNION_TYPE; - if ($2) /* UNION DISTINCT - remember position */ - lex->current_select->master_unit()->union_distinct= - lex->current_select; } select_init { @@ -13299,22 +13428,39 @@ union_option: | ALL { $$=0; } ; -take_first_select: /* empty */ - { - $$= Lex->current_select->master_unit()->first_select(); - }; +query_specification: + SELECT_SYM select_init2_derived + { + $$= Lex->current_select->master_unit()->first_select(); + } + | '(' select_paren_derived ')' + { + $$= Lex->current_select->master_unit()->first_select(); + } + ; +query_expression_body: + query_specification + | query_expression_body + UNION_SYM union_option + { + if (add_select_to_union_list(Lex, (bool)$3, FALSE)) + MYSQL_YYABORT; + } + query_specification + { + Lex->pop_context(); + $$= $1; + } + ; + +/* Corresponds to <query expression> in the SQL:2003 standard. */ subselect: - SELECT_SYM subselect_start select_init2 take_first_select - subselect_end - { - $$= $4; - } - | '(' subselect_start select_paren take_first_select - subselect_end ')' - { - $$= $4; - }; + subselect_start query_expression_body subselect_end + { + $$= $2; + } + ; subselect_start: { diff --git a/sql/table.cc b/sql/table.cc index aef836c330e..74add641544 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1640,9 +1640,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, share->table_name.str, (long) outparam)); - /* Parsing of partitioning information from .frm needs thd->lex set up. */ - DBUG_ASSERT(thd->lex->is_lex_started); - error= 1; bzero((char*) outparam, sizeof(*outparam)); outparam->in_use= thd; diff --git a/sql/tztime.cc b/sql/tztime.cc index c17b37e27fb..2ec641071ee 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1578,7 +1578,6 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) DBUG_RETURN(1); thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); /* Init all memory structures that require explicit destruction */ if (my_hash_init(&tz_names, &my_charset_latin1, 20, |