diff options
author | Nirbhay Choubey <nirbhay@mariadb.com> | 2015-10-31 18:07:02 -0400 |
---|---|---|
committer | Nirbhay Choubey <nirbhay@mariadb.com> | 2015-10-31 18:07:02 -0400 |
commit | 4d1511296288782df0e3d9373396724e250b24c1 (patch) | |
tree | 9fa47d141b71933859d6c2da585e5bb5e52db212 /sql | |
parent | 17b0b45b1de41a1b188c5de6c3e9d8e6ecc48a72 (diff) | |
parent | d775ecdd010daad4dc6147fba58acd006bf2c60c (diff) | |
download | mariadb-git-4d1511296288782df0e3d9373396724e250b24c1.tar.gz |
Merge tag 'mariadb-10.0.22' into 10.0-galera
Diffstat (limited to 'sql')
73 files changed, 1118 insertions, 458 deletions
diff --git a/sql/events.cc b/sql/events.cc index 1feb285e3a0..e23dcd21954 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -80,7 +80,8 @@ Event_queue *Events::event_queue; Event_scheduler *Events::scheduler; Event_db_repository *Events::db_repository; ulong Events::opt_event_scheduler= Events::EVENTS_OFF; -bool Events::check_system_tables_error= FALSE; +ulong Events::startup_state= Events::EVENTS_OFF; +ulong Events::inited; /* @@ -114,7 +115,7 @@ bool Events::check_if_system_tables_error() { DBUG_ENTER("Events::check_if_system_tables_error"); - if (check_system_tables_error) + if (!inited) { my_error(ER_EVENTS_DB_ERROR, MYF(0)); DBUG_RETURN(TRUE); @@ -257,10 +258,10 @@ common_1_lev_code: /** - Create a new query string for removing executable comments - for avoiding leak and keeping consistency of the execution + Create a new query string for removing executable comments + for avoiding leak and keeping consistency of the execution on master and slave. - + @param[in] thd Thread handler @param[in] buf Query string @@ -281,7 +282,7 @@ create_query_string(THD *thd, String *buf) thd->lex->stmt_definition_end - thd->lex->stmt_definition_begin)) return 1; - + return 0; } @@ -336,8 +337,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, if (parse_data->do_not_create) DBUG_RETURN(FALSE); - /* - Turn off row binlogging of this statement and use statement-based + /* + Turn off row binlogging of this statement and use statement-based so that all supporting tables are updated for CREATE EVENT command. */ save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); @@ -384,8 +385,10 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, String log_query; if (create_query_string(thd, &log_query)) { - sql_print_error("Event Error: An error occurred while creating query " - "string, before writing it into binary log."); + my_message_sql(ER_STARTUP, + "Event Error: An error occurred while creating query " + "string, before writing it into binary log.", + MYF(ME_NOREFRESH)); ret= true; } else @@ -473,8 +476,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, } } - /* - Turn off row binlogging of this statement and use statement-based + /* + Turn off row binlogging of this statement and use statement-based so that all supporting tables are updated for UPDATE EVENT command. */ save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); @@ -752,6 +755,13 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) int ret; DBUG_ENTER("Events::fill_schema_events"); + /* + If we didn't start events because of --skip-grant-tables, return an + empty set + */ + if (opt_noacl) + DBUG_RETURN(0); + if (check_if_system_tables_error()) DBUG_RETURN(1); @@ -780,6 +790,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) /** Initializes the scheduler's structures. + @param THD or null (if called by init) @param opt_noacl_or_bootstrap TRUE if there is --skip-grant-tables or --bootstrap option. In that case we disable the event scheduler. @@ -787,44 +798,56 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) @note This function is not synchronized. @retval FALSE Perhaps there was an error, and the event scheduler - is disabled. But the error is not fatal and the + is disabled. But the error is not fatal and the server start up can continue. @retval TRUE Fatal error. Startup must terminate (call unireg_abort()). */ bool -Events::init(bool opt_noacl_or_bootstrap) +Events::init(THD *thd, bool opt_noacl_or_bootstrap) { - - THD *thd; int err_no; bool res= FALSE; - + bool had_thd= thd != 0; DBUG_ENTER("Events::init"); + DBUG_ASSERT(inited == 0); + + /* + Was disabled explicitly from the command line + */ + if (opt_event_scheduler == Events::EVENTS_DISABLED || + opt_noacl_or_bootstrap) + DBUG_RETURN(FALSE); + /* We need a temporary THD during boot */ - if (!(thd= new THD())) + if (!thd) { - res= TRUE; - goto end; + + if (!(thd= new THD())) + { + res= TRUE; + goto end; + } + /* + The thread stack does not start from this function but we cannot + guess the real value. So better some value that doesn't assert than + no value. + */ + thd->thread_stack= (char*) &thd; + thd->store_globals(); + /* + Set current time for the thread that handles events. + Current time is stored in data member start_time of THD class. + Subsequently, this value is used to check whether event was expired + when make loading events from storage. Check for event expiration time + is done at Event_queue_element::compute_next_execution_time() where + event's status set to Event_parse_data::DISABLED and dropped flag set + to true if event was expired. + */ + thd->set_time(); } - /* - The thread stack does not start from this function but we cannot - guess the real value. So better some value that doesn't assert than - no value. - */ - thd->thread_stack= (char*) &thd; - thd->store_globals(); - /* - Set current time for the thread that handles events. - Current time is stored in data member start_time of THD class. - Subsequently, this value is used to check whether event was expired - when make loading events from storage. Check for event expiration time - is done at Event_queue_element::compute_next_execution_time() where - event's status set to Event_parse_data::DISABLED and dropped flag set - to true if event was expired. - */ - thd->set_time(); + /* We will need Event_db_repository anyway, even if the scheduler is disabled - to perform events DDL. @@ -844,28 +867,19 @@ Events::init(bool opt_noacl_or_bootstrap) are most likely not there and we're going to disable the event scheduler anyway. */ - if (opt_noacl_or_bootstrap || Event_db_repository::check_system_tables(thd)) + if (Event_db_repository::check_system_tables(thd)) { - if (! opt_noacl_or_bootstrap) - { - sql_print_error("Event Scheduler: An error occurred when initializing " - "system tables. Disabling the Event Scheduler."); - check_system_tables_error= TRUE; - } - + delete db_repository; + db_repository= 0; + my_message(ER_STARTUP, + "Event Scheduler: An error occurred when initializing " + "system tables. Disabling the Event Scheduler.", + MYF(ME_NOREFRESH)); /* Disable the scheduler since the system tables are not up to date */ - opt_event_scheduler= EVENTS_DISABLED; + opt_event_scheduler= EVENTS_OFF; goto end; } - /* - Was disabled explicitly from the command line, or because we're running - with --skip-grant-tables, or --bootstrap, or because we have no system - tables. - */ - if (opt_event_scheduler == Events::EVENTS_DISABLED) - goto end; - DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON || opt_event_scheduler == Events::EVENTS_OFF); @@ -880,22 +894,23 @@ Events::init(bool opt_noacl_or_bootstrap) if (event_queue->init_queue(thd) || load_events_from_db(thd) || (opt_event_scheduler == EVENTS_ON && scheduler->start(&err_no))) { - sql_print_error("Event Scheduler: Error while loading from disk."); + my_message_sql(ER_STARTUP, + "Event Scheduler: Error while loading from mysql.event table.", + MYF(ME_NOREFRESH)); res= TRUE; /* fatal error: request unireg_abort */ goto end; } Event_worker_thread::init(db_repository); + inited= 1; end: if (res) + deinit(); + if (!had_thd) { - delete db_repository; - delete event_queue; - delete scheduler; + delete thd; + set_current_thd(0); } - delete thd; - /* Remember that we don't have a THD */ - set_current_thd(0); DBUG_RETURN(res); } @@ -915,17 +930,14 @@ Events::deinit() { DBUG_ENTER("Events::deinit"); - if (opt_event_scheduler != EVENTS_DISABLED) - { - delete scheduler; - scheduler= NULL; /* safety */ - delete event_queue; - event_queue= NULL; /* safety */ - } - + delete scheduler; + scheduler= NULL; /* For restart */ + delete event_queue; + event_queue= NULL; /* For restart */ delete db_repository; - db_repository= NULL; /* safety */ + db_repository= NULL; /* For restart */ + inited= 0; DBUG_VOID_RETURN; } @@ -1028,7 +1040,7 @@ Events::dump_internal_status() holding LOCK_global_system_variables. */ mysql_mutex_lock(&LOCK_global_system_variables); - if (opt_event_scheduler == EVENTS_DISABLED) + if (!inited) puts("The Event Scheduler is disabled"); else { @@ -1042,11 +1054,13 @@ Events::dump_internal_status() bool Events::start(int *err_no) { + DBUG_ASSERT(inited); return scheduler->start(err_no); } bool Events::stop() { + DBUG_ASSERT(inited); return scheduler->stop(); } @@ -1076,7 +1090,6 @@ Events::load_events_from_db(THD *thd) bool ret= TRUE; uint count= 0; ulong saved_master_access; - DBUG_ENTER("Events::load_events_from_db"); DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); @@ -1101,7 +1114,9 @@ Events::load_events_from_db(THD *thd) if (ret) { - sql_print_error("Event Scheduler: Failed to open table mysql.event"); + my_message_sql(ER_STARTUP, + "Event Scheduler: Failed to open table mysql.event", + MYF(ME_NOREFRESH)); DBUG_RETURN(TRUE); } @@ -1123,9 +1138,11 @@ Events::load_events_from_db(THD *thd) if (et->load_from_row(thd, table)) { - sql_print_error("Event Scheduler: " - "Error while loading events from mysql.event. " - "The table probably contains bad data or is corrupted"); + my_message(ER_STARTUP, + "Event Scheduler: " + "Error while loading events from mysql.event. " + "The table probably contains bad data or is corrupted", + MYF(ME_NOREFRESH)); delete et; goto end; } @@ -1176,9 +1193,12 @@ Events::load_events_from_db(THD *thd) } } } - if (global_system_variables.log_warnings) - sql_print_information("Event Scheduler: Loaded %d event%s", - count, (count == 1) ? "" : "s"); + my_printf_error(ER_STARTUP, + "Event Scheduler: Loaded %d event%s", + MYF(ME_NOREFRESH | + (global_system_variables.log_warnings) ? + ME_JUST_INFO: 0), + count, (count == 1) ? "" : "s"); ret= FALSE; end: diff --git a/sql/events.h b/sql/events.h index 646fd257d52..91a0e6f28eb 100644 --- a/sql/events.h +++ b/sql/events.h @@ -79,9 +79,11 @@ public: and the @@global.event_scheduler SQL variable. See sys_var.cc */ - enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED }; + enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED, + EVENTS_ORIGINAL }; /* Protected using LOCK_global_system_variables only. */ - static ulong opt_event_scheduler; + static ulong opt_event_scheduler, startup_state; + static ulong inited; static bool check_if_system_tables_error(); static bool start(int *err_no); static bool stop(); @@ -91,8 +93,7 @@ public: static Event_db_repository * get_db_repository() { return db_repository; } - static bool - init(bool opt_noacl); + static bool init(THD *thd, bool opt_noacl); static void deinit(); @@ -130,6 +131,11 @@ public: static void dump_internal_status(); + static void set_original_state(ulong startup_state_org) + { + startup_state= startup_state_org; + } + private: static bool @@ -139,8 +145,6 @@ private: static Event_queue *event_queue; static Event_scheduler *scheduler; static Event_db_repository *db_repository; - /* Set to TRUE if an error at start up */ - static bool check_system_tables_error; private: /* Prevent use of these */ diff --git a/sql/field.h b/sql/field.h index 4e3a9f4c7b1..c64d6bc443c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,6 +1,6 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify @@ -977,6 +977,16 @@ public: /* Hash value */ virtual void hash(ulong *nr, ulong *nr2); +/** + Checks whether a string field is part of write_set. + + @return + FALSE - If field is not char/varchar/.... + - If field is char/varchar/.. and is not part of write set. + TRUE - If field is char/varchar/.. and is part of write set. +*/ + virtual bool is_updatable() const { return FALSE; } + /* Check whether the field can be used as a join attribute in hash join */ virtual bool hash_join_is_possible() { return TRUE; } virtual bool eq_cmp_as_binary() { return TRUE; } @@ -1174,6 +1184,11 @@ public: int store_decimal(const my_decimal *d); uint32 max_data_length() const; + bool is_updatable() const + { + DBUG_ASSERT(table && table->write_set); + return bitmap_is_set(table->write_set, field_index); + } bool match_collation_to_optimize_range() const { return true; } }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index e31f7c5f005..79d579b6828 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,7 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2010, 2012, Monty Program Ab - + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -905,15 +904,10 @@ int field_conv_incompatible(Field *to, Field *from) { // Be sure the value is stored Field_blob *blob=(Field_blob*) to; from->val_str(&blob->value); - /* - Copy value if copy_blobs is set, or source is not a string and - we have a pointer to its internal string conversion buffer. - */ - if (to->table->copy_blobs || - (!blob->value.is_alloced() && - from_real_type != MYSQL_TYPE_STRING && - from_real_type != MYSQL_TYPE_VARCHAR)) + + if (!blob->value.is_alloced() && from->is_updatable()) blob->value.copy(); + return blob->store(blob->value.ptr(),blob->value.length(),from->charset()); } if (from_real_type == MYSQL_TYPE_ENUM && diff --git a/sql/filesort.cc b/sql/filesort.cc index a545bb623c0..6ad7bee48c6 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -25,7 +25,6 @@ #include <my_global.h> #include "sql_priv.h" #include "filesort.h" -#include "unireg.h" // REQUIRED by other includes #ifdef HAVE_STDDEF_H #include <stddef.h> /* for macro offsetof */ #endif diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 929acda2716..85c19da9dda 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -5277,6 +5277,7 @@ err: { (void) m_file[j]->ha_index_end(); } + destroy_record_priority_queue(); } DBUG_RETURN(error); } diff --git a/sql/hostname.cc b/sql/hostname.cc index 1879d056623..f08ae247398 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -26,6 +26,7 @@ */ #include <my_global.h> #include "sql_priv.h" +#include "unireg.h" // SPECIAL_NO_HOST_CACHE #include "hostname.h" #include "my_global.h" #ifndef __WIN__ diff --git a/sql/item.cc b/sql/item.cc index 934846c4815..543ea4f0ffb 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -21,7 +21,6 @@ #endif #include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include <mysql.h> #include <m_ctype.h> #include "my_dir.h" @@ -4835,8 +4834,24 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) As this is an outer field it should be added to the list of non aggregated fields of the outer select. */ - marker= select->cur_pos_in_select_list; - select->join->non_agg_fields.push_back(this); + if (select->join) + { + marker= select->cur_pos_in_select_list; + select->join->non_agg_fields.push_back(this); + } + else + { + /* + join is absent if it is upper SELECT_LEX of non-select + command + */ + DBUG_ASSERT(select->master_unit()->outer_select() == NULL && + (thd->lex->sql_command != SQLCOM_SELECT && + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE_MULTI && + thd->lex->sql_command != SQLCOM_INSERT_SELECT && + thd->lex->sql_command != SQLCOM_REPLACE_SELECT)); + } } if (*from_field != view_ref_found) { diff --git a/sql/item.h b/sql/item.h index 353c9e0f6bc..5b7c0e815b4 100644 --- a/sql/item.h +++ b/sql/item.h @@ -25,7 +25,6 @@ #include "sql_priv.h" /* STRING_BUFFER_USUAL_SIZE */ #include "unireg.h" #include "sql_const.h" /* RAND_TABLE_BIT, MAX_FIELD_NAME */ -#include "unireg.h" // REQUIRED: for other includes #include "thr_malloc.h" /* sql_calloc */ #include "field.h" /* Derivation */ @@ -3771,6 +3770,8 @@ public: bool eq(const Item *item, bool binary_cmp) const; Item *get_tmp_table_item(THD *thd) { + if (const_item()) + return copy_or_same(thd); Item *item= Item_ref::get_tmp_table_item(thd); item->name= name; return item; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 90eef1ea55c..830a12ea48d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1466,9 +1466,36 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) bool Item_in_optimizer::fix_left(THD *thd) { DBUG_ENTER("Item_in_optimizer::fix_left"); - if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || - (!cache && !(cache= Item_cache::get_cache(args[0])))) + /* + Here we will store pointer on place of main storage of left expression. + For usual IN (ALL/ANY) it is subquery left_expr. + For other cases (MAX/MIN optimization, non-transformed EXISTS (10.0)) + it is args[0]. + */ + Item **ref0= args; + if (args[1]->type() == Item::SUBSELECT_ITEM && + ((Item_subselect *)args[1])->is_in_predicate()) + { + /* + left_expr->fix_fields() may cause left_expr to be substituted for + another item. (e.g. an Item_field may be changed into Item_ref). This + transformation is undone at the end of statement execution (e.g. the + Item_ref is deleted). However, Item_in_optimizer::args[0] may keep + the pointer to the post-transformation item. Because of that, on the + next execution we need to copy args[1]->left_expr again. + */ + ref0= &(((Item_in_subselect *)args[1])->left_expr); + args[0]= ((Item_in_subselect *)args[1])->left_expr; + } + if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) || + (!cache && !(cache= Item_cache::get_cache(*ref0)))) DBUG_RETURN(1); + /* + During fix_field() expression could be substituted. + So we copy changes before use + */ + if (args[0] != (*ref0)) + args[0]= (*ref0); DBUG_PRINT("info", ("actual fix fields")); cache->setup(args[0]); @@ -1531,6 +1558,16 @@ bool Item_in_optimizer::fix_left(THD *thd) bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); + Item_subselect *sub= 0; + uint col; + + /* + MAX/MIN optimization can convert the subquery into + expr + Item_singlerow_subselect + */ + if (args[1]->type() == Item::SUBSELECT_ITEM) + sub= (Item_subselect *)args[1]; + if (fix_left(thd)) return TRUE; if (args[0]->maybe_null) @@ -1538,12 +1575,11 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) if (!args[1]->fixed && args[1]->fix_fields(thd, args+1)) return TRUE; - - Item_in_subselect * sub= (Item_in_subselect *)args[1]; if (!invisible_mode() && - args[0]->cols() != sub->engine->cols()) + ((sub && ((col= args[0]->cols()) != sub->engine->cols())) || + (!sub && (args[1]->cols() != (col= 1))))) { - my_error(ER_OPERAND_COLUMNS, MYF(0), args[0]->cols()); + my_error(ER_OPERAND_COLUMNS, MYF(0), col); return TRUE; } if (args[1]->maybe_null) @@ -2756,7 +2792,8 @@ Item_func_if::str_op(String *str) String *res=arg->val_str(str); if (res) res->set_charset(collation.collation); - null_value=arg->null_value; + if ((null_value=arg->null_value)) + res= NULL; return res; } @@ -2767,7 +2804,8 @@ Item_func_if::decimal_op(my_decimal *decimal_value) DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_bool() ? args[1] : args[2]; my_decimal *value= arg->val_decimal(decimal_value); - null_value= arg->null_value; + if ((null_value= arg->null_value)) + value= NULL; return value; } diff --git a/sql/item_func.cc b/sql/item_func.cc index e38e1e4919a..0e66b71e558 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -885,7 +885,7 @@ String *Item_func_hybrid_result_type::val_str(String *str) case DECIMAL_RESULT: { my_decimal decimal_value, *val; - if (!(val= decimal_op(&decimal_value))) + if (!(val= decimal_op_with_null_check(&decimal_value))) return 0; // null is set my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); str->set_charset(collation.collation); @@ -912,24 +912,22 @@ String *Item_func_hybrid_result_type::val_str(String *str) if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) || - str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; + if (date_op_with_null_check(<ime) || + (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) return (String *) 0; - } ltime.time_type= mysql_type_to_time_type(field_type()); str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); str->set_charset(&my_charset_bin); + DBUG_ASSERT(!null_value); return str; } - return str_op(&str_value); + return str_op_with_null_check(&str_value); case TIME_RESULT: case ROW_RESULT: case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } + DBUG_ASSERT(!null_value || (str == NULL)); return str; } @@ -942,7 +940,7 @@ double Item_func_hybrid_result_type::val_real() { my_decimal decimal_value, *val; double result; - if (!(val= decimal_op(&decimal_value))) + if (!(val= decimal_op_with_null_check(&decimal_value))) return 0.0; // null is set my_decimal2double(E_DEC_FATAL_ERROR, val, &result); return result; @@ -959,18 +957,14 @@ double Item_func_hybrid_result_type::val_real() if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0 )) - { - null_value= 1; + if (date_op_with_null_check(<ime)) return 0; - } ltime.time_type= mysql_type_to_time_type(field_type()); return TIME_to_double(<ime); } char *end_not_used; int err_not_used; - String *res= str_op(&str_value); + String *res= str_op_with_null_check(&str_value); return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(), &end_not_used, &err_not_used) : 0.0); } @@ -990,7 +984,7 @@ longlong Item_func_hybrid_result_type::val_int() case DECIMAL_RESULT: { my_decimal decimal_value, *val; - if (!(val= decimal_op(&decimal_value))) + if (!(val= decimal_op_with_null_check(&decimal_value))) return 0; // null is set longlong result; my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); @@ -1005,18 +999,14 @@ longlong Item_func_hybrid_result_type::val_int() if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)) - { - null_value= 1; + if (date_op_with_null_check(<ime)) return 0; - } ltime.time_type= mysql_type_to_time_type(field_type()); return TIME_to_ulonglong(<ime); } int err_not_used; String *res; - if (!(res= str_op(&str_value))) + if (!(res= str_op_with_null_check(&str_value))) return 0; char *end= (char*) res->ptr() + res->length(); @@ -1038,17 +1028,21 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value) DBUG_ASSERT(fixed == 1); switch (cached_result_type) { case DECIMAL_RESULT: - val= decimal_op(decimal_value); + val= decimal_op_with_null_check(decimal_value); break; case INT_RESULT: { longlong result= int_op(); + if (null_value) + return NULL; int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value); break; } case REAL_RESULT: { double result= (double)real_op(); + if (null_value) + return NULL; double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value); break; } @@ -1057,19 +1051,20 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value) if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)) + if (date_op_with_null_check(<ime)) { my_decimal_set_zero(decimal_value); - null_value= 1; return 0; } ltime.time_type= mysql_type_to_time_type(field_type()); return date2my_decimal(<ime, decimal_value); } String *res; - if (!(res= str_op(&str_value))) + if (!(res= str_op_with_null_check(&str_value))) + { + null_value= 1; return NULL; + } str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(), res->length(), res->charset(), decimal_value); @@ -1092,7 +1087,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, case DECIMAL_RESULT: { my_decimal value, *res; - if (!(res= decimal_op(&value)) || + if (!(res= decimal_op_with_null_check(&value)) || decimal_to_datetime_with_warn(res, ltime, fuzzydate, field_name_or_null())) goto err; @@ -1122,7 +1117,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, return date_op(ltime, fuzzydate); char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; - if (!(res= str_op(&tmp)) || + if (!(res= str_op_with_null_check(&tmp)) || str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), ltime, fuzzydate)) goto err; diff --git a/sql/item_func.h b/sql/item_func.h index ce1f2fdd676..0b3454fa4b0 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -385,17 +385,17 @@ public: void no_rows_in_result() { - bool_func_call_args info; - info.original_func_item= this; - info.bool_function= &Item::no_rows_in_result; - walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info); + for (uint i= 0; i < arg_count; i++) + { + args[i]->no_rows_in_result(); + } } void restore_to_before_no_rows_in_result() { - bool_func_call_args info; - info.original_func_item= this; - info.bool_function= &Item::restore_to_before_no_rows_in_result; - walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info); + for (uint i= 0; i < arg_count; i++) + { + args[i]->no_rows_in_result(); + } } }; @@ -419,6 +419,29 @@ public: class Item_func_hybrid_result_type: public Item_func { + /* + Helper methods to make sure that the result of + decimal_op(), str_op() and date_op() is properly synched with null_value. + */ + bool date_op_with_null_check(MYSQL_TIME *ltime) + { + bool rc= date_op(ltime, + field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0); + DBUG_ASSERT(!rc ^ null_value); + return rc; + } + String *str_op_with_null_check(String *str) + { + String *res= str_op(str); + DBUG_ASSERT((res != NULL) ^ null_value); + return res; + } + my_decimal *decimal_op_with_null_check(my_decimal *decimal_buffer) + { + my_decimal *res= decimal_op(decimal_buffer); + DBUG_ASSERT((res != NULL) ^ null_value); + return res; + } protected: Item_result cached_result_type; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index fe2352f3008..ce01fc7a3b8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1378,7 +1378,7 @@ Item_in_subselect::Item_in_subselect(Item * left_exp, { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy)); - left_expr= left_exp; + left_expr_orig= left_expr= left_exp; func= &eq_creator; init(select_lex, new select_exists_subselect(this)); max_columns= UINT_MAX; @@ -1401,7 +1401,7 @@ Item_allany_subselect::Item_allany_subselect(Item * left_exp, :Item_in_subselect(), func_creator(fc), all(all_arg) { DBUG_ENTER("Item_allany_subselect::Item_allany_subselect"); - left_expr= left_exp; + left_expr_orig= left_expr= left_exp; func= func_creator(all_arg); init(select_lex, new select_exists_subselect(this)); max_columns= 1; @@ -3032,15 +3032,13 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) arena= thd->activate_stmt_arena_if_needed(&backup); if (!optimizer) { - result= (!(optimizer= new Item_in_optimizer(left_expr, this))); + result= (!(optimizer= new Item_in_optimizer(left_expr_orig, this))); if (result) goto out; } thd->lex->current_select= current->return_after_parsing(); result= optimizer->fix_left(thd); - /* fix_fields can change reference to left_expr, we need reassign it */ - left_expr= optimizer->arguments()[0]; thd->lex->current_select= current; if (changed) @@ -3107,11 +3105,13 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) { uint outer_cols_num; List<Item> *inner_cols; + char const *save_where= thd->where; DBUG_ENTER("Item_in_subselect::fix_fields"); if (test_strategy(SUBS_SEMI_JOIN)) DBUG_RETURN( !( (*ref)= new Item_int(1)) ); + thd->where= "IN/ALL/ANY subquery"; /* Check if the outer and inner IN operands match in those cases when we will not perform IN=>EXISTS transformation. Currently this is when we @@ -3142,7 +3142,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) if (outer_cols_num != inner_cols->elements) { my_error(ER_OPERAND_COLUMNS, MYF(0), outer_cols_num); - DBUG_RETURN(TRUE); + goto err; } if (outer_cols_num > 1) { @@ -3152,20 +3152,24 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) { inner_col= inner_col_it++; if (inner_col->check_cols(left_expr->element_index(i)->cols())) - DBUG_RETURN(TRUE); + goto err; } } } - if (thd_arg->lex->is_view_context_analysis() && - left_expr && !left_expr->fixed && + if (left_expr && !left_expr->fixed && left_expr->fix_fields(thd_arg, &left_expr)) - DBUG_RETURN(TRUE); + goto err; else if (Item_subselect::fix_fields(thd_arg, ref)) - DBUG_RETURN(TRUE); + goto err; fixed= TRUE; + thd->where= save_where; DBUG_RETURN(FALSE); + +err: + thd->where= save_where; + DBUG_RETURN(TRUE); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 92b269d02f1..3c0b7bd6ade 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -482,6 +482,12 @@ protected: Item **having_item); public: Item *left_expr; + /* + Important for PS/SP: left_expr_orig is the item that left_expr originally + pointed at. That item is allocated on the statement arena, while + left_expr could later be changed to something on the execution arena. + */ + Item *left_expr_orig; /* Priority of this predicate in the convert-to-semi-join-nest process. */ int sj_convert_priority; /* diff --git a/sql/key.cc b/sql/key.cc index e3787ea7869..aaaea9391c6 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -18,7 +18,6 @@ #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: by includes later #include "key.h" // key_rec_cmp #include "field.h" // Field diff --git a/sql/lock.cc b/sql/lock.cc index 7c8368ab0e3..07ea0b1f6dc 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -77,7 +77,6 @@ #include <my_global.h> #include "sql_priv.h" #include "debug_sync.h" -#include "unireg.h" // REQUIRED: for other includes #include "lock.h" #include "sql_base.h" // close_tables_for_reopen #include "sql_parse.h" // is_log_table_write_query diff --git a/sql/log.cc b/sql/log.cc index 4868902b524..6432716c1f4 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -7838,14 +7838,13 @@ void MYSQL_BIN_LOG::binlog_trigger_immediate_group_commit() { group_commit_entry *head; - mysql_mutex_lock(&LOCK_prepare_ordered); + mysql_mutex_assert_owner(&LOCK_prepare_ordered); head= group_commit_queue; if (head) { head->thd->has_waiter= true; mysql_cond_signal(&COND_prepare_ordered); } - mysql_mutex_unlock(&LOCK_prepare_ordered); } @@ -7864,9 +7863,11 @@ binlog_report_wait_for(THD *thd1, THD *thd2) { if (opt_binlog_commit_wait_count == 0) return; + mysql_mutex_lock(&LOCK_prepare_ordered); thd2->has_waiter= true; if (thd2->waiting_on_group_commit) mysql_bin_log.binlog_trigger_immediate_group_commit(); + mysql_mutex_unlock(&LOCK_prepare_ordered); } diff --git a/sql/log.h b/sql/log.h index c014e08b172..14389963aad 100644 --- a/sql/log.h +++ b/sql/log.h @@ -17,7 +17,6 @@ #ifndef LOG_H #define LOG_H -#include "unireg.h" // REQUIRED: for other includes #include "handler.h" /* my_xid */ class Relay_log_info; diff --git a/sql/log_event.cc b/sql/log_event.cc index f50bb3d8b81..a1dcce7d7c4 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -308,7 +308,7 @@ public: ~Write_on_release_cache() { copy_event_cache_to_file_and_reinit(m_cache, m_file); - if (m_flags | FLUSH_F) + if (m_flags & FLUSH_F) fflush(m_file); } @@ -816,6 +816,15 @@ const char* Log_event::get_type_str(Log_event_type type) case BINLOG_CHECKPOINT_EVENT: return "Binlog_checkpoint"; case GTID_EVENT: return "Gtid"; case GTID_LIST_EVENT: return "Gtid_list"; + + /* The following is only for mysqlbinlog */ + case IGNORABLE_LOG_EVENT: return "Ignorable log event"; + case ROWS_QUERY_LOG_EVENT: return "MySQL Rows_query"; + case GTID_LOG_EVENT: return "MySQL Gtid"; + case ANONYMOUS_GTID_LOG_EVENT: return "MySQL Anonymous_Gtid"; + case PREVIOUS_GTIDS_LOG_EVENT: return "MySQL Previous_gtids"; + case HEARTBEAT_LOG_EVENT: return "Heartbeat"; + default: return "Unknown"; /* impossible */ } } @@ -1419,6 +1428,8 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, DBUG_ENTER("Log_event::read_log_event"); DBUG_ASSERT(description_event != 0); char head[LOG_EVENT_MINIMAL_HEADER_LEN]; + my_off_t position= my_b_tell(file); + /* First we only want to read at most LOG_EVENT_MINIMAL_HEADER_LEN, just to check the event for sanity and to know its length; no need to really parse @@ -1430,7 +1441,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, LOG_EVENT_MINIMAL_HEADER_LEN); LOCK_MUTEX; - DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file))); + DBUG_PRINT("info", ("my_b_tell: %llu", (ulonglong) position)); if (my_b_read(file, (uchar *) head, header_size)) { DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \ @@ -1487,8 +1498,9 @@ err: { DBUG_ASSERT(error != 0); sql_print_error("Error in Log_event::read_log_event(): " - "'%s', data_len: %lu, event_type: %d", - error,data_len,(uchar)(head[EVENT_TYPE_OFFSET])); + "'%s' at offset: %llu data_len: %lu event_type: %d", + error, position, data_len, + (uchar)(head[EVENT_TYPE_OFFSET])); my_free(buf); /* The SQL slave thread will check if file->error<0 to know @@ -1521,10 +1533,12 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version)); DBUG_DUMP("data", (unsigned char*) buf, event_len); - /* Check the integrity */ + /* + Check the integrity; This is needed because handle_slave_io() doesn't + check if packet is of proper length. + */ if (event_len < EVENT_LEN_OFFSET || - (uchar)buf[EVENT_TYPE_OFFSET] >= ENUM_END_EVENT || - (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET)) + event_len != uint4korr(buf+EVENT_LEN_OFFSET)) { *error="Sanity check failed"; // Needed to free buffer DBUG_RETURN(NULL); // general sanity check - will fail on a partial read @@ -1706,6 +1720,15 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, case DELETE_ROWS_EVENT: ev = new Delete_rows_log_event(buf, event_len, description_event); break; + + /* MySQL GTID events are ignored */ + case GTID_LOG_EVENT: + case ANONYMOUS_GTID_LOG_EVENT: + case PREVIOUS_GTIDS_LOG_EVENT: + ev= new Ignorable_log_event(buf, description_event, + get_type_str((Log_event_type) event_type)); + break; + case TABLE_MAP_EVENT: ev = new Table_map_log_event(buf, event_len, description_event); break; @@ -1723,10 +1746,22 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, ev = new Annotate_rows_log_event(buf, event_len, description_event); break; default: - DBUG_PRINT("error",("Unknown event code: %d", - (int) buf[EVENT_TYPE_OFFSET])); - ev= NULL; - break; + /* + Create an object of Ignorable_log_event for unrecognized sub-class. + So that SLAVE SQL THREAD will only update the position and continue. + */ + if (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F) + { + ev= new Ignorable_log_event(buf, description_event, + get_type_str((Log_event_type) event_type)); + } + else + { + DBUG_PRINT("error",("Unknown event code: %d", + (int) buf[EVENT_TYPE_OFFSET])); + ev= NULL; + break; + } } } @@ -2192,20 +2227,13 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr, uint precision= meta >> 8; uint decimals= meta & 0xFF; uint bin_size= my_decimal_get_binary_size(precision, decimals); - uint length; my_decimal dec; binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec, precision, decimals); - int i, end; - char buff[512], *pos; - pos= buff; - pos+= sprintf(buff, "%s", dec.sign() ? "-" : ""); - end= ROUND_UP(dec.frac) + ROUND_UP(dec.intg)-1; - for (i=0; i < end; i++) - pos+= sprintf(pos, "%09d.", dec.buf[i]); - pos+= sprintf(pos, "%09d", dec.buf[i]); - length= (uint) (pos - buff); - my_b_write(file, buff, length); + int length= DECIMAL_MAX_STR_LENGTH; + char buff[DECIMAL_MAX_STR_LENGTH + 1]; + decimal2string(&dec, buff, &length, 0, 0, 0); + my_b_write(file, (uchar*)buff, length); my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)", precision, decimals); return bin_size; @@ -4920,6 +4948,9 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) post_header_len[HEARTBEAT_LOG_EVENT-1]= 0; post_header_len[IGNORABLE_LOG_EVENT-1]= 0; post_header_len[ROWS_QUERY_LOG_EVENT-1]= 0; + post_header_len[GTID_LOG_EVENT-1]= 0; + post_header_len[ANONYMOUS_GTID_LOG_EVENT-1]= 0; + post_header_len[PREVIOUS_GTIDS_LOG_EVENT-1]= 0; post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2; post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2; post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2; @@ -12835,6 +12866,52 @@ Incident_log_event::write_data_body(IO_CACHE *file) } +Ignorable_log_event::Ignorable_log_event(const char *buf, + const Format_description_log_event + *descr_event, + const char *event_name) + :Log_event(buf, descr_event), number((int) (uchar) buf[EVENT_TYPE_OFFSET]), + description(event_name) +{ + DBUG_ENTER("Ignorable_log_event::Ignorable_log_event"); + DBUG_VOID_RETURN; +} + +Ignorable_log_event::~Ignorable_log_event() +{ +} + +#ifndef MYSQL_CLIENT +/* Pack info for its unrecognized ignorable event */ +void Ignorable_log_event::pack_info(THD *thd, Protocol *protocol) +{ + char buf[256]; + size_t bytes; + bytes= my_snprintf(buf, sizeof(buf), "# Ignorable event type %d (%s)", + number, description); + protocol->store(buf, bytes, &my_charset_bin); +} +#endif + +#ifdef MYSQL_CLIENT +/* Print for its unrecognized ignorable event */ +void +Ignorable_log_event::print(FILE *file, + PRINT_EVENT_INFO *print_event_info) +{ + if (print_event_info->short_form) + return; + + print_header(&print_event_info->head_cache, print_event_info, FALSE); + my_b_printf(&print_event_info->head_cache, "\tIgnorable\n"); + my_b_printf(&print_event_info->head_cache, + "# Ignorable event type %d (%s)\n", number, description); + copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, + file); +} +#endif + + #ifdef MYSQL_CLIENT /** The default values for these variables should be values that are @@ -12916,4 +12993,25 @@ bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos, return TRUE; #endif } + +/** + Check if we should write event to the relay log + + This is used to skip events that is only supported by MySQL + + Return: + 0 ok + 1 Don't write event +*/ + +bool event_that_should_be_ignored(const char *buf) +{ + uint event_type= (uchar)buf[EVENT_TYPE_OFFSET]; + if (event_type == GTID_LOG_EVENT || + event_type == ANONYMOUS_GTID_LOG_EVENT || + event_type == PREVIOUS_GTIDS_LOG_EVENT || + (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F)) + return 1; + return 0; +} #endif diff --git a/sql/log_event.h b/sql/log_event.h index 6a3e6f174bb..8661e6e49e5 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -260,6 +260,7 @@ struct sql_ex_info #define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN) #define INCIDENT_HEADER_LEN 2 #define HEARTBEAT_HEADER_LEN 0 +#define IGNORABLE_HEADER_LEN 0 #define ROWS_HEADER_LEN_V2 10 #define ANNOTATE_ROWS_HEADER_LEN 0 #define BINLOG_CHECKPOINT_HEADER_LEN 4 @@ -295,7 +296,7 @@ struct sql_ex_info to the slave. It is used to increase the thd(max_allowed) for both the DUMP thread on the master and the SQL/IO thread on the slave. */ -#define MAX_MAX_ALLOWED_PACKET 1024*1024*1024 +#define MAX_MAX_ALLOWED_PACKET (1024*1024*1024) /* Event header offsets; @@ -520,6 +521,17 @@ struct sql_ex_info #define LOG_EVENT_RELAY_LOG_F 0x40 /** + @def LOG_EVENT_IGNORABLE_F + + For an event, 'e', carrying a type code, that a slave, + 's', does not recognize, 's' will check 'e' for + LOG_EVENT_IGNORABLE_F, and if the flag is set, then 'e' + is ignored. Otherwise, 's' acknowledges that it has + found an unknown event in the relay log. +*/ +#define LOG_EVENT_IGNORABLE_F 0x80 + +/** @def LOG_EVENT_SKIP_REPLICATION_F Flag set by application creating the event (with @@skip_replication); the @@ -697,6 +709,11 @@ enum Log_event_type UPDATE_ROWS_EVENT = 31, DELETE_ROWS_EVENT = 32, + /* MySQL 5.6 GTID events, ignored by MariaDB */ + GTID_LOG_EVENT= 33, + ANONYMOUS_GTID_LOG_EVENT= 34, + PREVIOUS_GTIDS_LOG_EVENT= 35, + /* Add new events here - right above this comment! Existing events (except ENUM_END_EVENT) should never change their numbers @@ -4740,6 +4757,60 @@ private: LEX_STRING m_message; }; +/** + @class Ignorable_log_event + + Base class for ignorable log events. Events deriving from + this class can be safely ignored by slaves that cannot + recognize them. Newer slaves, will be able to read and + handle them. This has been designed to be an open-ended + architecture, so adding new derived events shall not harm + the old slaves that support ignorable log event mechanism + (they will just ignore unrecognized ignorable events). + + @note The only thing that makes an event ignorable is that it has + the LOG_EVENT_IGNORABLE_F flag set. It is not strictly necessary + that ignorable event types derive from Ignorable_log_event; they may + just as well derive from Log_event and pass LOG_EVENT_IGNORABLE_F as + argument to the Log_event constructor. +**/ + +class Ignorable_log_event : public Log_event { +public: + int number; + const char *description; + +#ifndef MYSQL_CLIENT + Ignorable_log_event(THD *thd_arg) + :Log_event(thd_arg, LOG_EVENT_IGNORABLE_F, FALSE), + number(0), description("internal") + { + DBUG_ENTER("Ignorable_log_event::Ignorable_log_event"); + DBUG_VOID_RETURN; + } +#endif + + Ignorable_log_event(const char *buf, + const Format_description_log_event *descr_event, + const char *event_name); + virtual ~Ignorable_log_event(); + +#ifndef MYSQL_CLIENT + void pack_info(THD *, Protocol*); +#endif + +#ifdef MYSQL_CLIENT + virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info); +#endif + + virtual Log_event_type get_type_code() { return IGNORABLE_LOG_EVENT; } + + virtual bool is_valid() const { return 1; } + + virtual int get_data_size() { return IGNORABLE_HEADER_LEN; } +}; + + static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file) { @@ -4797,6 +4868,7 @@ bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos, ulonglong *relay_log_pos); bool event_checksum_test(uchar *buf, ulong event_len, uint8 alg); +bool event_that_should_be_ignored(const char *buf); uint8 get_checksum_alg(const char* buf, ulong len); extern TYPELIB binlog_checksum_typelib; diff --git a/sql/log_slow.h b/sql/log_slow.h index 2ae07da97c3..3ae2060cc27 100644 --- a/sql/log_slow.h +++ b/sql/log_slow.h @@ -16,23 +16,23 @@ /* Defining what to log to slow log */ #define LOG_SLOW_VERBOSITY_INIT 0 -#define LOG_SLOW_VERBOSITY_INNODB 1 << 0 -#define LOG_SLOW_VERBOSITY_QUERY_PLAN 1 << 1 -#define LOG_SLOW_VERBOSITY_EXPLAIN 1 << 2 +#define LOG_SLOW_VERBOSITY_INNODB (1 << 0) +#define LOG_SLOW_VERBOSITY_QUERY_PLAN (1 << 1) +#define LOG_SLOW_VERBOSITY_EXPLAIN (1 << 2) #define QPLAN_INIT QPLAN_QC_NO -#define QPLAN_ADMIN 1 << 0 -#define QPLAN_FILESORT 1 << 1 -#define QPLAN_FILESORT_DISK 1 << 2 -#define QPLAN_FULL_JOIN 1 << 3 -#define QPLAN_FULL_SCAN 1 << 4 -#define QPLAN_QC 1 << 5 -#define QPLAN_QC_NO 1 << 6 -#define QPLAN_TMP_DISK 1 << 7 -#define QPLAN_TMP_TABLE 1 << 8 -#define QPLAN_FILESORT_PRIORITY_QUEUE 1 << 9 +#define QPLAN_ADMIN (1 << 0) +#define QPLAN_FILESORT (1 << 1) +#define QPLAN_FILESORT_DISK (1 << 2) +#define QPLAN_FULL_JOIN (1 << 3) +#define QPLAN_FULL_SCAN (1 << 4) +#define QPLAN_QC (1 << 5) +#define QPLAN_QC_NO (1 << 6) +#define QPLAN_TMP_DISK (1 << 7) +#define QPLAN_TMP_TABLE (1 << 8) +#define QPLAN_FILESORT_PRIORITY_QUEUE (1 << 9) /* ... */ -#define QPLAN_MAX ((ulong) 1) << 31 /* reserved as placeholder */ +#define QPLAN_MAX (((ulong) 1) << 31) /* reserved as placeholder */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f75f5440279..323b851ac1f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4515,14 +4515,24 @@ static int init_common_variables() { if (lower_case_table_names_used) { +#if MYSQL_VERSION_ID < 100100 if (global_system_variables.log_warnings) sql_print_warning("You have forced lower_case_table_names to 0 through " "a command-line option, even though your file system " "'%s' is case insensitive. This means that you can " - "corrupt a MyISAM table by accessing it with " - "different cases. You should consider changing " - "lower_case_table_names to 1 or 2", - mysql_real_data_home); + "corrupt your tables if you access them using names " + "with different letter case. You should consider " + "changing lower_case_table_names to 1 or 2", + mysql_real_data_home); +#else + sql_print_error("The server option 'lower_case_table_names' is " + "configured to use case sensitive table names but the " + "data directory resides on a case-insensitive file system. " + "Please use a case sensitive file system for your data " + "directory or switch to a case-insensitive table name " + "mode."); +#endif + return 1; } else { @@ -6249,7 +6259,15 @@ int mysqld_main(int argc, char **argv) execute_ddl_log_recovery(); - if (Events::init(opt_noacl || opt_bootstrap)) + /* + Change EVENTS_ORIGINAL to EVENTS_OFF (the default value) as there is no + point in using ORIGINAL during startup + */ + if (Events::opt_event_scheduler == Events::EVENTS_ORIGINAL) + Events::opt_event_scheduler= Events::EVENTS_OFF; + + Events::set_original_state(Events::opt_event_scheduler); + if (Events::init((THD*) 0, opt_noacl || opt_bootstrap)) unireg_abort(1); #ifdef WITH_WSREP /* WSREP AFTER SE */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3597ade2cba..da18a30ac78 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. - Copyright (c) 2008, 2014, Monty Program Ab. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -876,8 +876,8 @@ public: Used to store 'current key tuples', in both range analysis and partitioning (list) analysis */ - uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], - max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + uchar *min_key; + uchar *max_key; /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ uint alloced_sel_args; @@ -3066,13 +3066,13 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_RETURN(0); // Can't use range } key_parts= param.key_parts; - thd->mem_root= &alloc; /* Make an array with description of all key parts of all table keys. This is used in get_mm_parts function. */ key_info= head->key_info; + uint max_key_len= 0; for (idx=0 ; idx < head->s->keys ; idx++, key_info++) { KEY_PART_INFO *key_part_info; @@ -3085,6 +3085,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, param.key[param.keys]=key_parts; key_part_info= key_info->key_part; + uint cur_key_len= 0; for (uint part= 0 ; part < n_key_parts ; part++, key_parts++, key_part_info++) { @@ -3092,6 +3093,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, key_parts->part= part; key_parts->length= key_part_info->length; key_parts->store_length= key_part_info->store_length; + cur_key_len += key_part_info->store_length; key_parts->field= key_part_info->field; key_parts->null_bit= key_part_info->null_bit; key_parts->image_type = @@ -3100,10 +3102,22 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, key_parts->flag= (uint8) key_part_info->key_part_flag; } param.real_keynr[param.keys++]=idx; + if (cur_key_len > max_key_len) + max_key_len= cur_key_len; } param.key_parts_end=key_parts; param.alloced_sel_args= 0; + max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */ + if (!(param.min_key= (uchar*)alloc_root(&alloc,max_key_len)) || + !(param.max_key= (uchar*)alloc_root(&alloc,max_key_len))) + { + thd->no_errors=0; + free_root(&alloc,MYF(0)); // Return memory & allocator + DBUG_RETURN(0); // Can't use range + } + + thd->mem_root= &alloc; /* Calculate cost of full index read for the shortest covering index */ if (!head->covering_keys.is_clear_all()) { @@ -3327,7 +3341,7 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, return TRUE; param->key_parts= key_part; - + uint max_key_len= 0; for (field_ptr= table->field; *field_ptr; field_ptr++) { if (bitmap_is_set(used_fields, (*field_ptr)->field_index)) @@ -3342,6 +3356,8 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, store_length+= HA_KEY_NULL_LENGTH; if (field->real_type() == MYSQL_TYPE_VARCHAR) store_length+= HA_KEY_BLOB_LENGTH; + if (max_key_len < store_length) + max_key_len= store_length; key_part->store_length= store_length; key_part->field= field; key_part->image_type= Field::itRAW; @@ -3351,6 +3367,13 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, key_part++; } } + + max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */ + if (!(param->min_key= (uchar*)alloc_root(param->mem_root, max_key_len)) || + !(param->max_key= (uchar*)alloc_root(param->mem_root, max_key_len))) + { + return true; + } param->keys= keys; param->key_parts_end= key_part; @@ -4611,10 +4634,19 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) key_tree->min_flag | key_tree->max_flag, &subpart_iter); - DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */ + if (res == 0) + { + /* + The only case where we can get "no satisfying subpartitions" + returned from the above call is when an error has occurred. + */ + DBUG_ASSERT(range_par->thd->is_error()); + return 0; + } + if (res == -1) goto pop_and_go_right; /* all subpartitions satisfy */ - + uint32 subpart_id; bitmap_clear_all(&ppar->subparts_bitmap); while ((subpart_id= subpart_iter.get_next(&subpart_iter)) != @@ -4899,12 +4931,15 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) Field **field= (ppar->part_fields)? part_info->part_field_array : part_info->subpart_field_array; bool in_subpart_fields= FALSE; + uint max_key_len= 0; + uint cur_key_len= 0; for (uint part= 0; part < total_parts; part++, key_part++) { key_part->key= 0; key_part->part= part; key_part->length= (uint16)(*field)->key_length(); key_part->store_length= (uint16)get_partition_field_store_length(*field); + cur_key_len += key_part->store_length; DBUG_PRINT("info", ("part %u length %u store_length %u", part, key_part->length, key_part->store_length)); @@ -4930,10 +4965,22 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) { field= part_info->subpart_field_array; in_subpart_fields= TRUE; + max_key_len= cur_key_len; + cur_key_len= 0; } } range_par->key_parts_end= key_part; + if (cur_key_len > max_key_len) + max_key_len= cur_key_len; + + max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */ + if (!(range_par->min_key= (uchar*)alloc_root(alloc,max_key_len)) || + !(range_par->max_key= (uchar*)alloc_root(alloc,max_key_len))) + { + return true; + } + DBUG_EXECUTE("info", print_partitioning_index(range_par->key_parts, range_par->key_parts_end);); return FALSE; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 0ad90e2ef3d..3d470b6ff5c 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -617,6 +617,18 @@ int check_and_do_in_subquery_rewrites(JOIN *join) thd->stmt_arena->state != Query_arena::PREPARED) */ { + SELECT_LEX *current= thd->lex->current_select; + thd->lex->current_select= current->return_after_parsing(); + char const *save_where= thd->where; + thd->where= "IN/ALL/ANY subquery"; + + bool failure= !in_subs->left_expr->fixed && + in_subs->left_expr->fix_fields(thd, &in_subs->left_expr); + thd->lex->current_select= current; + thd->where= save_where; + if (failure) + DBUG_RETURN(-1); /* purecov: deadcode */ + /* Check if the left and right expressions have the same # of columns, i.e. we don't have a case like @@ -630,18 +642,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join) my_error(ER_OPERAND_COLUMNS, MYF(0), in_subs->left_expr->cols()); DBUG_RETURN(-1); } - - SELECT_LEX *current= thd->lex->current_select; - thd->lex->current_select= current->return_after_parsing(); - char const *save_where= thd->where; - thd->where= "IN/ALL/ANY subquery"; - - bool failure= !in_subs->left_expr->fixed && - in_subs->left_expr->fix_fields(thd, &in_subs->left_expr); - thd->lex->current_select= current; - thd->where= save_where; - if (failure) - DBUG_RETURN(-1); /* purecov: deadcode */ } DBUG_PRINT("info", ("Checking if subq can be converted to semi-join")); @@ -704,6 +704,12 @@ int check_and_do_in_subquery_rewrites(JOIN *join) if (!optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) && !optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)) my_error(ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES, MYF(0)); + /* + Transform each subquery predicate according to its overloaded + transformer. + */ + if (subselect->select_transformer(join)) + DBUG_RETURN(-1); /* If the subquery predicate is IN/=ANY, analyse and set all possible @@ -755,12 +761,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join) allany_subs->add_strategy(strategy); } - /* - Transform each subquery predicate according to its overloaded - transformer. - */ - if (subselect->select_transformer(join)) - DBUG_RETURN(-1); } } DBUG_RETURN(0); @@ -1593,8 +1593,19 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) if (subq_pred->left_expr->cols() == 1) { nested_join->sj_outer_expr_list.push_back(subq_pred->left_expr); + /* + Create Item_func_eq. Note that + 1. this is done on the statement, not execution, arena + 2. if it's a PS then this happens only once - on the first execution. + On following re-executions, the item will be fix_field-ed normally. + 3. Thus it should be created as if it was fix_field'ed, in particular + all pointers to items in the execution arena should be protected + with thd->change_item_tree + */ Item_func_eq *item_eq= - new Item_func_eq(subq_pred->left_expr, subq_lex->ref_pointer_array[0]); + new Item_func_eq(subq_pred->left_expr_orig, subq_lex->ref_pointer_array[0]); + if (subq_pred->left_expr_orig != subq_pred->left_expr) + thd->change_item_tree(item_eq->arguments(), subq_pred->left_expr); item_eq->in_equality_no= 0; sj_nest->sj_on_expr= and_items(sj_nest->sj_on_expr, item_eq); } @@ -4362,6 +4373,74 @@ int init_dups_weedout(JOIN *join, uint first_table, int first_fanout_table, uint /* + @brief + Set up semi-join Loose Scan strategy for execution + + @detail + Other strategies are done in setup_semijoin_dups_elimination(), + however, we need to set up Loose Scan earlier, before make_join_select is + called. This is to prevent make_join_select() from switching full index + scans into quick selects (which will break Loose Scan access). + + @return + 0 OK + 1 Error +*/ + +int setup_semijoin_loosescan(JOIN *join) +{ + uint i; + DBUG_ENTER("setup_semijoin_loosescan"); + + POSITION *pos= join->best_positions + join->const_tables; + for (i= join->const_tables ; i < join->top_join_tab_count; ) + { + JOIN_TAB *tab=join->join_tab + i; + switch (pos->sj_strategy) { + case SJ_OPT_MATERIALIZE: + case SJ_OPT_MATERIALIZE_SCAN: + i+= 1; /* join tabs are embedded in the nest */ + pos += pos->n_sj_tables; + break; + case SJ_OPT_LOOSE_SCAN: + { + /* We jump from the last table to the first one */ + tab->loosescan_match_tab= tab + pos->n_sj_tables - 1; + + /* LooseScan requires records to be produced in order */ + if (tab->select && tab->select->quick) + tab->select->quick->need_sorted_output(); + + for (uint j= i; j < i + pos->n_sj_tables; j++) + join->join_tab[j].inside_loosescan_range= TRUE; + + /* Calculate key length */ + uint keylen= 0; + uint keyno= pos->loosescan_picker.loosescan_key; + for (uint kp=0; kp < pos->loosescan_picker.loosescan_parts; kp++) + keylen += tab->table->key_info[keyno].key_part[kp].store_length; + + tab->loosescan_key= keyno; + tab->loosescan_key_len= keylen; + if (pos->n_sj_tables > 1) + tab[pos->n_sj_tables - 1].do_firstmatch= tab; + i+= pos->n_sj_tables; + pos+= pos->n_sj_tables; + break; + } + default: + { + i++; + pos++; + break; + } + } + } + DBUG_RETURN(FALSE); +} + + +/* Setup the strategies to eliminate semi-join duplicates. SYNOPSIS @@ -4469,8 +4548,6 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, for (i= join->const_tables ; i < join->top_join_tab_count; ) { JOIN_TAB *tab=join->join_tab + i; - //POSITION *pos= join->best_positions + i; - uint keylen, keyno; switch (pos->sj_strategy) { case SJ_OPT_MATERIALIZE: case SJ_OPT_MATERIALIZE_SCAN: @@ -4480,26 +4557,7 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, break; case SJ_OPT_LOOSE_SCAN: { - /* We jump from the last table to the first one */ - tab->loosescan_match_tab= tab + pos->n_sj_tables - 1; - - /* LooseScan requires records to be produced in order */ - if (tab->select && tab->select->quick) - tab->select->quick->need_sorted_output(); - - for (uint j= i; j < i + pos->n_sj_tables; j++) - join->join_tab[j].inside_loosescan_range= TRUE; - - /* Calculate key length */ - keylen= 0; - keyno= pos->loosescan_picker.loosescan_key; - for (uint kp=0; kp < pos->loosescan_picker.loosescan_parts; kp++) - keylen += tab->table->key_info[keyno].key_part[kp].store_length; - - tab->loosescan_key= keyno; - tab->loosescan_key_len= keylen; - if (pos->n_sj_tables > 1) - tab[pos->n_sj_tables - 1].do_firstmatch= tab; + /* Setup already handled by setup_semijoin_loosescan */ i+= pos->n_sj_tables; pos+= pos->n_sj_tables; break; diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 3da94d05521..0fb1a931e36 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -194,8 +194,6 @@ public: PREV_BITS(key_part_map, max_loose_keypart+1) && // (3) !key_uses_partial_cols(s->table->s, key)) { - /* Ok, can use the strategy */ - part1_conds_met= TRUE; if (s->quick && s->quick->index == key && s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) { @@ -204,6 +202,12 @@ public: } DBUG_PRINT("info", ("Can use LooseScan scan")); + if (found_part & 1) + { + /* Can use LooseScan on ref access if the first key part is bound */ + part1_conds_met= TRUE; + } + /* Check if this is a special case where there are no usable bound IN-equalities, i.e. we have @@ -211,11 +215,13 @@ public: outer_expr IN (SELECT innertbl.key FROM ...) and outer_expr cannot be evaluated yet, so it's actually full - index scan and not a ref access + index scan and not a ref access. + We can do full index scan if it uses index-only. */ if (!(found_part & 1 ) && /* no usable ref access for 1st key part */ s->table->covering_keys.is_set(key)) { + part1_conds_met= TRUE; DBUG_PRINT("info", ("Can use full index scan for LooseScan")); /* Calculate the cost of complete loose index scan. */ @@ -383,6 +389,7 @@ public: bool create_sj_weedout_tmp_table(THD *thd); }; +int setup_semijoin_loosescan(JOIN *join); int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, uint no_jbuf_after); void destroy_sj_tmp_tables(JOIN *join); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index fc3ce09dd8e..1ff1f4a6449 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -341,7 +341,8 @@ int opt_sum_query(THD *thd, there are no outer joins. */ if (!conds && !((Item_sum_count*) item)->get_arg(0)->maybe_null && - !outer_tables && maybe_exact_count) + !outer_tables && maybe_exact_count && + ((item->used_tables() & OUTER_REF_TABLE_BIT) == 0)) { if (!is_exact_count) { @@ -369,7 +370,8 @@ int opt_sum_query(THD *thd, indexes to find the key. */ Item *expr=item_sum->get_arg(0); - if (expr->real_item()->type() == Item::FIELD_ITEM) + if (((expr->used_tables() & OUTER_REF_TABLE_BIT) == 0) && + expr->real_item()->type() == Item::FIELD_ITEM) { uchar key_buff[MAX_KEY_LENGTH]; TABLE_REF ref; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 73b88d64224..1607b1937df 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. +/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -77,6 +78,41 @@ partition_info *partition_info::get_clone() part_clone->subpartitions.push_back(subpart_clone); } clone->partitions.push_back(part_clone); + part_clone->list_val_list.empty(); + List_iterator<part_elem_value> list_val_it(part->list_val_list); + part_elem_value *new_val_arr= + (part_elem_value *)sql_alloc(sizeof(part_elem_value) * + part->list_val_list.elements); + if (!new_val_arr) + { + mem_alloc_error(sizeof(part_elem_value) * part->list_val_list.elements); + DBUG_RETURN(NULL); + } + p_column_list_val *new_colval_arr= + (p_column_list_val*)sql_alloc(sizeof(p_column_list_val) * + num_columns * + part->list_val_list.elements); + if (!new_colval_arr) + { + mem_alloc_error(sizeof(p_column_list_val) * num_columns * + part->list_val_list.elements); + DBUG_RETURN(NULL); + } + part_elem_value *val; + while ((val= list_val_it++)) + { + part_elem_value *new_val= new_val_arr++; + memcpy(new_val, val, sizeof(part_elem_value)); + if (!val->null_value) + { + p_column_list_val *new_colval= new_colval_arr; + new_colval_arr+= num_columns; + memcpy(new_colval, val->col_val_array, + sizeof(p_column_list_val) * num_columns); + new_val->col_val_array= new_colval; + } + part_clone->list_val_list.push_back(new_val); + } } DBUG_RETURN(clone); } @@ -1630,15 +1666,22 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, { int err= 0; + /* Check for partition expression. */ if (!list_of_part_fields) { DBUG_ASSERT(part_expr); err= part_expr->walk(&Item::check_partition_func_processor, 0, NULL); - if (!err && is_sub_partitioned() && !list_of_subpart_fields) - err= subpart_expr->walk(&Item::check_partition_func_processor, 0, - NULL); } + + /* Check for sub partition expression. */ + if (!err && is_sub_partitioned() && !list_of_subpart_fields) + { + DBUG_ASSERT(subpart_expr); + err= subpart_expr->walk(&Item::check_partition_func_processor, 0, + NULL); + } + if (err) { my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); diff --git a/sql/protocol.cc b/sql/protocol.cc index b2561f9ca11..eeca35359f6 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -27,7 +27,6 @@ #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "protocol.h" #include "sql_class.h" // THD #include <stdarg.h> diff --git a/sql/records.cc b/sql/records.cc index bfce2f83967..a37f7a18c11 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -66,10 +66,12 @@ static int rr_index_desc(READ_RECORD *info); @param reverse Scan in the reverse direction */ -void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, +bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, bool print_error, uint idx, bool reverse) { int error; + DBUG_ENTER("init_read_record_idx"); + empty_record(table); bzero((char*) info,sizeof(*info)); info->thd= thd; @@ -88,6 +90,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, /* read_record will be changed to rr_index in rr_index_first */ info->read_record= reverse ? rr_index_last : rr_index_first; + DBUG_RETURN(error != 0); } diff --git a/sql/records.h b/sql/records.h index 21477d4a30b..a3f0b5eb084 100644 --- a/sql/records.h +++ b/sql/records.h @@ -76,7 +76,7 @@ public: bool init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, SQL_SELECT *select, int use_record_cache, bool print_errors, bool disable_rr_cache); -void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, +bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, bool print_error, uint idx, bool reverse); void end_read_record(READ_RECORD *info); diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 19b193729fd..94b081bf4f4 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -15,7 +15,6 @@ #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED by later includes #include "rpl_injector.h" #include "transaction.h" #include "sql_parse.h" // begin_trans, end_trans, COMMIT diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index f322ca0e8b8..dfb7a1071c4 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -17,7 +17,6 @@ #include <my_global.h> // For HAVE_REPLICATION #include "sql_priv.h" #include <my_dir.h> -#include "unireg.h" // REQUIRED by other includes #include "rpl_mi.h" #include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD #include "strfunc.h" diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 600d2ab41aa..cc5da77303c 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -95,7 +95,6 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) if (cmp < 0) { strcpy(rli->group_master_log_name, qev->future_event_master_log_name); - rli->notify_group_master_log_name_update(); rli->group_master_log_pos= qev->future_event_master_log_pos; } else if (cmp == 0 @@ -317,13 +316,26 @@ convert_kill_to_deadlock_error(rpl_group_info *rgi) } -static bool +/* + Check if an event marks the end of an event group. Returns non-zero if so, + zero otherwise. + + In addition, returns 1 if the group is committing, 2 if it is rolling back. +*/ +static int is_group_ending(Log_event *ev, Log_event_type event_type) { - return event_type == XID_EVENT || - (event_type == QUERY_EVENT && - (((Query_log_event *)ev)->is_commit() || - ((Query_log_event *)ev)->is_rollback())); + if (event_type == XID_EVENT) + return 1; + if (event_type == QUERY_EVENT) + { + Query_log_event *qev = (Query_log_event *)ev; + if (qev->is_commit()) + return 1; + if (qev->is_rollback()) + return 2; + } + return 0; } @@ -574,7 +586,7 @@ do_retry: err= 1; goto err; } - if (is_group_ending(ev, event_type)) + if (is_group_ending(ev, event_type) == 1) rgi->mark_start_commit(); err= rpt_handle_event(qev, rpt); @@ -713,7 +725,8 @@ handle_rpl_parallel_thread(void *arg) Log_event_type event_type; rpl_group_info *rgi= qev->rgi; rpl_parallel_entry *entry= rgi->parallel_entry; - bool end_of_group, group_ending; + bool end_of_group; + int group_ending; next_qev= qev->next; if (qev->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE) @@ -888,7 +901,18 @@ handle_rpl_parallel_thread(void *arg) group_rgi= rgi; group_ending= is_group_ending(qev->ev, event_type); - if (group_ending && likely(!rgi->worker_error)) + /* + We do not unmark_start_commit() here in case of an explicit ROLLBACK + statement. Such events should be very rare, there is no real reason + to try to group commit them - on the contrary, it seems best to avoid + running them in parallel with following group commits, as with + ROLLBACK events we are already deep in dangerous corner cases with + mix of transactional and non-transactional tables or the like. And + avoiding the mark_start_commit() here allows us to keep an assertion + in ha_rollback_trans() that we do not rollback after doing + mark_start_commit(). + */ + if (group_ending == 1 && likely(!rgi->worker_error)) { /* Do an extra check for (deadlock) kill here. This helps prevent a @@ -2040,6 +2064,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, { memcpy(rli->future_event_master_log_name, rev->new_log_ident, rev->ident_len+1); + rli->notify_group_master_log_name_update(); } } diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc index 061fab78dbd..5b876373b9c 100644 --- a/sql/rpl_record_old.cc +++ b/sql/rpl_record_old.cc @@ -15,7 +15,6 @@ #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED by other includes #include "rpl_rli.h" #include "rpl_record_old.h" #include "log_event.h" // Log_event_type diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 3207c858b20..8a2a55fcde0 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -996,7 +996,6 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, if (cmp < 0) { strcpy(group_master_log_name, rgi->future_event_master_log_name); - notify_group_master_log_name_update(); group_master_log_pos= log_pos; } else if (group_master_log_pos < log_pos) @@ -1218,7 +1217,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev) if (ev && ev->server_id == (uint32) global_system_variables.server_id && !replicate_same_server_id) DBUG_RETURN(FALSE); - log_name= group_master_log_name; + log_name= (opt_slave_parallel_threads > 0 ? + future_event_master_log_name : group_master_log_name); log_pos= ((!ev)? group_master_log_pos : (get_flag(IN_TRANSACTION) || !ev->log_pos) ? group_master_log_pos : ev->log_pos - ev->data_written); diff --git a/sql/slave.cc b/sql/slave.cc index 2db78d2532d..e5bd92bf4fb 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5699,6 +5699,11 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) error_msg.append(llbuf, strlen(llbuf)); goto err; } + + /* + Heartbeat events doesn't count in the binlog size, so we don't have to + increment mi->master_log_pos + */ goto skip_relay_logging; } break; @@ -5928,6 +5933,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) else if ((s_id == global_system_variables.server_id && !mi->rli.replicate_same_server_id) || + event_that_should_be_ignored(buf) || /* the following conjunction deals with IGNORE_SERVER_IDS, if set If the master is on the ignore list, execution of diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f5652596682..bab4659105c 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2561,9 +2561,11 @@ int check_change_password(THD *thd, const char *host, const char *user, #ifdef WITH_WSREP if ((!WSREP(thd) || !thd->wsrep_applier) && - !thd->slave_thread && !thd->security_ctx->priv_user[0]) + !thd->slave_thread && !thd->security_ctx->priv_user[0] && + !in_bootstrap) #else - if (!thd->slave_thread && !thd->security_ctx->priv_user[0]) + if (!thd->slave_thread && !thd->security_ctx->priv_user[0] && + !in_bootstrap) #endif /* WITH_WSREP */ { my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER), @@ -6759,16 +6761,18 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, for (tl= tables; number-- ; tl= tl->next_global) { - sctx= MY_TEST(tl->security_ctx) ? tl->security_ctx : thd->security_ctx; + TABLE_LIST *const t_ref= + tl->correspondent_table ? tl->correspondent_table : tl; + sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; const ACL_internal_table_access *access= - get_cached_table_access(&tl->grant.m_internal, - tl->get_db_name(), - tl->get_table_name()); + get_cached_table_access(&t_ref->grant.m_internal, + t_ref->get_db_name(), + t_ref->get_table_name()); if (access) { - switch(access->check(orig_want_access, &tl->grant.privilege)) + switch(access->check(orig_want_access, &t_ref->grant.privilege)) { case ACL_INTERNAL_ACCESS_GRANTED: /* @@ -6792,26 +6796,26 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (!want_access) continue; // ok - if (!(~tl->grant.privilege & want_access) || - tl->is_anonymous_derived_table() || tl->schema_table) + if (!(~t_ref->grant.privilege & want_access) || + t_ref->is_anonymous_derived_table() || t_ref->schema_table) { /* - It is subquery in the FROM clause. VIEW set tl->derived after + It is subquery in the FROM clause. VIEW set t_ref->derived after table opening, but this function always called before table opening. */ - if (!tl->referencing_view) + if (!t_ref->referencing_view) { /* If it's a temporary table created for a subquery in the FROM clause, or an INFORMATION_SCHEMA table, drop the request for a privilege. */ - tl->grant.want_privilege= 0; + t_ref->grant.want_privilege= 0; } continue; } - if (is_temporary_table(tl)) + if (is_temporary_table(t_ref)) { /* If this table list element corresponds to a pre-opened temporary @@ -6819,8 +6823,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, Note that during creation of temporary table we still need to check if user has CREATE_TMP_ACL. */ - tl->grant.privilege|= TMP_TABLE_ACLS; - tl->grant.want_privilege= 0; + t_ref->grant.privilege|= TMP_TABLE_ACLS; + t_ref->grant.want_privilege= 0; continue; } @@ -6831,20 +6835,20 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } grant_table= table_hash_search(sctx->host, sctx->ip, - tl->get_db_name(), + t_ref->get_db_name(), sctx->priv_user, - tl->get_table_name(), + t_ref->get_table_name(), FALSE); if (sctx->priv_role[0]) - grant_table_role= table_hash_search("", NULL, tl->get_db_name(), + grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(), sctx->priv_role, - tl->get_table_name(), + t_ref->get_table_name(), TRUE); if (!grant_table && !grant_table_role) { - want_access&= ~tl->grant.privilege; - goto err; + want_access&= ~t_ref->grant.privilege; + goto err; // No grants } /* @@ -6854,19 +6858,19 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (any_combination_will_do) continue; - tl->grant.grant_table_user= grant_table; // Remember for column test - tl->grant.grant_table_role= grant_table_role; - tl->grant.version= grant_version; - tl->grant.privilege|= grant_table ? grant_table->privs : 0; - tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0; - tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege); + t_ref->grant.grant_table_user= grant_table; // Remember for column test + t_ref->grant.grant_table_role= grant_table_role; + t_ref->grant.version= grant_version; + t_ref->grant.privilege|= grant_table ? grant_table->privs : 0; + t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0; + t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege); - if (!(~tl->grant.privilege & want_access)) + if (!(~t_ref->grant.privilege & want_access)) continue; if ((want_access&= ~((grant_table ? grant_table->cols : 0) | (grant_table_role ? grant_table_role->cols : 0) | - tl->grant.privilege))) + t_ref->grant.privilege))) { goto err; // impossible } @@ -9013,8 +9017,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, So we need to examine the current element once again, but we don't need to restart the search from the beginning. */ - if (idx != elements) - idx++; + idx++; break; } @@ -9046,6 +9049,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair, (uchar*) old_key, old_key_length); + idx++; // see the comment above break; } diff --git a/sql/sql_admin.h b/sql/sql_admin.h index 77fc41e2ec4..96594fad0cb 100644 --- a/sql/sql_admin.h +++ b/sql/sql_admin.h @@ -17,7 +17,7 @@ #define SQL_TABLE_MAINTENANCE_H /* Must be able to hold ALTER TABLE t PARTITION BY ... KEY ALGORITHM = 1 ... */ -#define SQL_ADMIN_MSG_TEXT_SIZE 128 * 1024 +#define SQL_ADMIN_MSG_TEXT_SIZE (128 * 1024) bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list, LEX_STRING *key_cache_name); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 87e54b5771a..71ef74e1013 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1098,13 +1098,20 @@ bool close_temporary_tables(THD *thd) DBUG_RETURN(FALSE); DBUG_ASSERT(!thd->rgi_slave); + /* + Ensure we don't have open HANDLERs for tables we are about to close. + This is necessary when close_temporary_tables() is called as part + of execution of BINLOG statement (e.g. for format description event). + */ + mysql_ha_rm_temporary_tables(thd); if (!mysql_bin_log.is_open()) { TABLE *tmp_next; - for (table= thd->temporary_tables; table; table= tmp_next) + for (TABLE *t= thd->temporary_tables; t; t= tmp_next) { - tmp_next= table->next; - close_temporary(table, 1, 1); + tmp_next= t->next; + mysql_lock_remove(thd, thd->lock, t); + close_temporary(t, 1, 1); } thd->temporary_tables= 0; DBUG_RETURN(FALSE); @@ -1200,6 +1207,7 @@ bool close_temporary_tables(THD *thd) strlen(table->s->table_name.str)); s_query.append(','); next= table->next; + mysql_lock_remove(thd, thd->lock, table); close_temporary(table, 1, 1); } thd->clear_error(); @@ -7424,14 +7432,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, */ result= FALSE; - /* - Save the lists made during natural join matching (because - the matching done only once but we need the list in case - of prepared statements). - */ - table_ref_1->persistent_used_items= table_ref_1->used_items; - table_ref_2->persistent_used_items= table_ref_2->used_items; - err: if (arena) thd->restore_active_arena(arena, &backup); @@ -8478,11 +8478,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, } } #endif - /* - field_iterator.create_item() builds used_items which we - have to save because changes made once and they are persistent - */ - tables->persistent_used_items= tables->used_items; if ((field= field_iterator.field())) { diff --git a/sql/sql_base.h b/sql/sql_base.h index a6d90199860..aa4a041fc10 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -16,7 +16,6 @@ #ifndef SQL_BASE_INCLUDED #define SQL_BASE_INCLUDED -#include "unireg.h" // REQUIRED: for other includes #include "sql_trigger.h" /* trg_event_type */ #include "sql_class.h" /* enum_mark_columns */ #include "mysqld.h" /* key_map */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4ce99da7c7d..5f1714558eb 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -30,7 +30,6 @@ #include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_class.h" #include "sql_cache.h" // query_cache_abort #include "sql_base.h" // close_thread_tables @@ -5432,16 +5431,13 @@ void xid_cache_delete(XID_STATE *xid_state) BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-logging. - 6. Error: Cannot execute row injection: binlogging impossible since - BINLOG_FORMAT = STATEMENT. - - 7. Warning: Unsafe statement binlogged in statement format since + 6. Warning: Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. In addition, we can produce the following error (not depending on the variables of the decision diagram): - 8. Error: Cannot execute statement: binlogging impossible since more + 7. Error: Cannot execute statement: binlogging impossible since more than one engine is involved and at least one engine is self-logging. @@ -5720,10 +5716,10 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (lex->is_stmt_row_injection()) { /* - 6. Error: Cannot execute row injection since - BINLOG_FORMAT = STATEMENT + We have to log the statement as row or give an error. + Better to accept what master gives us than stopping replication. */ - my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0)); + set_current_stmt_binlog_format_row(); } else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0 && sqlcom_can_generate_row_events(this)) @@ -5755,7 +5751,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x", binlog_unsafe_warning_flags)); } - /* log in statement format! */ + /* log in statement format (or row if row event)! */ } /* No statement-only engines and binlog_format != STATEMENT. I.e., nothing prevents us from row logging if needed. */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 7b22b2eb0ce..596a2713d19 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -21,9 +21,6 @@ /* Classes in mysql */ #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ -#ifdef MYSQL_SERVER -#include "unireg.h" // REQUIRED: for other includes -#endif #include <waiting_threads.h> #include "sql_const.h" #include <mysql/plugin_audit.h> diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 7b9769cab89..096f4616300 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -28,7 +28,6 @@ #include "sql_audit.h" #include "sql_connect.h" #include "probes_mysql.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_parse.h" // sql_command_flags, // execute_init_command, // do_command diff --git a/sql/sql_const.h b/sql/sql_const.h index 2cbc616559d..5c3a6d9a7cf 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -17,12 +17,14 @@ @file File containing constants that can be used throughout the server. - @note This file shall not contain any includes of any kinds. + @note This file shall not contain or include any declarations of any kinds. */ #ifndef SQL_CONST_INCLUDED #define SQL_CONST_INCLUDED +#include <mysql_version.h> + #define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */ /* extra 4+4 bytes for slave tmp tables */ #define MAX_DBKEY_LENGTH (NAME_LEN*2+1+1+4+4) @@ -142,7 +144,7 @@ */ #define STACK_MIN_SIZE 16000 // Abort if less stack during eval. -#define STACK_MIN_SIZE_FOR_OPEN 1024*80 +#define STACK_MIN_SIZE_FOR_OPEN (1024*80) #define STACK_BUFF_ALLOC 352 ///< For stack overrun checks #ifndef MYSQLD_NET_RETRY_COUNT #define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int. @@ -238,7 +240,7 @@ #define DEFAULT_CONCURRENCY 10 #define DELAYED_LIMIT 100 /**< pause after xxx inserts */ #define DELAYED_QUEUE_SIZE 1000 -#define DELAYED_WAIT_TIMEOUT 5*60 /**< Wait for delayed insert */ +#define DELAYED_WAIT_TIMEOUT (5*60) /**< Wait for delayed insert */ #define MAX_CONNECT_ERRORS 100 ///< errors before disabling host #define LONG_TIMEOUT ((ulong) 3600L*24L*365L) diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 2a57066c17d..4de7f88d736 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -508,17 +508,18 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, free_underlaid_joins(thd, select_lex); DBUG_RETURN(TRUE); } + if (query_plan.index == MAX_KEY || (select && select->quick)) + error= init_read_record(&info, thd, table, select, 1, 1, FALSE); + else + error= init_read_record_idx(&info, thd, table, 1, query_plan.index, + reverse); + if (error) { - if (init_read_record(&info, thd, table, select, 1, 1, FALSE)) - { - delete select; - free_underlaid_joins(thd, select_lex); - DBUG_RETURN(TRUE); - } + delete select; + free_underlaid_joins(thd, select_lex); + DBUG_RETURN(TRUE); } - else - init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); init_ftfuncs(thd, select_lex, 1); THD_STAGE_INFO(thd, stage_updating); diff --git a/sql/sql_digest.h b/sql/sql_digest.h index ce159283d4d..eaf74b9542e 100644 --- a/sql/sql_digest.h +++ b/sql/sql_digest.h @@ -41,6 +41,9 @@ struct sql_digest_storage For Example: SELECT * FROM T1; <SELECT_TOKEN> <*> <FROM_TOKEN> <ID_TOKEN> <2> <T1> + + @note Only the first @c m_byte_count bytes are initialized, + out of @c m_token_array_length. */ unsigned char *m_token_array; /* Length of the token array to be considered for DIGEST_TEXT calculation. */ @@ -63,10 +66,6 @@ struct sql_digest_storage m_full= false; m_byte_count= 0; m_charset_number= 0; - if (m_token_array_length > 0) - { - memset(m_token_array, 0, m_token_array_length); - } memset(m_md5, 0, MD5_HASH_SIZE); } diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 3e18b701031..b2fa8187925 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -510,8 +510,10 @@ Diagnostics_area::set_error_status(uint sql_errno, void Diagnostics_area::disable_status() { + DBUG_ENTER("disable_status"); DBUG_ASSERT(! is_set()); m_status= DA_DISABLED; + DBUG_VOID_RETURN; } Warning_info::Warning_info(ulonglong warn_id_arg, diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 110bca96530..5fc7c20d409 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2001, 2013, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab. +/* Copyright (c) 2001, 2015, Oracle and/or its affiliates. + Copyright (c) 2011, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -55,7 +55,6 @@ #include <my_global.h> #include "sql_priv.h" #include "sql_handler.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_base.h" // close_thread_tables #include "lock.h" // mysql_unlock_tables #include "key.h" // key_copy @@ -1190,3 +1189,36 @@ void mysql_ha_set_explicit_lock_duration(THD *thd) DBUG_VOID_RETURN; } + +/** + Remove temporary tables from the HANDLER's hash table. The reason + for having a separate function, rather than calling + mysql_ha_rm_tables() is that it is not always feasible (e.g. in + close_temporary_tables) to obtain a TABLE_LIST containing the + temporary tables. + + @See close_temporary_tables + @param thd Thread identifier. +*/ +void mysql_ha_rm_temporary_tables(THD *thd) +{ + DBUG_ENTER("mysql_ha_rm_temporary_tables"); + + TABLE_LIST *tmp_handler_tables= NULL; + for (uint i= 0; i < thd->handler_tables_hash.records; i++) + { + TABLE_LIST *handler_table= reinterpret_cast<TABLE_LIST*> + (my_hash_element(&thd->handler_tables_hash, i)); + + if (handler_table->table && handler_table->table->s->tmp_table) + { + handler_table->next_local= tmp_handler_tables; + tmp_handler_tables= handler_table; + } + } + + if (tmp_handler_tables) + mysql_ha_rm_tables(thd, tmp_handler_tables); + + DBUG_VOID_RETURN; +} diff --git a/sql/sql_handler.h b/sql/sql_handler.h index 133f553675e..7fe5ae5bba8 100644 --- a/sql/sql_handler.h +++ b/sql/sql_handler.h @@ -1,6 +1,8 @@ #ifndef SQL_HANDLER_INCLUDED #define SQL_HANDLER_INCLUDED -/* Copyright (C) 2010 Monty Program Ab +/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. + Copyright (C) 2010, 2015, MariaDB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. @@ -73,6 +75,7 @@ void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables); void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables); void mysql_ha_cleanup(THD *thd); void mysql_ha_set_explicit_lock_duration(THD *thd); +void mysql_ha_rm_temporary_tables(THD *thd); SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, enum enum_ha_read_modes mode, char *keyname, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 6af2c0646ba..4e033f21b25 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2010, 2014, SkySQL Ab. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,7 +58,6 @@ #include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_insert.h" #include "sql_update.h" // compare_record #include "sql_base.h" // close_thread_tables @@ -3899,7 +3898,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, /* Add selected items to field list */ List_iterator_fast<Item> it(*items); Item *item; - Field *tmp_field; DBUG_ENTER("create_table_from_items"); tmp_table.alias= 0; @@ -3914,24 +3912,49 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, while ((item=it++)) { - Create_field *cr_field; - Field *field, *def_field; + Field *tmp_table_field; if (item->type() == Item::FUNC_ITEM) { if (item->result_type() != STRING_RESULT) - field= item->tmp_table_field(&tmp_table); + tmp_table_field= item->tmp_table_field(&tmp_table); else - field= item->tmp_table_field_from_field_type(&tmp_table, 0); + tmp_table_field= item->tmp_table_field_from_field_type(&tmp_table, + false); } else - field= create_tmp_field(thd, &tmp_table, item, item->type(), - (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0, - 0); - if (!field || - !(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ? - ((Item_field *)item)->field : - (Field*) 0)))) - DBUG_RETURN(0); + { + Field *from_field, * default_field; + tmp_table_field= create_tmp_field(thd, &tmp_table, item, item->type(), + (Item ***) NULL, &from_field, &default_field, + 0, 0, 0, 0, 0); + } + + if (!tmp_table_field) + DBUG_RETURN(NULL); + + Field *table_field; + + switch (item->type()) + { + /* + We have to take into account both the real table's fields and + pseudo-fields used in trigger's body. These fields are used + to copy defaults values later inside constructor of + the class Create_field. + */ + case Item::FIELD_ITEM: + case Item::TRIGGER_FIELD_ITEM: + table_field= ((Item_field *) item)->field; + break; + default: + table_field= NULL; + } + + Create_field *cr_field= new Create_field(tmp_table_field, table_field); + + if (!cr_field) + DBUG_RETURN(NULL); + if (item->maybe_null) cr_field->flags &= ~NOT_NULL_FLAG; alter_info->create_list.push_back(cr_field); @@ -4021,7 +4044,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, { if (!thd->is_error()) // CREATE ... IF NOT EXISTS my_ok(thd); // succeed, but did nothing - DBUG_RETURN(0); + DBUG_RETURN(NULL); } DEBUG_SYNC(thd,"create_table_select_before_lock"); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 26f4eabb1f9..4aad94b8188 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -20,7 +20,6 @@ #define MYSQL_LEX 1 #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_class.h" // sql_lex.h: SQLCOM_END #include "sql_lex.h" #include "sql_parse.h" // add_to_list @@ -568,6 +567,16 @@ void lex_end(LEX *lex) DBUG_ENTER("lex_end"); DBUG_PRINT("enter", ("lex: 0x%lx", (long) lex)); + lex_end_stage1(lex); + lex_end_stage2(lex); + + DBUG_VOID_RETURN; +} + +void lex_end_stage1(LEX *lex) +{ + DBUG_ENTER("lex_end_stage1"); + /* release used plugins */ if (lex->plugins.elements) /* No function call and no mutex if no plugins. */ { @@ -579,6 +588,19 @@ void lex_end(LEX *lex) delete lex->sphead; lex->sphead= NULL; + DBUG_VOID_RETURN; +} + +/* + MASTER INFO parameters (or state) is normally cleared towards the end + of a statement. But in case of PS, the state needs to be preserved during + its lifetime and should only be cleared on PS close or deallocation. +*/ +void lex_end_stage2(LEX *lex) +{ + DBUG_ENTER("lex_end_stage2"); + + /* Reset LEX_MASTER_INFO */ lex->mi.reset(); DBUG_VOID_RETURN; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3d008723f66..413afdb4369 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2947,6 +2947,8 @@ extern void lex_init(void); extern void lex_free(void); extern void lex_start(THD *thd); extern void lex_end(LEX *lex); +extern void lex_end_stage1(LEX *lex); +extern void lex_end_stage2(LEX *lex); void end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex); int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex); extern int MYSQLlex(union YYSTYPE *yylval, THD *thd); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 503cc579dd7..08687b20b00 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2014, Oracle and/or its affiliates. - Copyright (c) 2010, 2014, SkySQL Ab. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2028,8 +2028,15 @@ int READ_INFO::read_xml() break; case '/': /* close tag */ - level--; chr= my_tospace(GET); + /* Decrease the 'level' only when (i) It's not an */ + /* (without space) empty tag i.e. <tag/> or, (ii) */ + /* It is of format <row col="val" .../> */ + if(chr != '>' || in_tag) + { + level--; + in_tag= false; + } if(chr != '>') /* if this is an empty tag <tag /> */ tag.length(0); /* we should keep tag value */ while(chr != '>' && chr != my_b_EOF) diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index c6c465aa4e2..8cf849b97d0 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -24,7 +24,6 @@ #include <my_global.h> #include "sql_priv.h" #include "sql_manager.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_base.h" // flush_tables static bool volatile manager_thread_in_use; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ca7b236eb04..03367acab08 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2014, SkySQL Ab. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,6 @@ #define MYSQL_LEX 1 #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_parse.h" // sql_kill, *_precheck, *_prepare #include "lock.h" // try_transactional_lock, // check_transactional_lock, @@ -677,7 +676,7 @@ static char *fgets_fn(char *buffer, size_t size, fgets_input_t input, int *error static void handle_bootstrap_impl(THD *thd) { MYSQL_FILE *file= bootstrap_file; - DBUG_ENTER("handle_bootstrap"); + DBUG_ENTER("handle_bootstrap_impl"); #ifndef EMBEDDED_LIBRARY pthread_detach_this_thread(); @@ -3887,7 +3886,10 @@ end_with_restore_list: lex->duplicates, lex->ignore))) { - res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE); + if (explain) + res= mysql_explain_union(thd, &thd->lex->unit, sel_result); + else + res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE); /* Invalidate the table in the query cache if something changed after unlocking when changes become visible. @@ -3903,6 +3905,16 @@ end_with_restore_list: query_cache_invalidate3(thd, first_table, 1); first_table->next_local= save_table; } + if (explain) + { + /* + sel_result needs to be cleaned up properly. + INSERT... SELECT statement will call either send_eof() or + abort_result_set(). EXPLAIN doesn't call either, so we need + to cleanup manually. + */ + sel_result->abort_result_set(); + } delete sel_result; } @@ -6326,9 +6338,12 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, for (; i < number && tables != first_not_own_table && tables; tables= tables->next_global, i++) { + TABLE_LIST *const table_ref= tables->correspondent_table ? + tables->correspondent_table : tables; + ulong want_access= requirements; - if (tables->security_ctx) - sctx= tables->security_ctx; + if (table_ref->security_ctx) + sctx= table_ref->security_ctx; else sctx= backup_ctx; @@ -6336,26 +6351,26 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, Register access for view underlying table. Remove SHOW_VIEW_ACL, because it will be checked during making view */ - tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); + table_ref->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); - if (tables->schema_table_reformed) + if (table_ref->schema_table_reformed) { - if (check_show_access(thd, tables)) + if (check_show_access(thd, table_ref)) goto deny; continue; } - DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0, - tables->view != 0)); + DBUG_PRINT("info", ("derived: %d view: %d", table_ref->derived != 0, + table_ref->view != 0)); - if (tables->is_anonymous_derived_table()) + if (table_ref->is_anonymous_derived_table()) continue; thd->security_ctx= sctx; - if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, - &tables->grant.m_internal, + if (check_access(thd, want_access, table_ref->get_db_name(), + &table_ref->grant.privilege, + &table_ref->grant.m_internal, 0, no_errors)) goto deny; } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index f0fde223984..be7824aae9e 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -49,7 +49,6 @@ #define MYSQL_LEX 1 #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_partition.h" #include "key.h" // key_restore #include "sql_parse.h" // parse_sql diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 27d37d9fb83..4b13139014d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3456,7 +3456,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) thd->mdl_context.release_transactional_locks(); } - lex_end(lex); + /* Preserve CHANGE MASTER attributes */ + lex_end_stage1(lex); cleanup_stmt(); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; @@ -4078,6 +4079,10 @@ void Prepared_statement::deallocate() { /* We account deallocate in the same manner as mysqld_stmt_close */ status_var_increment(thd->status_var.com_stmt_close); + + /* It should now be safe to reset CHANGE MASTER parameters */ + lex_end_stage2(lex); + /* Statement map calls delete stmt on erase */ thd->stmt_map.erase(this); } diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 26d515842ed..6b30dd28a51 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -31,7 +31,6 @@ #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_profile.h" #include <my_sys.h> #include "sql_show.h" // schema_table_store_record diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index f064146e7fc..ca99a81b455 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -93,6 +93,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, my_error(ER_UNKNOWN_ERROR, MYF(0)); } } + opt_noacl= 0; if (tmp_thd) { diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 6307108f4b2..af02c1ca966 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -2869,7 +2869,19 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) if (init_master_info(mi,master_info_file_tmp,relay_log_info_file_tmp, 0, thread_mask)) slave_errno=ER_MASTER_INFO; - else if (server_id_supplied && *mi->host) + else if (!server_id_supplied) + { + slave_errno= ER_BAD_SLAVE; net_report= 0; + my_message(slave_errno, "Misconfigured slave: server_id was not set; Fix in config file", + MYF(0)); + } + else if (!*mi->host) + { + slave_errno= ER_BAD_SLAVE; net_report= 0; + my_message(slave_errno, "Misconfigured slave: MASTER_HOST was not set; Fix in config file or with CHANGE MASTER TO", + MYF(0)); + } + else { /* If we will start SQL thread we will care about UNTIL options If @@ -2963,8 +2975,6 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) relay_log_info_file_tmp, thread_mask); } - else - slave_errno = ER_BAD_SLAVE; } else { @@ -3321,7 +3331,8 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) *master_info_added= true; } if (global_system_variables.log_warnings > 1) - sql_print_information("Master: '%.*s' Master_info_file: '%s' " + sql_print_information("Master connection name: '%.*s' " + "Master_info_file: '%s' " "Relay_info_file: '%s'", (int) mi->connection_name.length, mi->connection_name.str, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index aa9a3f2da0e..edfd9f5ebd3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1530,6 +1530,9 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S /* Cache constant expressions in WHERE, HAVING, ON clauses. */ cache_const_exprs(); + if (setup_semijoin_loosescan(this)) + DBUG_RETURN(1); + if (make_join_select(this, select, conds)) { zero_result_cause= @@ -9676,9 +9679,14 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) Check again if we should use an index. We could have used an column from a previous table in the index if we are using limit and this is the first table + + (1) - Don't switch the used index if we are using semi-join + LooseScan on this table. Using different index will not + produce the desired ordering and de-duplication. */ if (!tab->table->is_filled_at_execution() && + !tab->loosescan_match_tab && // (1) ((cond && (!tab->keys.is_subset(tab->const_keys) && i > 0)) || (!tab->const_keys.is_clear_all() && i == join->const_tables && join->unit->select_limit_cnt < @@ -15592,6 +15600,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::FIELD_ITEM: case Item::DEFAULT_VALUE_ITEM: case Item::INSERT_VALUE_ITEM: + case Item::TRIGGER_FIELD_ITEM: { Item_field *field= (Item_field*) item; bool orig_modify= modify_item; @@ -18043,6 +18052,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, if (return_tab < join->return_tab) join->return_tab= return_tab; + /* check for errors evaluating the condition */ + if (join->thd->is_error()) + DBUG_RETURN(NESTED_LOOP_ERROR); + if (join->return_tab < join_tab) DBUG_RETURN(NESTED_LOOP_OK); /* diff --git a/sql/sql_select.h b/sql/sql_select.h index 61157130f50..bbeb2aa6952 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -717,8 +717,7 @@ public: struct st_position *pos, struct st_position *loose_scan_pos); friend bool get_best_combination(JOIN *join); - friend int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, - uint no_jbuf_after); + friend int setup_semijoin_loosescan(JOIN *join); friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c2ae37248b8..d1a24345443 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -8156,13 +8156,14 @@ bool get_schema_tables_result(JOIN *join, TABLE_LIST *table_list= tab->table->pos_in_table_list; if (table_list->schema_table && thd->fill_information_schema_tables()) { -#if MYSQL_VERSION_ID > 100105 -#error I_S tables only need to be re-populated if make_cond_for_info_schema() will preserve outer fields - bool is_subselect= (&lex->unit != lex->current_select->master_unit() && - lex->current_select->master_unit()->item); -#else -#define is_subselect false -#endif + /* + I_S tables only need to be re-populated if make_cond_for_info_schema() + preserves outer fields + */ + bool is_subselect= &lex->unit != lex->current_select->master_unit() && + lex->current_select->master_unit()->item && + tab->select_cond && + tab->select_cond->used_tables() & OUTER_REF_TABLE_BIT; /* A value of 0 indicates a dummy implementation */ if (table_list->schema_table->fill_table == 0) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6449f8f22f0..65d601968ba 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5909,6 +5909,16 @@ drop_create_field: { if (!key->create_if_not_exists) continue; + + /* Check if the table already has a PRIMARY KEY */ + if (key->type == Key::PRIMARY && + table->s->primary_key != MAX_KEY) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DUP_KEYNAME, ER(ER_MULTIPLE_PRI_KEY)); + goto remove_key_no_warn; + } + /* If the name of the key is not specified, */ /* let us check the name of the first key part. */ if ((keyname= key->name.str) == NULL) @@ -5975,6 +5985,7 @@ drop_create_field: remove_key: push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), keyname); +remove_key_no_warn: key_it.remove(); if (key->type == Key::FOREIGN_KEY) { diff --git a/sql/sql_time.cc b/sql/sql_time.cc index ca689d55a2b..f2596401c2e 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -19,7 +19,6 @@ #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED by other includes #include "sql_time.h" #include "tztime.h" // struct Time_zone #include "sql_class.h" // THD diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 52892550d35..7dfe8939945 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <mysqld_error.h> + /* Forward declarations */ class Item_trigger_field; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index a316fbad726..71875433c39 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -286,8 +286,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, { if (!(tmp_result= union_result= new select_union)) goto err; - if (describe) - tmp_result= sel_result; } else tmp_result= sel_result; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 150ef50f81c..e64aebcb78c 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -22,7 +22,6 @@ #include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_update.h" #include "sql_cache.h" // query_cache_* #include "sql_base.h" // close_tables_for_reopen @@ -459,7 +458,8 @@ int mysql_update(THD *thd, query_plan.scanned_rows= select? select->records: table->file->stats.records; if (select && select->quick && select->quick->unique_key_range()) - { // Single row select (always "ordered"): Ok to use with key field UPDATE + { + /* Single row select (always "ordered"): Ok to use with key field UPDATE */ need_sort= FALSE; query_plan.index= MAX_KEY; used_key_is_modified= FALSE; @@ -468,7 +468,8 @@ int mysql_update(THD *thd, { ha_rows scanned_limit= query_plan.scanned_rows; query_plan.index= get_index_for_order(order, table, select, limit, - &scanned_limit, &need_sort, &reverse); + &scanned_limit, &need_sort, + &reverse); if (!need_sort) query_plan.scanned_rows= scanned_limit; @@ -481,12 +482,15 @@ int mysql_update(THD *thd, else { if (need_sort) - { // Assign table scan index to check below for modified key fields: + { + /* Assign table scan index to check below for modified key fields: */ query_plan.index= table->file->key_used_on_scan; } if (query_plan.index != MAX_KEY) - { // Check if we are modifying a key that we are used to search with: - used_key_is_modified= is_key_used(table, query_plan.index, table->write_set); + { + /* Check if we are modifying a key that we are used to search with: */ + used_key_is_modified= is_key_used(table, query_plan.index, + table->write_set); } } } @@ -597,19 +601,20 @@ int mysql_update(THD *thd, B. query_plan.index != MAX_KEY B.1 quick select is used, start the scan with init_read_record B.2 quick select is not used, this is full index scan (with LIMIT) - Full index scan must be started with init_read_record_idx + Full index scan must be started with init_read_record_idx */ if (query_plan.index == MAX_KEY || (select && select->quick)) + error= init_read_record(&info, thd, table, select, 0, 1, FALSE); + else + error= init_read_record_idx(&info, thd, table, 1, query_plan.index, + reverse); + + if (error) { - if (init_read_record(&info, thd, table, select, 0, 1, FALSE)) - { - close_cached_file(&tempfile); - goto err; - } + close_cached_file(&tempfile); + goto err; } - else - init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); THD_STAGE_INFO(thd, stage_searching_rows_for_update); ha_rows tmp_limit= limit; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b2e3b0e46c0..13f963e2f4c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -35,7 +35,6 @@ #define Select Lex->current_select #include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_parse.h" /* comp_*_creator */ #include "sql_table.h" /* primary_key_name */ #include "sql_partition.h" /* mem_alloc_error, partition_info, HASH_PARTITION */ @@ -11513,8 +11512,20 @@ procedure_clause: if (add_proc_to_list(lex->thd, item)) MYSQL_YYABORT; Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + /* + PROCEDURE CLAUSE cannot handle subquery as one of its parameter, + so set expr_allows_subselect as false to disallow any subqueries + further. Reset expr_allows_subselect back to true once the + parameters are reduced. + */ + Lex->expr_allows_subselect= false; } '(' procedure_list ')' + { + /* Subqueries are allowed from now.*/ + Lex->expr_allows_subselect= true; + } ; procedure_list: @@ -15164,6 +15175,9 @@ current_role: grant_role: ident_or_text { + CHARSET_INFO *cs= system_charset_info; + /* trim end spaces (as they'll be lost in mysql.user anyway) */ + $1.length= cs->cset->lengthsp(cs, $1.str, $1.length); if ($1.length == 0) { my_error(ER_INVALID_ROLE, MYF(0), ""); @@ -15178,8 +15192,7 @@ grant_role: $$->auth= empty_lex_str; if (check_string_char_length(&$$->user, ER(ER_USERNAME), - username_char_length, - system_charset_info, 0)) + username_char_length, cs, 0)) MYSQL_YYABORT; } | current_role diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 16e498c2937..4d2e35bac87 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -836,30 +836,26 @@ static Sys_var_ulong Sys_delayed_queue_size( VALID_RANGE(1, UINT_MAX), DEFAULT(DELAYED_QUEUE_SIZE), BLOCK_SIZE(1)); #ifdef HAVE_EVENT_SCHEDULER -static const char *event_scheduler_names[]= { "OFF", "ON", "DISABLED", NullS }; +static const char *event_scheduler_names[]= { "OFF", "ON", "DISABLED", + "ORIGINAL", NullS }; static bool event_scheduler_check(sys_var *self, THD *thd, set_var *var) { - /* DISABLED is only accepted on the command line */ - if (var->save_result.ulonglong_value == Events::EVENTS_DISABLED) - return true; - /* - If the scheduler was disabled because there are no/bad - system tables, produce a more meaningful error message - than ER_OPTION_PREVENTS_STATEMENT - */ - if (Events::check_if_system_tables_error()) - return true; if (Events::opt_event_scheduler == Events::EVENTS_DISABLED) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables"); return true; } + /* DISABLED is only accepted on the command line */ + if (var->save_result.ulonglong_value == Events::EVENTS_DISABLED) + return true; return false; } + static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type) { int err_no= 0; + bool ret; uint opt_event_scheduler_value= Events::opt_event_scheduler; mysql_mutex_unlock(&LOCK_global_system_variables); /* @@ -878,9 +874,25 @@ static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type) rare and it's difficult to avoid it without opening up possibilities for deadlocks. See bug#51160. */ - bool ret= opt_event_scheduler_value == Events::EVENTS_ON - ? Events::start(&err_no) - : Events::stop(); + + /* EVENTS_ORIGINAL means we should revert back to the startup state */ + if (opt_event_scheduler_value == Events::EVENTS_ORIGINAL) + { + opt_event_scheduler_value= Events::opt_event_scheduler= + Events::startup_state; + } + + /* + If the scheduler was not properly inited (because of wrong system tables), + try to init it again. This is needed for mysql_upgrade to work properly if + the event tables where upgraded. + */ + if (!Events::inited && (Events::init(thd, 0) || !Events::inited)) + ret= 1; + else + ret= opt_event_scheduler_value == Events::EVENTS_ON ? + Events::start(&err_no) : + Events::stop(); mysql_mutex_lock(&LOCK_global_system_variables); if (ret) { diff --git a/sql/table.cc b/sql/table.cc index 9cb33e92d89..90cdc7089b9 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -19,7 +19,6 @@ #include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "table.h" #include "key.h" // find_ref_key #include "sql_table.h" // build_table_filename, @@ -5338,6 +5337,12 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, item->maybe_null= TRUE; /* Save item in case we will need to fall back to materialization. */ view->used_items.push_front(item); + /* + If we create this reference on persistent memory then it should be + present in persistent list + */ + if (thd->mem_root == thd->stmt_arena->mem_root) + view->persistent_used_items.push_front(item); DBUG_RETURN(item); } @@ -6915,6 +6920,7 @@ bool TABLE_LIST::handle_derived(LEX *lex, uint phases) { SELECT_LEX_UNIT *unit; DBUG_ENTER("handle_derived"); + DBUG_PRINT("enter", ("phases: 0x%x", phases)); if ((unit= get_unit())) { for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 68c032fb67b..e720e43498a 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -1634,7 +1634,7 @@ int tp_get_idle_thread_count() Delay in microseconds, after which "pool blocked" message is printed. (30 sec == 30 Mio usec) */ -#define BLOCK_MSG_DELAY 30*1000000 +#define BLOCK_MSG_DELAY (30*1000000) #define MAX_THREADS_REACHED_MSG \ "Threadpool could not create additional thread to handle queries, because the \ diff --git a/sql/unireg.h b/sql/unireg.h index 2d51aa39fd4..b13dd494c74 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -110,36 +110,36 @@ The flag means that we need to process tables only to get necessary data. Views are not processed. */ -#define OPEN_TABLE_ONLY OPEN_FRM_FILE_ONLY*2 +#define OPEN_TABLE_ONLY (OPEN_FRM_FILE_ONLY*2) /** This flag is used in function get_all_tables() which fills I_S tables with data which are retrieved from frm files and storage engine The flag means that we need to process views only to get necessary data. Tables are not processed. */ -#define OPEN_VIEW_ONLY OPEN_TABLE_ONLY*2 +#define OPEN_VIEW_ONLY (OPEN_TABLE_ONLY*2) /** This flag is used in function get_all_tables() which fills I_S tables with data which are retrieved from frm files and storage engine. The flag means that we need to open a view using open_normal_and_derived_tables() function. */ -#define OPEN_VIEW_FULL OPEN_VIEW_ONLY*2 +#define OPEN_VIEW_FULL (OPEN_VIEW_ONLY*2) /** This flag is used in function get_all_tables() which fills I_S tables with data which are retrieved from frm files and storage engine. The flag means that I_S table uses optimization algorithm. */ -#define OPTIMIZE_I_S_TABLE OPEN_VIEW_FULL*2 +#define OPTIMIZE_I_S_TABLE (OPEN_VIEW_FULL*2) /** This flag is used to instruct tdc_open_view() to check metadata version. */ -#define CHECK_METADATA_VERSION OPEN_TRIGGER_ONLY*2 +#define CHECK_METADATA_VERSION (OPEN_TRIGGER_ONLY*2) /* The flag means that we need to process trigger files only. */ -#define OPEN_TRIGGER_ONLY OPTIMIZE_I_S_TABLE*2 +#define OPEN_TRIGGER_ONLY (OPTIMIZE_I_S_TABLE*2) #define SC_INFO_LENGTH 4 /* Form format constant */ #define TE_INFO_LENGTH 3 |