diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_strfunc.cc | 14 | ||||
-rw-r--r-- | sql/item_strfunc.h | 1 | ||||
-rw-r--r-- | sql/item_sum.cc | 4 | ||||
-rw-r--r-- | sql/item_sum.h | 2 | ||||
-rw-r--r-- | sql/lock.cc | 56 | ||||
-rw-r--r-- | sql/sql_lex.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 103 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_union.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 24 |
10 files changed, 163 insertions, 46 deletions
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index a3e47154bc3..60183ac9b5a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -80,6 +80,20 @@ String *Item_str_func::check_well_formed_result(String *str) } +my_decimal *Item_str_func::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + char buff[64]; + String *res, tmp(buff,sizeof(buff), &my_charset_bin); + res= val_str(&tmp); + if (!res) + return 0; + (void)str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(), + res->length(), res->charset(), decimal_value); + return decimal_value; +} + + double Item_str_func::val_real() { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 6a95a9e5d1f..7d7b62df0dc 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -33,6 +33,7 @@ public: Item_str_func(List<Item> &list) :Item_func(list) {decimals=NOT_FIXED_DEC; } longlong val_int(); double val_real(); + my_decimal *val_decimal(my_decimal *); enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); String *check_well_formed_result(String *str); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 8826144266e..962454e237e 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -377,12 +377,12 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, case INT_RESULT: return new Field_longlong(max_length,maybe_null,name,table,unsigned_flag); case STRING_RESULT: - if (max_length > 255 && convert_blob_length) + if (max_length/collation.collation->mbmaxlen > 255 && convert_blob_length) return new Field_varstring(convert_blob_length, maybe_null, name, table, collation.collation); return make_string_field(table); - case DECIMAL_RESULT: +case DECIMAL_RESULT: return new Field_new_decimal(max_length, maybe_null, name, table, decimals, unsigned_flag); case ROW_RESULT: diff --git a/sql/item_sum.h b/sql/item_sum.h index 328f8dd8400..f4ff257aa4e 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1121,7 +1121,7 @@ public: virtual Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { - if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) + if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB ) return FIELD_TYPE_BLOB; else return MYSQL_TYPE_VARCHAR; diff --git a/sql/lock.cc b/sql/lock.cc index a584b063a0b..8ea13b2117c 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -78,6 +78,7 @@ extern HASH open_cache; static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, uint flags, TABLE **write_locked); +static void reset_lock_data(MYSQL_LOCK *sql_lock); static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error, const char *); @@ -135,12 +136,16 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, */ if (wait_if_global_read_lock(thd, 1, 1)) { + /* Clear the lock type of all lock data to avoid reusage. */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; break; - } + } if (thd->version != refresh_version) { + /* Clear the lock type of all lock data to avoid reusage. */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); goto retry; } @@ -149,6 +154,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, thd->proc_info="System lock"; if (lock_external(thd, tables, count)) { + /* Clear the lock type of all lock data to avoid reusage. */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; break; @@ -699,7 +706,10 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, *write_lock_used=table; if (table->db_stat & HA_READ_ONLY) { - my_error(ER_OPEN_AS_READONLY, MYF(0), table->alias); + my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias); + /* Clear the lock type of the lock data that are stored already. */ + sql_lock->lock_count= locks - sql_lock->locks; + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); DBUG_RETURN(0); } @@ -724,6 +734,48 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, } +/* + Reset lock type in lock data. + + SYNOPSIS + reset_lock_data() + sql_lock The MySQL lock. + + DESCRIPTION + + After a locking error we want to quit the locking of the table(s). + The test case in the bug report for Bug #18544 has the following + cases: 1. Locking error in lock_external() due to InnoDB timeout. + 2. Locking error in get_lock_data() due to missing write permission. + 3. Locking error in wait_if_global_read_lock() due to lock conflict. + + In all these cases we have already set the lock type into the lock + data of the open table(s). If the table(s) are in the open table + cache, they could be reused with the non-zero lock type set. This + could lead to ignoring a different lock type with the next lock. + + Clear the lock type of all lock data. This ensures that the next + lock request will set its lock type properly. + + RETURN + void +*/ + +static void reset_lock_data(MYSQL_LOCK *sql_lock) +{ + THR_LOCK_DATA **ldata; + THR_LOCK_DATA **ldata_end; + + for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count; + ldata < ldata_end; + ldata++) + { + /* Reset lock type. */ + (*ldata)->type= TL_UNLOCK; + } +} + + /***************************************************************************** Lock table based on the name. This is used when we need total access to a closed, not open table diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8512c075f69..34e7ee969b6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -454,6 +454,7 @@ public: void print(String *str); + bool add_fake_select_lex(THD *thd); void init_prepare_fake_select_lex(THD *thd); inline bool is_prepared() { return prepared; } bool change_result(select_subselect *result, select_subselect *old_result); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 38eadb5a738..c58628cd856 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5544,48 +5544,17 @@ mysql_new_select(LEX *lex, bool move_down) } else { - Name_resolution_context *outer_context; if (lex->current_select->order_list.first && !lex->current_select->braces) { my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY"); DBUG_RETURN(1); } select_lex->include_neighbour(lex->current_select); - /* - we are not sure that we have one level of SELECTs above, so we take - outer_context address from first select of unit - */ - outer_context= - select_lex->master_unit()->first_select()->context.outer_context; - SELECT_LEX_UNIT *unit= select_lex->master_unit(); - SELECT_LEX *fake= unit->fake_select_lex; - if (!fake) - { - /* - as far as we included SELECT_LEX for UNION unit should have - fake SELECT_LEX for UNION processing - */ - if (!(fake= unit->fake_select_lex= new (thd->mem_root) SELECT_LEX())) - DBUG_RETURN(1); - fake->include_standalone(unit, - (SELECT_LEX_NODE**)&unit->fake_select_lex); - fake->select_number= INT_MAX; - fake->parent_lex= lex; /* Used in init_query. */ - fake->make_empty_select(); - fake->linkage= GLOBAL_OPTIONS_TYPE; - fake->select_limit= 0; - - fake->context.outer_context= outer_context; - /* allow item list resolving in fake select for ORDER BY */ - fake->context.resolve_in_select_list= TRUE; - fake->context.select_lex= fake; - /* - Remove the name resolution context of the fake select from the - context stack. - */ - lex->pop_context(); - } - select_lex->context.outer_context= outer_context; + SELECT_LEX_UNIT *unit= select_lex->master_unit(); + if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd)) + DBUG_RETURN(1); + select_lex->context.outer_context= + unit->first_select()->context.outer_context; } select_lex->master_unit()->global_parameters= select_lex; @@ -6358,6 +6327,68 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) /* + Create a fake SELECT_LEX for a unit + + SYNOPSIS: + add_fake_select_lex() + thd thread handle + + DESCRIPTION + The method create a fake SELECT_LEX object for a unit. + This object is created for any union construct containing a union + operation and also for any single select union construct of the form + (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... + or of the form + (SELECT ... ORDER BY LIMIT n) ORDER BY ... + + NOTES + The object is used to retrieve rows from the temporary table + where the result on the union is obtained. + + RETURN VALUES + 1 on failure to create the object + 0 on success +*/ + +bool st_select_lex_unit::add_fake_select_lex(THD *thd) +{ + SELECT_LEX *first_sl= first_select(); + DBUG_ENTER("add_fake_select_lex"); + DBUG_ASSERT(!fake_select_lex); + + if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX())) + DBUG_RETURN(1); + fake_select_lex->include_standalone(this, + (SELECT_LEX_NODE**)&fake_select_lex); + fake_select_lex->select_number= INT_MAX; + fake_select_lex->parent_lex= thd->lex; /* Used in init_query. */ + fake_select_lex->make_empty_select(); + fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE; + fake_select_lex->select_limit= 0; + + fake_select_lex->context.outer_context=first_sl->context.outer_context; + /* allow item list resolving in fake select for ORDER BY */ + fake_select_lex->context.resolve_in_select_list= TRUE; + fake_select_lex->context.select_lex= fake_select_lex; + + if (!first_sl->next_select()) + { + /* + This works only for + (SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m], + (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m] + just before the parser starts processing order_list + */ + global_parameters= fake_select_lex; + fake_select_lex->no_table_names_allowed= 1; + thd->lex->current_select= fake_select_lex; + } + thd->lex->pop_context(); + DBUG_RETURN(0); +} + + +/* Push a new name resolution context for a JOIN ... ON clause to the context stack of a query block. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0211539e784..47c5281b258 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -224,7 +224,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, register SELECT_LEX *select_lex = &lex->select_lex; DBUG_ENTER("handle_select"); - if (select_lex->next_select()) + if (select_lex->next_select() || select_lex->master_unit()->fake_select_lex) res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option); else { diff --git a/sql/sql_union.cc b/sql/sql_union.cc index dee88af7d83..c5af81ae55a 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -202,7 +202,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_sl; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; - is_union= test(first_sl->next_select()); + is_union= first_sl->next_select() || fake_select_lex; /* Global option */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c3d9cb5913b..6473163a6ec 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5609,14 +5609,32 @@ order_clause: ORDER_SYM BY { LEX *lex=Lex; - if (lex->current_select->linkage != GLOBAL_OPTIONS_TYPE && - lex->current_select->olap != - UNSPECIFIED_OLAP_TYPE) + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE) { my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY"); YYABORT; } + if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!first_sl->next_select() && + (first_sl->order_list.elements || + first_sl->select_limit) && + unit->add_fake_select_lex(lex->thd)) + YYABORT; + } } order_list; order_list: |