diff options
Diffstat (limited to 'sql')
303 files changed, 18511 insertions, 5113 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index b3c99cc4788..9f796896a41 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -36,7 +36,7 @@ ELSE() ENDIF() INCLUDE_DIRECTORIES( -${CMAKE_SOURCE_DIR}/include +${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql ${PCRE_INCLUDES} ${ZLIB_INCLUDE_DIR} @@ -77,7 +77,8 @@ ENDIF() SET (SQL_SOURCE ../sql-common/client.c compat56.cc derror.cc des_key_file.cc - discover.cc ../sql-common/errmsg.c field.cc field_conv.cc + discover.cc ../sql-common/errmsg.c + field.cc field_conv.cc field_comp.cc filesort_utils.cc filesort.cc gstream.cc signal_handler.cc @@ -121,7 +122,7 @@ SET (SQL_SOURCE rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc sql_connect.cc scheduler.cc sql_partition_admin.cc sql_profile.cc event_parse_data.cc sql_alter.cc - sql_signal.cc rpl_handler.cc mdl.cc sql_admin.cc + sql_signal.cc mdl.cc sql_admin.cc transaction.cc sys_vars.cc sql_truncate.cc datadict.cc sql_reload.cc item_inetfunc.cc @@ -137,12 +138,17 @@ SET (SQL_SOURCE my_apc.cc mf_iocache_encr.cc item_jsonfunc.cc my_json_writer.cc rpl_gtid.cc rpl_parallel.cc + semisync.cc semisync_master.cc semisync_slave.cc + semisync_master_ack_receiver.cc sql_type.cc item_windowfunc.cc sql_window.cc sql_cte.cc sql_sequence.cc sql_sequence.h ha_sequence.h + sql_tvc.cc sql_tvc.h + opt_split.cc ${WSREP_SOURCES} table_cache.cc encryption.cc temporary_tables.cc + proxy_protocol.cc ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE} diff --git a/sql/authors.h b/sql/authors.h index 3a8f5497248..609b77059f4 100644 --- a/sql/authors.h +++ b/sql/authors.h @@ -75,6 +75,8 @@ struct show_table_authors_st show_table_authors[]= { "Prepared statements (4.1), Cursors (5.0), GET_LOCK (10.0)" }, { "Ian Gilfillan", "South Africa", "MariaDB documentation"}, { "Federico Razolli", "Italy", "MariaDB documentation Italian translation"}, + { "Vinchen", "Shenzhen, China", "Instant ADD Column for InnoDB, Spider engine optimization, from Tencent Game DBA Team" }, + { "Willhan", "Shenzhen, China", "Big Column Compression, Spider engine optimization, from Tencent Game DBA Team" }, /* People working on MySQL code base (not NDB) */ { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" }, diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h index 3573c5ceb27..d7c28215631 100644 --- a/sql/bounded_queue.h +++ b/sql/bounded_queue.h @@ -16,9 +16,8 @@ #ifndef BOUNDED_QUEUE_INCLUDED #define BOUNDED_QUEUE_INCLUDED -#include "my_global.h" #include "my_base.h" -#include "my_sys.h" +#include <my_sys.h> #include "queues.h" #include <string.h> diff --git a/sql/compat56.cc b/sql/compat56.cc index 704d1db9a98..6cbf4a66ac8 100644 --- a/sql/compat56.cc +++ b/sql/compat56.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" +#include "mariadb.h" #include "compat56.h" #include "myisampack.h" #include "my_time.h" diff --git a/sql/create_options.cc b/sql/create_options.cc index 22e58f8801b..d010b73c222 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -19,6 +19,7 @@ Engine defined options of tables/fields/keys in CREATE/ALTER TABLE. */ +#include "mariadb.h" #include "create_options.h" #include <my_getopt.h> #include "set_var.h" diff --git a/sql/datadict.cc b/sql/datadict.cc index 5a4d8bbb8bf..4e18fe06cb6 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "datadict.h" #include "sql_priv.h" #include "sql_class.h" @@ -24,7 +24,7 @@ static int read_string(File file, uchar**to, size_t length) { DBUG_ENTER("read_string"); - my_free(*to); + /* This can't use MY_THREAD_SPECIFIC as it's used on server start */ if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) || mysql_file_read(file, *to, length, MYF(MY_NABP))) { @@ -51,7 +51,7 @@ static int read_string(File file, uchar**to, size_t length) @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise - @retval TABLE_TYPE_UNKNOWN error + @retval TABLE_TYPE_UNKNOWN error - file can't be opened @retval TABLE_TYPE_NORMAL table @retval TABLE_TYPE_SEQUENCE sequence table @retval TABLE_TYPE_VIEW view diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 6b09978d128..d44b313ec24 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -15,7 +15,7 @@ /* see include/mysql/service_debug_sync.h for debug sync documentation */ -#include <my_global.h> +#include "mariadb.h" #include "debug_sync.h" #if defined(ENABLED_DEBUG_SYNC) @@ -1457,7 +1457,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) ER_DEBUG_SYNC_TIMEOUT, ER_THD(thd, ER_DEBUG_SYNC_TIMEOUT)); thd->abort_on_warning= save_abort_on_warning; - DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ABORT();); + DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ASSERT(0);); break; } error= 0; diff --git a/sql/debug_sync.h b/sql/debug_sync.h index 999667b9efc..70d28cb982b 100644 --- a/sql/debug_sync.h +++ b/sql/debug_sync.h @@ -26,8 +26,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_global.h> - class THD; #if defined(ENABLED_DEBUG_SYNC) diff --git a/sql/derror.cc b/sql/derror.cc index 5a1bee23f4a..8be1c26b7e4 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -21,7 +21,7 @@ Read language depeneded messagefile */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "derror.h" diff --git a/sql/derror.h b/sql/derror.h index 9f2aee71c7e..a171a248190 100644 --- a/sql/derror.h +++ b/sql/derror.h @@ -16,8 +16,6 @@ #ifndef DERROR_INCLUDED #define DERROR_INCLUDED -#include "my_global.h" /* uint */ - bool init_errmessage(void); void free_error_messages(); bool read_texts(const char *file_name, const char *language, diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc index ede2e9fa9d4..e7785a0a223 100644 --- a/sql/des_key_file.cc +++ b/sql/des_key_file.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> // HAVE_* +#include "mariadb.h" // HAVE_* #include "sql_priv.h" #include "des_key_file.h" // st_des_keyschedule, st_des_keyblock #include "log.h" // sql_print_error diff --git a/sql/discover.cc b/sql/discover.cc index 62a0084e2e7..a683166fb7f 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -21,7 +21,7 @@ Functions for discover of frm file from handler */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "discover.h" diff --git a/sql/discover.h b/sql/discover.h index e1508107235..c3127c3bff3 100644 --- a/sql/discover.h +++ b/sql/discover.h @@ -16,8 +16,6 @@ #ifndef DISCOVER_INCLUDED #define DISCOVER_INCLUDED -#include "my_global.h" /* uchar */ - int extension_based_table_discovery(MY_DIR *dirp, const char *ext, handlerton::discovered_list *tl); diff --git a/sql/encryption.cc b/sql/encryption.cc index 52eab262570..3174968f9b3 100644 --- a/sql/encryption.cc +++ b/sql/encryption.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <mysql/plugin_encryption.h> #include "log.h" #include "sql_plugin.h" diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index ef334233085..c85192ee258 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MYSQL_LEX 1 -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_parse.h" // parse_sql @@ -1236,7 +1236,7 @@ Event_timed::get_create_event(THD *thd, String *buf) append_unescaped(buf, comment.str, comment.length); } buf->append(STRING_WITH_LEN(" DO ")); - buf->append(body.str, body.length); + buf->append(&body); DBUG_RETURN(0); } @@ -1284,7 +1284,7 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql) */ sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER ")); - sp_sql->append(body.str, body.length); + sp_sql->append(&body); DBUG_RETURN(thd->is_fatal_error); } diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 20c2384ff04..9d899cb637e 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" // close_thread_tables @@ -499,7 +499,7 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table, */ do { - ret= read_record_info.read_record(&read_record_info); + ret= read_record_info.read_record(); if (ret == 0) ret= copy_event_to_schema_table(thd, schema_table, event_table); } while (ret == 0); @@ -1008,7 +1008,7 @@ Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema) if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE)) goto end; - while (!ret && !(read_record_info.read_record(&read_record_info)) ) + while (!ret && !(read_record_info.read_record())) { char *et_field= get_field(thd->mem_root, table->field[field]); diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index e203712e229..b2ff80626db 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sp_head.h" diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h index e1aed36aa01..d2e14d74cf8 100644 --- a/sql/event_parse_data.h +++ b/sql/event_parse_data.h @@ -17,7 +17,7 @@ #ifndef _EVENT_PARSE_DATA_H_ #define _EVENT_PARSE_DATA_H_ -#include "sql_list.h" /* Sql_alloc */ +#include "sql_alloc.h" class Item; class THD; diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 208ad55c5f5..e46326afe18 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "event_queue.h" diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 225a3172dc1..d0a2405526a 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "event_scheduler.h" @@ -85,7 +85,7 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE]; String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); prefix.length(0); - prefix.append("Event Scheduler: ["); + prefix.append(STRING_WITH_LEN("Event Scheduler: [")); prefix.append(et->definer.str, et->definer.length, system_charset_info); prefix.append("][", 2); @@ -297,7 +297,6 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) DBUG_ENTER("Event_worker_thread::run"); DBUG_PRINT("info", ("Time is %u, THD: %p", (uint)my_time(0), thd)); - inc_thread_running(); if (res) goto end; @@ -326,7 +325,6 @@ end: event->name.str)); delete event; - dec_thread_running(); deinit_event_thread(thd); DBUG_VOID_RETURN; diff --git a/sql/events.cc b/sql/events.cc index 0830fa7c611..3ad546217a7 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_parse.h" // check_access @@ -644,7 +644,7 @@ Events::drop_schema_events(THD *thd, const char *db) DBUG_ENTER("Events::drop_schema_events"); DBUG_PRINT("enter", ("dropping events from %s", db)); - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); /* Sic: no check if the scheduler is disabled or system tables @@ -1175,7 +1175,7 @@ Events::load_events_from_db(THD *thd) DBUG_RETURN(TRUE); } - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { Event_queue_element *et; bool created, dropped; diff --git a/sql/field.cc b/sql/field.cc index e723b62163a..39815adb4b0 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -27,7 +27,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "rpl_rli.h" // Pull in Relay_log_info @@ -51,7 +51,7 @@ *****************************************************************************/ static const char *zero_timestamp="0000-00-00 00:00:00.000000"; -LEX_CSTRING temp_lex_str= {"temp", 4}; +LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")}; uchar Field_null::null[1]={1}; const char field_separator=','; @@ -1518,7 +1518,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, goto out_of_range; } } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) return 1; return 0; @@ -1539,7 +1539,7 @@ double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs, set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); *error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_edom_and_truncation("double", str == end, cs, str, length, end)) *error= 1; @@ -1603,7 +1603,8 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val) Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const LEX_CSTRING *field_name_arg) - :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), + :ptr(ptr_arg), field_visibility(NOT_INVISIBLE), + null_ptr(null_ptr_arg), table(0), orig_table(0), table_name(0), field_name(*field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), part_of_key_not_clustered(0), part_of_sortkey(0), @@ -1873,7 +1874,7 @@ void Field::make_field(Send_field *field) if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) { field->db_name= orig_table->s->db.str; - if (orig_table->pos_in_table_list && + if (orig_table->pos_in_table_list && orig_table->pos_in_table_list->schema_table) field->org_table_name= (orig_table->pos_in_table_list-> schema_table->table_name); @@ -2195,6 +2196,7 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); tmp->reset_fields(); + tmp->field_visibility= NOT_INVISIBLE; return tmp; } @@ -2490,7 +2492,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) it makes the code easer to read. */ - if (get_thd()->count_cuted_fields) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; @@ -2660,7 +2662,8 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { if (pos == right_wall) { - if (get_thd()->count_cuted_fields && !is_cuted_fields_incr) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return 0; } @@ -3053,7 +3056,7 @@ int Field_new_decimal::store(const char *from, uint length, DBUG_RETURN(1); } - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (check_edom_and_important_data_truncation("decimal", err && err != E_DEC_TRUNCATED, @@ -3098,7 +3101,7 @@ int Field_new_decimal::store(const char *from, uint length, - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2) Also, we send a note if a string had some trailing spaces: '1.12 ' */ - if (thd->count_cuted_fields && + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && (err == E_DEC_TRUNCATED || err2 == E_DEC_TRUNCATED || end < from + length)) @@ -3261,7 +3264,7 @@ void Field_new_decimal::sql_type(String &str) const @returns number of bytes written to metadata_ptr */ -int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr) +int Field_new_decimal::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= precision; *(metadata_ptr + 1)= decimals(); @@ -4175,7 +4178,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) error= 1; else @@ -4470,7 +4473,7 @@ bool Field_float::send_binary(Protocol *protocol) @returns number of bytes written to metadata_ptr */ -int Field_float::do_save_field_metadata(uchar *metadata_ptr) +int Field_float::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; @@ -4787,7 +4790,7 @@ void Field_double::sort_string(uchar *to,uint length __attribute__((unused))) @returns number of bytes written to metadata_ptr */ -int Field_double::do_save_field_metadata(uchar *metadata_ptr) +int Field_double::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; @@ -5246,36 +5249,6 @@ static longlong read_lowendian(const uchar *from, uint bytes) } } -static void store_bigendian(ulonglong num, uchar *to, uint bytes) -{ - switch(bytes) { - case 1: mi_int1store(to, num); break; - case 2: mi_int2store(to, num); break; - case 3: mi_int3store(to, num); break; - case 4: mi_int4store(to, num); break; - case 5: mi_int5store(to, num); break; - case 6: mi_int6store(to, num); break; - case 7: mi_int7store(to, num); break; - case 8: mi_int8store(to, num); break; - default: DBUG_ASSERT(0); - } -} - -static longlong read_bigendian(const uchar *from, uint bytes) -{ - switch(bytes) { - case 1: return mi_uint1korr(from); - case 2: return mi_uint2korr(from); - case 3: return mi_uint3korr(from); - case 4: return mi_uint4korr(from); - case 5: return mi_uint5korr(from); - case 6: return mi_uint6korr(from); - case 7: return mi_uint7korr(from); - case 8: return mi_sint8korr(from); - default: DBUG_ASSERT(0); return 0; - } -} - void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) { mi_int4store(ptr, timestamp); @@ -6105,7 +6078,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && (error= check_int(cs, from, len, end, error))) { if (error == 1) /* empty or incorrect string */ @@ -6813,7 +6786,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, bool count_spaces) { THD *thd= get_thd(); - if ((pstr < end) && thd->count_cuted_fields) + if ((pstr < end) && thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (test_if_important_data(field_charset, pstr, end)) { @@ -6859,6 +6832,20 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) } +int Field_str::store(longlong nr, bool unsigned_val) +{ + char buff[64]; + uint length; + length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, + buff, + sizeof(buff), + (unsigned_val ? 10: + -10), + nr); + return store(buff, length, field_charset); +} + + /** Store double value in Field_string or Field_varstring. @@ -6871,7 +6858,8 @@ int Field_str::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; - uint local_char_length= field_length / charset()->mbmaxlen; + uint local_char_length= MY_MIN(sizeof(buff), + field_length / field_charset->mbmaxlen); size_t length= 0; my_bool error= (local_char_length == 0); @@ -6903,17 +6891,6 @@ uint Field_str::is_equal(Create_field *new_field) } -int Field_string::store(longlong nr, bool unsigned_val) -{ - char buff[64]; - int l; - CHARSET_INFO *cs=charset(); - l= (cs->cset->longlong10_to_str)(cs,buff,sizeof(buff), - unsigned_val ? 10 : -10, nr); - return Field_string::store(buff,(uint)l,cs); -} - - int Field_longstr::store_decimal(const my_decimal *d) { char buff[DECIMAL_MAX_STR_LENGTH+1]; @@ -7193,7 +7170,7 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) the master. @note For information about how the length is packed, see @c - Field_string::do_save_field_metadata + Field_string::save_field_metadata @param to Destination of the data @param from Source of the data @@ -7276,7 +7253,7 @@ Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end, @returns number of bytes written to metadata_ptr */ -int Field_string::do_save_field_metadata(uchar *metadata_ptr) +int Field_string::save_field_metadata(uchar *metadata_ptr) { DBUG_ASSERT(field_length < 1024); DBUG_ASSERT((real_type() & 0xF0) == 0xF0); @@ -7373,7 +7350,7 @@ const uint Field_varstring::MAX_SIZE= UINT_MAX16; @returns number of bytes written to metadata_ptr */ -int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) +int Field_varstring::save_field_metadata(uchar *metadata_ptr) { DBUG_ASSERT(field_length <= 65535); int2store((char*)metadata_ptr, field_length); @@ -7400,20 +7377,6 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) } -int Field_varstring::store(longlong nr, bool unsigned_val) -{ - char buff[64]; - uint length; - length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, - buff, - sizeof(buff), - (unsigned_val ? 10: - -10), - nr); - return Field_varstring::store(buff, length, field_charset); -} - - double Field_varstring::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -7530,26 +7493,29 @@ int Field_varstring::key_cmp(const uchar *a,const uchar *b) void Field_varstring::sort_string(uchar *to,uint length) { - uint tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + String buf; + + val_str(&buf, &buf); if (field_charset == &my_charset_bin) { /* Store length last in high-byte order to sort longer strings first */ if (length_bytes == 1) - to[length-1]= tot_length; + to[length - 1]= buf.length(); else - mi_int2store(to+length-2, tot_length); + mi_int2store(to + length - 2, buf.length()); length-= length_bytes; } - - tot_length= field_charset->coll->strnxfrm(field_charset, - to, length, - char_length() * - field_charset->strxfrm_multiply, - ptr + length_bytes, tot_length, - MY_STRXFRM_PAD_WITH_SPACE | - MY_STRXFRM_PAD_TO_MAXLEN); - DBUG_ASSERT(tot_length == length); + +#ifndef DBUG_OFF + uint rc= +#endif + field_charset->coll->strnxfrm(field_charset, to, length, + char_length() * field_charset->strxfrm_multiply, + (const uchar*) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); } @@ -7565,6 +7531,11 @@ enum ha_base_keytype Field_varstring::key_type() const } +/* + Compressed columns need one extra byte to store the compression method. + This byte is invisible to the end user, but not for the storage engine. +*/ + void Field_varstring::sql_type(String &res) const { THD *thd= table->in_use; @@ -7574,7 +7545,8 @@ void Field_varstring::sql_type(String &res) const length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(), "%s(%d)", (has_charset() ? "varchar" : "varbinary"), - (int) field_length / charset()->mbmaxlen); + (int) field_length / charset()->mbmaxlen - + MY_TEST(compression_method())); res.length(length); if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_charset() && (charset()->state & MY_CS_BINSORT)) @@ -7676,32 +7648,36 @@ uint Field_varstring::max_packed_col_length(uint max_length) uint Field_varstring::get_key_image(uchar *buff, uint length, imagetype type_arg) { - uint f_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - uint local_char_length= length / field_charset->mbmaxlen; - uchar *pos= ptr+length_bytes; - local_char_length= my_charpos(field_charset, pos, pos + f_length, - local_char_length); - set_if_smaller(f_length, local_char_length); + String val; + uint local_char_length; + my_bitmap_map *old_map; + + old_map= dbug_tmp_use_all_columns(table, table->read_set); + val_str(&val, &val); + dbug_tmp_restore_column_map(table->read_set, old_map); + + local_char_length= val.charpos(length / field_charset->mbmaxlen); + if (local_char_length < val.length()) + val.length(local_char_length); /* Key is always stored with 2 bytes */ - int2store(buff,f_length); - memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length); - if (f_length < length) + int2store(buff, val.length()); + memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length()); + if (val.length() < length) { /* Must clear this as we do a memcmp in opt_range.cc to detect identical keys */ - bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length)); + memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length()); } - return HA_KEY_BLOB_LENGTH+f_length; + return HA_KEY_BLOB_LENGTH + val.length(); } void Field_varstring::set_key_image(const uchar *buff,uint length) { length= uint2korr(buff); // Real length is here - (void) Field_varstring::store((const char*) buff+HA_KEY_BLOB_LENGTH, length, - field_charset); + (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset); } @@ -7758,13 +7734,14 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, uint Field_varstring::is_equal(Create_field *new_field) { if (new_field->type_handler() == type_handler() && - new_field->charset == field_charset) + new_field->charset == field_charset && + !new_field->compression_method() == !compression_method()) { - if (new_field->length == max_display_length()) + if (new_field->length == field_length) return IS_EQUAL_YES; - if (new_field->length > max_display_length() && - ((new_field->length <= 255 && max_display_length() <= 255) || - (new_field->length > 255 && max_display_length() > 255))) + if (new_field->length > field_length && + ((new_field->length <= 255 && field_length <= 255) || + (new_field->length > 255 && field_length > 255))) return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length } return IS_EQUAL_NO; @@ -7786,6 +7763,201 @@ void Field_varstring::hash(ulong *nr, ulong *nr2) } +/** + Compress field + + @param[out] to destination buffer for compressed data + @param[in,out] to_length in: size of to, out: compressed data length + @param[in] from data to compress + @param[in] length from length + @param[in] cs from character set + + In worst case (no compression performed) storage requirement is increased by + 1 byte to store header. If it exceeds field length, normal data truncation is + performed. + + Generic compressed header format (1 byte): + + Bits 1-4: method specific bits + Bits 5-8: compression method + + If compression method is 0 then header is immediately followed by + uncompressed data. + + If compression method is zlib: + + Bits 1-3: number of bytes occupied by original data length + Bits 4: true if zlib wrapper not present + Bits 5-8: store 8 (zlib) + + Header is immediately followed by original data length, + followed by compressed data. +*/ + +int Field_longstr::compress(char *to, uint *to_length, + const char *from, uint length, + CHARSET_INFO *cs) +{ + THD *thd= get_thd(); + char *buf= 0; + int rc= 0; + + if (length == 0) + { + *to_length= 0; + return 0; + } + + if (String::needs_conversion_on_storage(length, cs, field_charset) || + *to_length <= length) + { + String_copier copier; + const char *end= from + length; + + if (!(buf= (char*) my_malloc(*to_length - 1, MYF(MY_WME)))) + { + *to_length= 0; + return -1; + } + + length= copier.well_formed_copy(field_charset, buf, *to_length - 1, + cs, from, length, + (*to_length - 1) / field_charset->mbmaxlen); + rc= check_conversion_status(&copier, end, cs, true); + from= buf; + DBUG_ASSERT(length > 0); + } + + if (length >= thd->variables.column_compression_threshold && + (*to_length= compression_method()->compress(thd, to, from, length))) + status_var_increment(thd->status_var.column_compressions); + else + { + /* Store uncompressed */ + to[0]= 0; + memcpy(to + 1, from, length); + *to_length= length + 1; + } + + if (buf) + my_free(buf); + return rc; +} + + +/* + Memory is allocated only when original data was actually compressed. + Otherwise val_ptr points at data located immediately after header. + + Data can be stored uncompressed if data was shorter than threshold + or compressed data was longer than original data. +*/ + +String *Field_longstr::uncompress(String *val_buffer, String *val_ptr, + const uchar *from, uint from_length) +{ + if (from_length) + { + uchar method= (*from & 0xF0) >> 4; + + /* Uncompressed data */ + if (!method) + { + val_ptr->set((const char*) from + 1, from_length - 1, field_charset); + return val_ptr; + } + + if (compression_methods[method].uncompress) + { + if (!compression_methods[method].uncompress(val_buffer, from, from_length, + field_length)) + { + val_buffer->set_charset(field_charset); + status_var_increment(get_thd()->status_var.column_decompressions); + return val_buffer; + } + } + } + + /* + It would be better to return 0 in case of errors, but to take the + safer route, let's return a zero string and let the general + handler catch the error. + */ + val_ptr->set("", 0, field_charset); + return val_ptr; +} + + +int Field_varstring_compressed::store(const char *from, uint length, + CHARSET_INFO *cs) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + uint to_length= MY_MIN(field_length, field_charset->mbmaxlen * length + 1); + int rc= compress((char*) get_data(), &to_length, from, length, cs); + store_length(to_length); + return rc; +} + + +String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + return uncompress(val_buffer, val_ptr, get_data(), get_length()); +} + + +double Field_varstring_compressed::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +longlong Field_varstring_compressed::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +int Field_varstring_compressed::cmp_max(const uchar *a_ptr, const uchar *b_ptr, + uint max_len) +{ + String a, b; + uint a_length, b_length; + + if (length_bytes == 1) + { + a_length= (uint) *a_ptr; + b_length= (uint) *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + + uncompress(&a, &a, a_ptr + length_bytes, a_length); + uncompress(&b, &b, b_ptr + length_bytes, b_length); + + if (a.length() > max_len) + a.length(max_len); + if (b.length() > max_len) + b.length(max_len); + + return sortcmp(&a, &b, field_charset); +} + + /**************************************************************************** ** blob type ** A blob is saved as a length and a pointer. The length is stored in the @@ -7828,6 +8000,7 @@ uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const int Field_blob::copy_value(Field_blob *from) { DBUG_ASSERT(field_charset == from->charset()); + DBUG_ASSERT(!compression_method() == !from->compression_method()); int rc= 0; uint32 length= from->get_length(); uchar *data= from->get_ptr(); @@ -7935,22 +8108,6 @@ oom_error: } -int Field_blob::store(double nr) -{ - CHARSET_INFO *cs=charset(); - value.set_real(nr, NOT_FIXED_DEC, cs); - return Field_blob::store(value.ptr(),(uint) value.length(), cs); -} - - -int Field_blob::store(longlong nr, bool unsigned_val) -{ - CHARSET_INFO *cs=charset(); - value.set_int(nr, unsigned_val, cs); - return Field_blob::store(value.ptr(), (uint) value.length(), cs); -} - - double Field_blob::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -8167,9 +8324,9 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table, @returns number of bytes written to metadata_ptr */ -int Field_blob::do_save_field_metadata(uchar *metadata_ptr) +int Field_blob::save_field_metadata(uchar *metadata_ptr) { - DBUG_ENTER("Field_blob::do_save_field_metadata"); + DBUG_ENTER("Field_blob::save_field_metadata"); *metadata_ptr= pack_length_no_ptr(); DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr)); DBUG_RETURN(1); @@ -8185,33 +8342,30 @@ uint32 Field_blob::sort_length() const void Field_blob::sort_string(uchar *to,uint length) { - uchar *blob; - uint blob_length=get_length(); + String buf; - if (!blob_length && field_charset->pad_char == 0) + val_str(&buf, &buf); + if (!buf.length() && field_charset->pad_char == 0) bzero(to,length); else { if (field_charset == &my_charset_bin) { - uchar *pos; - /* Store length of blob last in blob to shorter blobs before longer blobs */ length-= packlength; - pos= to+length; - - store_bigendian(blob_length, pos, packlength); + store_bigendian(buf.length(), to + length, packlength); } - memcpy(&blob, ptr+packlength, sizeof(char*)); - - blob_length= field_charset->coll->strnxfrm(field_charset, - to, length, length, - blob, blob_length, - MY_STRXFRM_PAD_WITH_SPACE | - MY_STRXFRM_PAD_TO_MAXLEN); - DBUG_ASSERT(blob_length == length); + +#ifndef DBUG_OFF + uint rc= +#endif + field_charset->coll->strnxfrm(field_charset, to, length, length, + (const uchar*) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); } } @@ -8306,11 +8460,9 @@ const uchar *Field_blob::unpack(uchar *to, const uchar *from, DBUG_RETURN(0); // Error in data uint32 const length= get_length(from, master_packlength); DBUG_DUMP("packed", from, length + master_packlength); - bitmap_set_bit(table->write_set, field_index); if (from + master_packlength + length > from_end) DBUG_RETURN(0); - store(reinterpret_cast<const char*>(from) + master_packlength, - length, field_charset); + set_ptr(length, const_cast<uchar*> (from) + master_packlength); DBUG_DUMP("record", to, table->s->reclength); DBUG_RETURN(from + master_packlength + length); } @@ -8330,11 +8482,68 @@ uint Field_blob::max_packed_col_length(uint max_length) } +/* + Blob fields are regarded equal if they have same character set, + same blob store length and if either both are compressed or both are + uncompressed. + The logic for compression is that we don't have to uncompress and compress + again an already compressed field just because compression method changes. +*/ + uint Field_blob::is_equal(Create_field *new_field) { return new_field->type_handler() == type_handler() && new_field->charset == field_charset && - new_field->pack_length == pack_length(); + new_field->pack_length == pack_length() && + !new_field->compression_method() == !compression_method(); +} + + +int Field_blob_compressed::store(const char *from, uint length, + CHARSET_INFO *cs) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + uint to_length= MY_MIN(max_data_length(), field_charset->mbmaxlen * length + 1); + int rc; + + if (value.alloc(to_length)) + { + set_ptr((uint32) 0, NULL); + return -1; + } + + rc= compress((char*) value.ptr(), &to_length, from, length, cs); + set_ptr(to_length, (uchar*) value.ptr()); + return rc; +} + + +String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + return uncompress(val_buffer, val_ptr, get_ptr(), get_length()); +} + + +double Field_blob_compressed::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +longlong Field_blob_compressed::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); } @@ -8511,7 +8720,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) wkb_type > (uint32) Geometry::wkb_last) goto err; - if (geom_type != Field::GEOM_GEOMETRY && + if (geom_type != Field::GEOM_GEOMETRY && geom_type != Field::GEOM_GEOMETRYCOLLECTION && (uint32) geom_type != wkb_type) { @@ -8651,7 +8860,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) set_warning(WARN_DATA_TRUNCATED, 1); err= 1; } - if (!get_thd()->count_cuted_fields && !length) + if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length) err= 0; } else @@ -8678,7 +8887,7 @@ int Field_enum::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(WARN_DATA_TRUNCATED, 1); - if (nr != 0 || get_thd()->count_cuted_fields) + if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { nr= 0; error= 1; @@ -8713,7 +8922,7 @@ longlong Field_enum::val_int(void) @returns number of bytes written to metadata_ptr */ -int Field_enum::do_save_field_metadata(uchar *metadata_ptr) +int Field_enum::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= real_type(); *(metadata_ptr + 1)= pack_length(); @@ -9432,9 +9641,9 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg) @returns number of bytes written to metadata_ptr */ -int Field_bit::do_save_field_metadata(uchar *metadata_ptr) +int Field_bit::save_field_metadata(uchar *metadata_ptr) { - DBUG_ENTER("Field_bit::do_save_field_metadata"); + DBUG_ENTER("Field_bit::save_field_metadata"); DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d", bit_len, bytes_in_rec)); /* @@ -9904,6 +10113,8 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, uint filter= VCOL_IMPOSSIBLE; if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT) filter|= VCOL_NOT_STRICTLY_DETERMINISTIC; + if (type == VCOL_GENERATED_VIRTUAL) + filter|= VCOL_NOT_VIRTUAL; if (ret || (res.errors & filter)) { @@ -10216,6 +10427,16 @@ Field *make_field(TABLE_SHARE *share, unireg_check, field_name, field_charset); if (field_type == MYSQL_TYPE_VARCHAR) + { + if (unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_varstring_compressed( + ptr, field_length, + HA_VARCHAR_PACKLENGTH(field_length), + null_pos, null_bit, + unireg_check, field_name, + share, field_charset, zlib_compression_method); + return new (mem_root) Field_varstring(ptr,field_length, HA_VARCHAR_PACKLENGTH(field_length), @@ -10223,6 +10444,7 @@ Field *make_field(TABLE_SHARE *share, unireg_check, field_name, share, field_charset); + } return 0; // Error } @@ -10244,10 +10466,18 @@ Field *make_field(TABLE_SHARE *share, } #endif if (f_is_blob(pack_flag)) + { + if (unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_blob_compressed(ptr, null_pos, null_bit, + unireg_check, field_name, share, + pack_length, field_charset, zlib_compression_method); + return new (mem_root) Field_blob(ptr,null_pos,null_bit, unireg_check, field_name, share, pack_length, field_charset); + } if (interval) { if (f_is_enum(pack_flag)) @@ -10426,10 +10656,26 @@ Column_definition::Column_definition(THD *thd, Field *old_field, comment= old_field->comment; decimals= old_field->decimals(); vcol_info= old_field->vcol_info; - default_value= orig_field ? orig_field->default_value : 0; - check_constraint= orig_field ? orig_field->check_constraint : 0; option_list= old_field->option_list; pack_flag= 0; + compression_method_ptr= 0; + field_visibility= old_field->field_visibility; + + if (orig_field) + { + default_value= orig_field->default_value; + check_constraint= orig_field->check_constraint; + if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + } + } + else + { + default_value= 0; + check_constraint= 0; + } switch (real_field_type()) { case MYSQL_TYPE_TINY_BLOB: @@ -10450,7 +10696,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field, case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: /* This is corrected in create_length_to_internal_length */ - length= (length+charset->mbmaxlen-1) / charset->mbmaxlen; + length= (length+charset->mbmaxlen-1) / charset->mbmaxlen - + MY_TEST(old_field->compression_method()); break; #ifdef HAVE_SPATIAL case MYSQL_TYPE_GEOMETRY: @@ -10550,6 +10797,7 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field, flags= dup_field->flags; interval= dup_field->interval; vcol_info= dup_field->vcol_info; + field_visibility= dup_field->field_visibility; } @@ -10614,6 +10862,29 @@ bool Column_definition::has_default_expression() (flags & BLOB_FLAG))); } + +bool Column_definition::set_compressed(const char *method) +{ + enum enum_field_types sql_type= real_field_type(); + /* We can't use f_is_blob here as pack_flag is not yet set */ + if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB || + sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_type == MYSQL_TYPE_LONG_BLOB) + { + if (!method || !strcmp(method, zlib_compression_method->name)) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + return false; + } + my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method); + } + else + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); + return true; +} + + /** maximum possible display length for blob. @@ -10652,11 +10923,12 @@ uint32 Field_blob::max_display_length() @param cut_increment - whenever we should increase cut fields count @note - This function won't produce warning and increase cut fields counter - if count_cuted_fields == CHECK_FIELD_IGNORE for current thread. + This function won't produce warning or notes or increase cut fields counter + if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION + for the current thread. - if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. - This allows us to avoid notes in optimisation, like convert_constant_item(). + This allows us to avoid notes in optimisation, like + convert_constant_item(). @retval 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE @@ -10673,7 +10945,7 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, will have table == NULL. */ THD *thd= get_thd(); - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { thd->cuted_fields+= cut_increment; push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str, diff --git a/sql/field.h b/sql/field.h index 0d7d8e42615..0bff381360b 100644 --- a/sql/field.h +++ b/sql/field.h @@ -32,6 +32,7 @@ #include "sql_error.h" /* Sql_condition */ #include "compat56.h" #include "sql_type.h" /* Type_std_attributes */ +#include "field_comp.h" class Send_field; class Copy_field; @@ -49,8 +50,9 @@ class Virtual_tmp_table; enum enum_check_fields { CHECK_FIELD_IGNORE, + CHECK_FIELD_EXPRESSION, CHECK_FIELD_WARN, - CHECK_FIELD_ERROR_FOR_NULL + CHECK_FIELD_ERROR_FOR_NULL, }; /* @@ -557,6 +559,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type) #define VCOL_TIME_FUNC 8 #define VCOL_AUTO_INC 16 #define VCOL_IMPOSSIBLE 32 +#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */ #define VCOL_NOT_STRICTLY_DETERMINISTIC \ (VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC) @@ -673,6 +676,8 @@ public: { DBUG_ASSERT(0); } uchar *ptr; // Position to field in record + + field_visible_type field_visibility; /** Byte where the @c NULL bit is stored inside a record. If this Field is a @c NOT @c NULL field, this member is @c NULL. @@ -713,7 +718,8 @@ public: TIMESTAMP_OLD_FIELD=18, // TIMESTAMP created before 4.1.3 TIMESTAMP_DN_FIELD=21, // TIMESTAMP DEFAULT NOW() TIMESTAMP_UN_FIELD=22, // TIMESTAMP ON UPDATE NOW() - TIMESTAMP_DNUN_FIELD=23 // TIMESTAMP DEFAULT NOW() ON UPDATE NOW() + TIMESTAMP_DNUN_FIELD=23, // TIMESTAMP DEFAULT NOW() ON UPDATE NOW() + TMYSQL_COMPRESSED= 24, // Compatibility with TMySQL }; enum geometry_type { @@ -917,8 +923,21 @@ public: DBUG_RETURN(field_metadata); } virtual uint row_pack_length() const { return 0; } + + + /** + Retrieve the field metadata for fields. + + This default implementation returns 0 and saves 0 in the first_byte value. + + @param first_byte First byte of field metadata + + @returns 0 no bytes written. + */ + virtual int save_field_metadata(uchar *first_byte) - { return do_save_field_metadata(first_byte); } + { return 0; } + /* data_length() return the "real size" of the data in memory. @@ -1517,6 +1536,8 @@ public: /* Mark field in read map. Updates also virtual fields */ void register_field_in_read_map(); + virtual Compression_method *compression_method() const { return 0; } + virtual Virtual_tmp_table **virtual_tmp_table_addr() { return NULL; @@ -1550,19 +1571,6 @@ private: */ virtual size_t do_last_null_byte() const; -/** - Retrieve the field metadata for fields. - - This default implementation returns 0 and saves 0 in the metadata_ptr - value. - - @param metadata_ptr First byte of field metadata - - @returns 0 no bytes written. -*/ - virtual int do_save_field_metadata(uchar *metadata_ptr) - { return 0; } - protected: uchar *pack_int(uchar *to, const uchar *from, size_t size) { @@ -1703,7 +1711,7 @@ public: charset() == from->charset(); } int store(double nr); - int store(longlong nr, bool unsigned_val)=0; + int store(longlong nr, bool unsigned_val); int store_decimal(const my_decimal *); int store(const char *to,uint length,CHARSET_INFO *cs)=0; int store_hex_hybrid(const char *str, uint length) @@ -1751,6 +1759,11 @@ protected: const Item *item) const; bool cmp_to_string_with_stricter_collation(const Item_bool_func *cond, const Item *item) const; + int compress(char *to, uint *to_length, + const char *from, uint length, + CHARSET_INFO *cs); + String *uncompress(String *val_buffer, String *val_ptr, + const uchar *from, uint from_length); public: Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -1861,7 +1874,7 @@ public: /* New decimal/numeric field which use fixed point arithmetic */ class Field_new_decimal :public Field_num { private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); public: /* The maximum number of decimal digits can be stored */ uint precision; @@ -2183,7 +2196,7 @@ public: uint row_pack_length() const { return pack_length(); } void sql_type(String &str) const; private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -2236,7 +2249,7 @@ public: uint row_pack_length() const { return pack_length(); } void sql_type(String &str) const; private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -2524,7 +2537,7 @@ public: TIMESTAMP(0..6) - MySQL56 version */ class Field_timestampf :public Field_timestamp_with_dec { - int do_save_field_metadata(uchar *metadata_ptr) + int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); return 1; @@ -2792,7 +2805,7 @@ public: */ class Field_timef :public Field_time_with_dec { void store_TIME(MYSQL_TIME *ltime); - int do_save_field_metadata(uchar *metadata_ptr) + int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); return 1; @@ -2954,7 +2967,7 @@ public: class Field_datetimef :public Field_datetime_with_dec { void store_TIME(MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; - int do_save_field_metadata(uchar *metadata_ptr) + int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); return 1; @@ -3085,8 +3098,7 @@ public: return 0; } int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); - int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + using Field_str::store; double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3119,7 +3131,7 @@ public: Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); virtual uint get_key_image(uchar *buff,uint length, imagetype type); private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -3133,6 +3145,15 @@ public: { return length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); } +protected: + void store_length(uint32 number) + { + if (length_bytes == 1) + *ptr= (uchar) number; + else + int2store(ptr, number); + } +public: /* The maximum space available in a Field_varstring, in bytes. See length_bytes. @@ -3177,11 +3198,11 @@ public: bool memcpy_field_possible(const Field *from) const { return Field_str::memcpy_field_possible(from) && + !compression_method() == !from->compression_method() && length_bytes == ((Field_varstring*) from)->length_bytes; } int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); - int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + using Field_str::store; double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3215,10 +3236,91 @@ public: void hash(ulong *nr, ulong *nr2); uint length_size() { return length_bytes; } private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); +}; + + +class Field_varstring_compressed: public Field_varstring { +public: + Field_varstring_compressed(uchar *ptr_arg, + uint32 len_arg, uint length_bytes_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, const DTCollation &collation, + Compression_method *compression_method_arg): + Field_varstring(ptr_arg, len_arg, length_bytes_arg, null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, + share, collation), + compression_method_ptr(compression_method_arg) { DBUG_ASSERT(len_arg > 0); } + Compression_method *compression_method() const + { return compression_method_ptr; } +private: + Compression_method *compression_method_ptr; + int store(const char *to, uint length, CHARSET_INFO *charset); + using Field_str::store; + String *val_str(String *, String *); + double val_real(void); + longlong val_int(void); + uint size_of() const { return sizeof(*this); } + enum_field_types binlog_type() const { return MYSQL_TYPE_VARCHAR_COMPRESSED; } + void sql_type(String &str) const + { + Field_varstring::sql_type(str); + str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/")); + } + uint32 max_display_length() { return field_length - 1; } + int cmp_max(const uchar *a_ptr, const uchar *b_ptr, uint max_len); + + /* + Compressed fields can't have keys as two rows may have different + compression methods or compression levels. + */ + + int key_cmp(const uchar *str, uint length) + { DBUG_ASSERT(0); return 0; } + using Field_varstring::key_cmp; }; +static inline uint8 number_storage_requirement(uint32 n) +{ + return n < 256 ? 1 : n < 65536 ? 2 : n < 16777216 ? 3 : 4; +} + + +static inline void store_bigendian(ulonglong num, uchar *to, uint bytes) +{ + switch(bytes) { + case 1: mi_int1store(to, num); break; + case 2: mi_int2store(to, num); break; + case 3: mi_int3store(to, num); break; + case 4: mi_int4store(to, num); break; + case 5: mi_int5store(to, num); break; + case 6: mi_int6store(to, num); break; + case 7: mi_int7store(to, num); break; + case 8: mi_int8store(to, num); break; + default: DBUG_ASSERT(0); + } +} + + +static inline longlong read_bigendian(const uchar *from, uint bytes) +{ + switch(bytes) { + case 1: return mi_uint1korr(from); + case 2: return mi_uint2korr(from); + case 3: return mi_uint3korr(from); + case 4: return mi_uint4korr(from); + case 5: return mi_uint5korr(from); + case 6: return mi_uint6korr(from); + case 7: return mi_uint7korr(from); + case 8: return mi_sint8korr(from); + default: DBUG_ASSERT(0); return 0; + } +} + + extern LEX_CSTRING temp_lex_str; class Field_blob :public Field_longstr { @@ -3261,13 +3363,7 @@ public: NONE, field_name_arg, collation) { flags|= BLOB_FLAG; - packlength= 4; - if (set_packlength) - { - packlength= len_arg <= 255 ? 1 : - len_arg <= 65535 ? 2 : - len_arg <= 16777215 ? 3 : 4; - } + packlength= set_packlength ? number_storage_requirement(len_arg) : 4; } Field_blob(uint32 packlength_arg) :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, &temp_lex_str, @@ -3300,7 +3396,8 @@ public: if (from->type() == MYSQL_TYPE_BIT) return do_field_int; */ - if (!(from->flags & BLOB_FLAG) || from->charset() != charset()) + if (!(from->flags & BLOB_FLAG) || from->charset() != charset() || + !from->compression_method() != !compression_method()) return do_conv_blob; if (from->pack_length() != Field_blob::pack_length()) return do_copy_blob; @@ -3317,11 +3414,11 @@ public: bool memcpy_field_possible(const Field *from) const { return Field_str::memcpy_field_possible(from) && + !compression_method() == !from->compression_method() && !table->copy_blobs; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); + int store(const char *to, uint length, CHARSET_INFO *charset); + using Field_str::store; double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3455,7 +3552,54 @@ public: uint32 char_length() const; uint is_equal(Create_field *new_field); private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); +}; + + +class Field_blob_compressed: public Field_blob { +public: + Field_blob_compressed(uchar *ptr_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, + uint blob_pack_length, const DTCollation &collation, + Compression_method *compression_method_arg): + Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, share, blob_pack_length, collation), + compression_method_ptr(compression_method_arg) {} + Compression_method *compression_method() const + { return compression_method_ptr; } +private: + Compression_method *compression_method_ptr; + int store(const char *to, uint length, CHARSET_INFO *charset); + using Field_str::store; + String *val_str(String *, String *); + double val_real(void); + longlong val_int(void); + uint size_of() const { return sizeof(*this); } + enum_field_types binlog_type() const { return MYSQL_TYPE_BLOB_COMPRESSED; } + void sql_type(String &str) const + { + Field_blob::sql_type(str); + str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/")); + } + + /* + Compressed fields can't have keys as two rows may have different + compression methods or compression levels. + */ + + uint get_key_image(uchar *buff, uint length, imagetype type_arg) + { DBUG_ASSERT(0); return 0; } + void set_key_image(const uchar *buff, uint length) + { DBUG_ASSERT(0); } + int key_cmp(const uchar *a, const uchar *b) + { DBUG_ASSERT(0); return 0; } + int key_cmp(const uchar *str, uint length) + { DBUG_ASSERT(0); return 0; } + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) + { DBUG_ASSERT(0); return 0; } }; @@ -3620,7 +3764,7 @@ public: const Item *item, bool is_eq_func) const; private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); uint is_equal(Create_field *new_field); }; @@ -3798,7 +3942,7 @@ public: private: virtual size_t do_last_null_byte() const; - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -3896,6 +4040,7 @@ class Column_definition: public Sql_alloc, bool prepare_stage1_check_typelib_default(); bool prepare_stage1_convert_default(THD *, MEM_ROOT *, CHARSET_INFO *to); const Type_handler *field_type() const; // Prevent using this + Compression_method *compression_method_ptr; public: LEX_CSTRING field_name; LEX_CSTRING comment; // Comment for field @@ -3905,6 +4050,7 @@ public: max number of characters. */ ulonglong length; + field_visible_type field_visibility; /* The value of `length' as set by parser: is the number of characters for most of the types, or of bytes for BLOBs or numeric types. @@ -3933,8 +4079,9 @@ public: Column_definition() :Type_handler_hybrid_field_type(&type_handler_null), + compression_method_ptr(0), comment(null_clex_str), - on_update(NULL), length(0), decimals(0), + on_update(NULL), length(0),field_visibility(NOT_INVISIBLE), decimals(0), flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), interval(0), charset(&my_charset_bin), srid(0), geom_type(Field::GEOM_GEOMETRY), @@ -3957,6 +4104,8 @@ public: void create_length_to_internal_length_string() { length*= charset->mbmaxlen; + if (real_field_type() == MYSQL_TYPE_VARCHAR && compression_method()) + length++; DBUG_ASSERT(length <= UINT_MAX32); key_length= (uint) length; pack_length= type_handler()->calc_pack_length((uint32) length); @@ -4106,6 +4255,11 @@ public: { *this= *def; } + bool set_compressed(const char *method); + void set_compression_method(Compression_method *compression_method_arg) + { compression_method_ptr= compression_method_arg; } + Compression_method *compression_method() const + { return compression_method_ptr; } }; @@ -4404,5 +4558,6 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, #define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT) #define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) #define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) +#define f_visibility(x) (static_cast<field_visible_type> ((x) & 3)) #endif /* FIELD_INCLUDED */ diff --git a/sql/field_comp.cc b/sql/field_comp.cc new file mode 100644 index 00000000000..9a7b3a7c7e0 --- /dev/null +++ b/sql/field_comp.cc @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 MariaDB Foundation + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include <my_global.h> +#include "sql_string.h" +#include "sql_class.h" +#include "field_comp.h" +#include <zlib.h> + + +static uint compress_zlib(THD *thd, char *to, const char *from, uint length) +{ + uint level= thd->variables.column_compression_zlib_level; + + if (level > 0) + { + z_stream stream; + int wbits= thd->variables.column_compression_zlib_wrap ? MAX_WBITS : + -MAX_WBITS; + uint strategy= thd->variables.column_compression_zlib_strategy; + /* Store only meaningful bytes of original data length. */ + uchar original_pack_length= number_storage_requirement(length); + + *to= 0x80 + original_pack_length + (wbits < 0 ? 8 : 0); + store_bigendian(length, (uchar*) to + 1, original_pack_length); + + stream.avail_in= length; + stream.next_in= (Bytef*) from; + + stream.avail_out= length - original_pack_length - 1; + stream.next_out= (Bytef*) to + original_pack_length + 1; + + stream.zalloc= 0; + stream.zfree= 0; + stream.opaque= 0; + + if (deflateInit2(&stream, level, Z_DEFLATED, wbits, 8, strategy) == Z_OK && + deflate(&stream, Z_FINISH) == Z_STREAM_END && + deflateEnd(&stream) == Z_OK) + return (uint) (stream.next_out - (Bytef*) to); + } + return 0; +} + + +static int uncompress_zlib(String *to, const uchar *from, uint from_length, + uint field_length) +{ + z_stream stream; + uchar original_pack_length; + int wbits; + ulonglong avail_out; + + original_pack_length= *from & 0x07; + wbits= *from & 8 ? -MAX_WBITS : MAX_WBITS; + + from++; + from_length--; + + if (from_length < original_pack_length) + { + my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0)); + return 1; + } + + avail_out= (ulonglong)read_bigendian(from, original_pack_length); + + if (avail_out > field_length) + { + my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0)); + return 1; + } + + stream.avail_out= (uint)avail_out; + if (to->alloc(stream.avail_out)) + return 1; + + stream.next_out= (Bytef*) to->ptr(); + + stream.avail_in= from_length - original_pack_length; + stream.next_in= (Bytef*) from + original_pack_length; + + stream.zalloc= 0; + stream.zfree= 0; + stream.opaque= 0; + + if (inflateInit2(&stream, wbits) == Z_OK && + inflate(&stream, Z_FINISH) == Z_STREAM_END && + inflateEnd(&stream) == Z_OK) + { + to->length(stream.total_out); + return 0; + } + my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0)); + return 1; +} + + +Compression_method compression_methods[MAX_COMPRESSION_METHODS]= +{ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { "zlib", compress_zlib, uncompress_zlib }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; diff --git a/sql/field_comp.h b/sql/field_comp.h new file mode 100644 index 00000000000..7eb8ab1e75e --- /dev/null +++ b/sql/field_comp.h @@ -0,0 +1,33 @@ +#ifndef FIELD_COMP_H_INCLUDED +#define FIELD_COMP_H_INCLUDED +/* Copyright (C) 2017 MariaDB Foundation + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#define MAX_COMPRESSION_METHODS 16 + +struct Compression_method +{ + const char *name; + uint (*compress)(THD *thd, char *to, const char *from, uint length); + int (*uncompress)(String *to, const uchar *from, uint from_length, + uint field_length); +}; + + +extern Compression_method compression_methods[MAX_COMPRESSION_METHODS]; +#define zlib_compression_method (&compression_methods[8]) + +#endif diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 932188d56a8..db5c9429954 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -25,7 +25,7 @@ gives much more speed. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // THD #include <m_ctype.h> @@ -122,6 +122,7 @@ static int set_bad_null_error(Field *field, int err) field->set_warning(Sql_condition::WARN_LEVEL_WARN, err, 1); /* fall through */ case CHECK_FIELD_IGNORE: + case CHECK_FIELD_EXPRESSION: return 0; case CHECK_FIELD_ERROR_FOR_NULL: if (!field->table->in_use->no_errors) @@ -528,7 +529,8 @@ static void do_varstring1(Copy_field *copy) if (length > copy->to_length- 1) { length=copy->to_length - 1; - if (copy->from_field->table->in_use->count_cuted_fields && + if (copy->from_field->table->in_use->count_cuted_fields > + CHECK_FIELD_EXPRESSION && copy->to_field) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -547,7 +549,7 @@ static void do_varstring1_mb(Copy_field *copy) Well_formed_prefix prefix(cs, (char*) from_ptr, from_length, to_char_length); if (prefix.length() < from_length) { - if (current_thd->count_cuted_fields) + if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -562,7 +564,8 @@ static void do_varstring2(Copy_field *copy) if (length > copy->to_length- HA_KEY_BLOB_LENGTH) { length=copy->to_length-HA_KEY_BLOB_LENGTH; - if (copy->from_field->table->in_use->count_cuted_fields && + if (copy->from_field->table->in_use->count_cuted_fields > + CHECK_FIELD_EXPRESSION && copy->to_field) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -582,7 +585,7 @@ static void do_varstring2_mb(Copy_field *copy) Well_formed_prefix prefix(cs, (char*) from_beg, from_length, char_length); if (prefix.length() < from_length) { - if (current_thd->count_cuted_fields) + if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -754,7 +757,8 @@ Field::Copy_func *Field_varstring::get_copy_func(const Field *from) const return do_field_varbinary_pre50; if (Field_varstring::real_type() != from->real_type() || Field_varstring::charset() != from->charset() || - length_bytes != ((const Field_varstring*) from)->length_bytes) + length_bytes != ((const Field_varstring*) from)->length_bytes || + !compression_method() != !from->compression_method()) return do_field_string; return length_bytes == 1 ? (from->charset()->mbmaxlen == 1 ? do_varstring1 : do_varstring1_mb) : diff --git a/sql/filesort.cc b/sql/filesort.cc index 476b0bfb7dd..4cf2a00dbc4 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -22,12 +22,9 @@ Sorts a database */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "filesort.h" -#ifdef HAVE_STDDEF_H -#include <stddef.h> /* for macro offsetof */ -#endif #include <m_ctype.h> #include "sql_sort.h" #include "probes_mysql.h" @@ -37,7 +34,6 @@ #include "bounded_queue.h" #include "filesort_utils.h" #include "sql_select.h" -#include "log_slow.h" #include "debug_sync.h" /// How to write record_ref. diff --git a/sql/filesort.h b/sql/filesort.h index 2b4f7ac2654..bd1d81f91ef 100644 --- a/sql/filesort.h +++ b/sql/filesort.h @@ -17,7 +17,7 @@ #define FILESORT_INCLUDED #include "my_base.h" /* ha_rows */ -#include "sql_list.h" /* Sql_alloc */ +#include "sql_alloc.h" #include "filesort_utils.h" class SQL_SELECT; diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc index cb0b2d52b6f..b39bb880c15 100644 --- a/sql/filesort_utils.cc +++ b/sql/filesort_utils.cc @@ -13,11 +13,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "filesort_utils.h" #include "sql_const.h" #include "sql_sort.h" #include "table.h" -#include "my_sys.h" namespace { diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h index d537b602edf..544cd3e0f2b 100644 --- a/sql/filesort_utils.h +++ b/sql/filesort_utils.h @@ -16,7 +16,6 @@ #ifndef FILESORT_UTILS_INCLUDED #define FILESORT_UTILS_INCLUDED -#include "my_global.h" #include "my_base.h" #include "sql_array.h" diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc index ab48542add6..da70daea4a8 100644 --- a/sql/gcalc_slicescan.cc +++ b/sql/gcalc_slicescan.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <my_sys.h> #include <m_string.h> diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc index 71118ae1c9f..2aeaafc1a76 100644 --- a/sql/gcalc_tools.cc +++ b/sql/gcalc_tools.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #ifdef HAVE_SPATIAL diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index 3a3273d279b..b2bc1aee091 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -78,11 +78,9 @@ So, we can read full search-structure as 32-bit word */ #define NO_YACC_SYMBOLS -#include <my_global.h> +#include "mariadb.h" #include "mysql_version.h" #include "lex.h" -#include <stdlib.h> -#include <stdio.h> #include <string.h> #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc index bd2b9728177..4b5e0746eac 100644 --- a/sql/gen_lex_token.cc +++ b/sql/gen_lex_token.cc @@ -14,9 +14,7 @@ along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> -#include <stdlib.h> -#include <stdio.h> +#include "mariadb.h" #include <string.h> /* We only need the tokens here */ @@ -132,6 +130,8 @@ void compute_tokens() set_token(WITH_CUBE_SYM, "WITH CUBE"); set_token(WITH_ROLLUP_SYM, "WITH ROLLUP"); + set_token(VALUES_IN_SYM, "VALUES IN"); + set_token(VALUES_LESS_SYM, "VALUES LESS"); set_token(NOT2_SYM, "!"); set_token(OR2_SYM, "|"); set_token(PARAM_MARKER, "?"); diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc index c1b5cfbe254..e75800d8986 100644 --- a/sql/group_by_handler.cc +++ b/sql/group_by_handler.cc @@ -21,6 +21,7 @@ upper level. */ +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" diff --git a/sql/gstream.cc b/sql/gstream.cc index adb46083621..ff3604ac7a4 100644 --- a/sql/gstream.cc +++ b/sql/gstream.cc @@ -18,7 +18,7 @@ NOTE: These functions assumes that the string is end \0 terminated! */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "gstream.h" #include "m_string.h" // LEX_STRING diff --git a/sql/gstream.h b/sql/gstream.h index f10b7e9b830..b9310b716ba 100644 --- a/sql/gstream.h +++ b/sql/gstream.h @@ -17,8 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* NULL, NullS */ -#include "my_sys.h" /* MY_ALLOW_ZERO_PTR */ +#include <my_sys.h> /* MY_ALLOW_ZERO_PTR */ #include "m_ctype.h" /* my_charset_latin1, my_charset_bin */ class Gis_read_stream diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 843b854e5f7..44ae66c76c8 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -35,7 +35,7 @@ Partitioning lays the foundation for more manageable databases that are extremely large. It does also lay the foundation for more parallelism in the execution of queries. This functionality will grow with later - versions of MySQL. + versions of MySQL/MariaDB. The partition is setup to use table locks. It implements an partition "SHARE" that is inserted into a hash by table name. You can use this to store @@ -46,7 +46,7 @@ if this file. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" // append_file_to_dir #include "create_options.h" @@ -58,6 +58,7 @@ #include "sql_plugin.h" #include "sql_show.h" // append_identifier #include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE +#include "sql_select.h" #include "debug_sync.h" @@ -73,9 +74,7 @@ HA_REC_NOT_IN_SEQ | \ HA_CAN_REPAIR) #define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \ - HA_CAN_FULLTEXT | \ HA_DUPLICATE_POS | \ - HA_CAN_SQL_HANDLER | \ HA_CAN_INSERT_DELAYED | \ HA_READ_BEFORE_WRITE_REMOVAL |\ HA_CAN_TABLES_WITHOUT_ROLLBACK) @@ -92,9 +91,6 @@ static handler *partition_create_handler(handlerton *hton, static uint partition_flags(); static uint alter_table_flags(uint flags); -extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2); -extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); - /* If frm_error() is called then we will use this to to find out what file extensions exist for the storage engine. This is also used by the default @@ -126,7 +122,6 @@ static void init_partition_psi_keys(void) static int partition_initialize(void *p) { - handlerton *partition_hton; partition_hton= (handlerton *)p; @@ -166,12 +161,8 @@ bool Partition_share::init(uint num_parts) auto_inc_initialized= false; partition_name_hash_initialized= false; next_auto_inc_val= 0; - partitions_share_refs= new Parts_share_refs; - if (!partitions_share_refs) - DBUG_RETURN(true); - if (partitions_share_refs->init(num_parts)) + if (partitions_share_refs.init(num_parts)) { - delete partitions_share_refs; DBUG_RETURN(true); } DBUG_RETURN(false); @@ -189,7 +180,7 @@ bool Partition_share::init(uint num_parts) New partition object */ -static handler *partition_create_handler(handlerton *hton, +static handler *partition_create_handler(handlerton *hton, TABLE_SHARE *share, MEM_ROOT *mem_root) { @@ -232,8 +223,6 @@ static uint alter_table_flags(uint flags __attribute__((unused))) HA_FAST_CHANGE_PARTITION); } -const uint32 ha_partition::NO_CURRENT_PART_ID= NOT_A_PARTITION_ID; - /* Constructor method @@ -249,12 +238,19 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(table)"); - init_alloc_root(&m_mem_root, 512, 512, MYF(0)); - init_handler_variables(); + ha_partition_init(); DBUG_VOID_RETURN; } +/* Initialize all partition variables */ + +void ha_partition::ha_partition_init() +{ + init_alloc_root(&m_mem_root, 512, 512, MYF(0)); + init_handler_variables(); +} + /* Constructor method @@ -271,8 +267,7 @@ ha_partition::ha_partition(handlerton *hton, partition_info *part_info) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); DBUG_ASSERT(part_info); - init_alloc_root(&m_mem_root, 512, 512, MYF(0)); - init_handler_variables(); + ha_partition_init(); m_part_info= part_info; m_create_handler= TRUE; m_is_sub_partitioned= m_part_info->is_sub_partitioned(); @@ -298,8 +293,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share, :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(clone)"); - init_alloc_root(&m_mem_root, 512, 512, MYF(0)); - init_handler_variables(); + ha_partition_init(); m_part_info= part_info_arg; m_create_handler= TRUE; m_is_sub_partitioned= m_part_info->is_sub_partitioned(); @@ -362,6 +356,7 @@ void ha_partition::init_handler_variables() m_curr_key_info[0]= NULL; m_curr_key_info[1]= NULL; m_part_func_monotonicity_info= NON_MONOTONIC; + m_key_not_found= FALSE; auto_increment_lock= FALSE; auto_increment_safe_stmt_log_lock= FALSE; /* @@ -377,6 +372,27 @@ void ha_partition::init_handler_variables() m_new_partitions_share_refs.empty(); m_part_ids_sorted_by_num_of_records= NULL; + m_range_info= NULL; + m_mrr_full_buffer_size= 0; + m_mrr_new_full_buffer_size= 0; + m_mrr_full_buffer= NULL; + m_mrr_range_first= NULL; + + m_pre_calling= FALSE; + m_pre_call_use_parallel= FALSE; + + ft_first= ft_current= NULL; + bulk_access_executing= FALSE; // For future + + /* + Clear bitmaps to allow on one to call my_bitmap_free() on them at any time + */ + my_bitmap_clear(&m_bulk_insert_started); + my_bitmap_clear(&m_locked_partitions); + my_bitmap_clear(&m_partitions_to_reset); + my_bitmap_clear(&m_key_not_found_partitions); + my_bitmap_clear(&m_mrr_used_partitions); + #ifdef DONT_HAVE_TO_BE_INITALIZED m_start_key.flag= 0; m_ordered= TRUE; @@ -385,9 +401,9 @@ void ha_partition::init_handler_variables() const char *ha_partition::table_type() const -{ +{ // we can do this since we only support a single engine type - return m_file[0]->table_type(); + return m_file[0]->table_type(); } @@ -688,8 +704,9 @@ int ha_partition::create(const char *name, TABLE *table_arg, partition_element *part_elem; handler **file, **abort_file; DBUG_ENTER("ha_partition::create"); + DBUG_PRINT("enter", ("name: '%s'", name)); - DBUG_ASSERT(*fn_rext((char*)name) == '\0'); + DBUG_ASSERT(!fn_frm_ext(name)); /* Not allowed to create temporary partitioned tables */ if (create_info && create_info->tmp_table()) @@ -701,7 +718,6 @@ int ha_partition::create(const char *name, TABLE *table_arg, if (get_from_handler_file(name, ha_thd()->mem_root, false)) DBUG_RETURN(TRUE); DBUG_ASSERT(m_file_buffer); - DBUG_PRINT("enter", ("name: (%s)", name)); name_buffer_ptr= m_name_buffer_ptr; file= m_file; /* @@ -964,7 +980,7 @@ int ha_partition::rename_partitions(const char *path) When state is PART_IS_CHANGED it means that we have created a new TEMP partition that is to be renamed to normal partition name and we are to delete the old partition with currently the normal name. - + We perform this operation by 1) Delete old partition with normal partition name 2) Signal this in table log entry @@ -1199,7 +1215,7 @@ int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt) DBUG_RETURN(handle_opt_partitions(thd, check_opt, PRELOAD_KEYS_PARTS)); } - + /* Handle optimize/analyze/check/repair of one partition @@ -1221,7 +1237,7 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, int error; handler *file= m_file[part_id]; DBUG_ENTER("handle_opt_part"); - DBUG_PRINT("enter", ("flag = %u", flag)); + DBUG_PRINT("enter", ("flag: %u", flag)); if (flag == OPTIMIZE_PARTS) error= file->ha_optimize(thd, check_opt); @@ -1265,7 +1281,7 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, /* - print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE + print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE (modelled after mi_check_print_msg) TODO: move this into the handler, or rewrite mysql_admin_table. */ @@ -1294,7 +1310,7 @@ static bool print_admin_msg(THD* thd, uint len, va_end(args); if (msg_length >= (len - 1)) goto err; - msgbuf[len - 1] = 0; // healthy paranoia + msgbuf[len - 1]= 0; // healthy paranoia if (!thd->vio_ok()) @@ -1388,7 +1404,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error", table_share->db.str, table->alias, opt_op_name[flag], - "Subpartition %s returned error", + "Subpartition %s returned error", sub_elem->partition_name); } /* reset part_state for the remaining partitions */ @@ -1414,7 +1430,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, { print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error", table_share->db.str, table->alias, - opt_op_name[flag], "Partition %s returned error", + opt_op_name[flag], "Partition %s returned error", part_elem->partition_name); } /* reset part_state for the remaining partitions */ @@ -1456,7 +1472,7 @@ bool ha_partition::check_and_repair(THD *thd) } while (*(++file)); DBUG_RETURN(FALSE); } - + /** @breif Check if the table can be automatically repaired @@ -1496,7 +1512,7 @@ bool ha_partition::is_crashed() const } while (*(++file)); DBUG_RETURN(FALSE); } - + /* Prepare by creating a new partition @@ -1541,7 +1557,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl, if ((error= set_up_table_before_create(tbl, part_name, create_info, p_elem))) goto error_create; - tbl->s->connect_string = p_elem->connect_string; + if (!(file->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION)) + tbl->s->connect_string= p_elem->connect_string; if ((error= file->ha_create(part_name, tbl, create_info))) { /* @@ -1876,7 +1893,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, in the partitions. */ - uint disable_non_uniq_indexes = indexes_are_disabled(); + uint disable_non_uniq_indexes= indexes_are_disabled(); i= 0; part_count= 0; @@ -2115,10 +2132,11 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) DATA DIRECTORY and INDEX DIRECTORY are never applied to the whole partitioned table, only its parts. */ - my_bool from_alter = (create_info->data_file_name == (const char*) -1); - create_info->data_file_name= create_info->index_file_name = NULL; + my_bool from_alter= (create_info->data_file_name == (const char*) -1); + create_info->data_file_name= create_info->index_file_name= NULL; - create_info->connect_string= null_clex_str; + if (!(m_file[0]->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION)) + create_info->connect_string= null_clex_str; /* We do not need to update the individual partition DATA DIRECTORY settings @@ -2136,8 +2154,8 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) List_iterator<partition_element> part_it(m_part_info->partitions); partition_element *part_elem, *sub_elem; uint num_subparts= m_part_info->num_subparts; - uint num_parts = num_subparts ? m_file_tot_parts / num_subparts - : m_file_tot_parts; + uint num_parts= (num_subparts ? m_file_tot_parts / num_subparts : + m_file_tot_parts); HA_CREATE_INFO dummy_info; memset(&dummy_info, 0, sizeof(dummy_info)); @@ -2253,7 +2271,7 @@ void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) comment Original comment RETURN VALUE - new comment + new comment DESCRIPTION No comment changes so far @@ -2380,7 +2398,7 @@ uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type) /* Here we rely on the fact that all tables are of the same type */ uint8 type= m_file[0]->table_cache_type(); (*tables_type)|= type; - DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts)); + DBUG_PRINT("enter", ("cnt: %u", (uint) m_tot_parts)); /* We need save underlying tables only for HA_CACHE_TBL_ASKTRANSACT: HA_CACHE_TBL_NONTRANSACT - because all changes goes through partition table @@ -2535,7 +2553,7 @@ register_query_cache_dependant_tables(THD *thd, @return status @retval TRUE Error @retval FALSE Success - + @details Set up 1) Comment on partition @@ -2545,7 +2563,7 @@ register_query_cache_dependant_tables(THD *thd, */ int ha_partition::set_up_table_before_create(TABLE *tbl, - const char *partition_name_with_path, + const char *partition_name_with_path, HA_CREATE_INFO *info, partition_element *part_elem) { @@ -2640,8 +2658,7 @@ bool ha_partition::create_handler_file(const char *name) DBUG_ENTER("create_handler_file"); num_parts= m_part_info->partitions.elements; - DBUG_PRINT("info", ("table name = %s, num_parts = %u", name, - num_parts)); + DBUG_PRINT("enter", ("table name: %s num_parts: %u", name, num_parts)); tot_name_len= 0; for (i= 0; i < num_parts; i++) { @@ -2760,7 +2777,7 @@ bool ha_partition::create_handler_file(const char *name) { uchar buffer[4]; part_elem= part_it++; - uint length = part_elem->connect_string.length; + uint length= part_elem->connect_string.length; int4store(buffer, length); if (my_write(file, buffer, 4, MYF(MY_WME | MY_NABP)) || my_write(file, (uchar *) part_elem->connect_string.str, length, @@ -2958,7 +2975,7 @@ bool ha_partition::read_par_file(const char *name) if (chksum) goto err2; m_tot_parts= uint4korr((file_buffer) + PAR_NUM_PARTS_OFFSET); - DBUG_PRINT("info", ("No of parts = %u", m_tot_parts)); + DBUG_PRINT("info", ("No of parts: %u", m_tot_parts)); tot_partition_words= (m_tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; tot_name_len_offset= file_buffer + PAR_ENGINES_OFFSET + @@ -3064,7 +3081,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root) } my_afree(engine_array); - + if (create_handlers(mem_root)) { clear_handler_file(); @@ -3286,9 +3303,8 @@ bool ha_partition::set_ha_share_ref(Handler_share **ha_share_arg) DBUG_RETURN(true); if (!(part_share= get_share())) DBUG_RETURN(true); - DBUG_ASSERT(part_share->partitions_share_refs); - DBUG_ASSERT(part_share->partitions_share_refs->num_parts >= m_tot_parts); - ha_shares= part_share->partitions_share_refs->ha_shares; + DBUG_ASSERT(part_share->partitions_share_refs.num_parts >= m_tot_parts); + ha_shares= part_share->partitions_share_refs.ha_shares; for (i= 0; i < m_tot_parts; i++) { if (m_file[i]->set_ha_share_ref(&ha_shares[i])) @@ -3347,63 +3363,53 @@ void ha_partition::free_partition_bitmaps() my_bitmap_free(&m_locked_partitions); my_bitmap_free(&m_partitions_to_reset); my_bitmap_free(&m_key_not_found_partitions); + my_bitmap_free(&m_mrr_used_partitions); } /** Helper function for initializing all internal bitmaps. + + Note: + All bitmaps, including partially allocated, are freed in + free_partion_bitmaps() */ bool ha_partition::init_partition_bitmaps() { DBUG_ENTER("ha_partition::init_partition_bitmaps"); + /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) DBUG_RETURN(true); - bitmap_clear_all(&m_bulk_insert_started); /* Initialize the bitmap we use to keep track of locked partitions */ if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE)) - { - my_bitmap_free(&m_bulk_insert_started); DBUG_RETURN(true); - } - bitmap_clear_all(&m_locked_partitions); /* Initialize the bitmap we use to keep track of partitions which may have something to reset in ha_reset(). */ if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE)) - { - my_bitmap_free(&m_bulk_insert_started); - my_bitmap_free(&m_locked_partitions); DBUG_RETURN(true); - } - bitmap_clear_all(&m_partitions_to_reset); /* Initialize the bitmap we use to keep track of partitions which returned HA_ERR_KEY_NOT_FOUND from index_read_map. */ if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) - { - my_bitmap_free(&m_bulk_insert_started); - my_bitmap_free(&m_locked_partitions); - my_bitmap_free(&m_partitions_to_reset); DBUG_RETURN(true); - } - bitmap_clear_all(&m_key_not_found_partitions); - m_key_not_found= false; + + if (bitmap_init(&m_mrr_used_partitions, NULL, m_tot_parts, TRUE)) + DBUG_RETURN(true); + /* Initialize the bitmap for read/lock_partitions */ if (!m_is_clone_of) { DBUG_ASSERT(!m_clone_mem_root); if (m_part_info->set_partition_bitmaps(NULL)) - { - free_partition_bitmaps(); DBUG_RETURN(true); - } } DBUG_RETURN(false); } @@ -3468,9 +3474,29 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) } if (init_partition_bitmaps()) - DBUG_RETURN(error); - - DBUG_ASSERT(m_part_info); + goto err_alloc; + + /* Allocate memory used with MMR */ + if (!(m_range_info= (void **) + my_multi_malloc(MYF(MY_WME), + &m_range_info, sizeof(range_id_t) * m_tot_parts, + &m_stock_range_seq, sizeof(uint) * m_tot_parts, + &m_mrr_buffer, sizeof(HANDLER_BUFFER) * m_tot_parts, + &m_mrr_buffer_size, sizeof(uint) * m_tot_parts, + &m_part_mrr_range_length, sizeof(uint) * m_tot_parts, + &m_part_mrr_range_first, + sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts, + &m_part_mrr_range_current, + sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts, + &m_partition_part_key_multi_range_hld, + sizeof(PARTITION_PART_KEY_MULTI_RANGE_HLD) * + m_tot_parts, + NullS))) + goto err_alloc; + + bzero(m_mrr_buffer, m_tot_parts * sizeof(HANDLER_BUFFER)); + bzero(m_part_mrr_range_first, + sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts); if (m_is_clone_of) { @@ -3509,21 +3535,31 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) file= m_file; do { + LEX_CSTRING save_connect_string= table->s->connect_string; if ((error= create_partition_name(name_buff, sizeof(name_buff), name, name_buffer_ptr, NORMAL_PART_NAME, FALSE))) goto err_handler; - table->s->connect_string = m_connect_string[(uint)(file-m_file)]; - if ((error= (*file)->ha_open(table, name_buff, mode, - test_if_locked | HA_OPEN_NO_PSI_CALL))) + if (!((*file)->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION)) + table->s->connect_string= m_connect_string[(uint)(file-m_file)]; + error= (*file)->ha_open(table, name_buff, mode, + test_if_locked | HA_OPEN_NO_PSI_CALL); + table->s->connect_string= save_connect_string; + if (error) goto err_handler; - bzero(&table->s->connect_string, sizeof(LEX_STRING)); if (m_file == file) m_num_locks= (*file)->lock_count(); DBUG_ASSERT(m_num_locks == (*file)->lock_count()); name_buffer_ptr+= strlen(name_buffer_ptr) + 1; } while (*(++file)); } - + /* + We want to know the upper bound for locks, to allocate enough memory. + There is no performance lost if we simply return in lock_count() the + maximum number locks needed, only some minor over allocation of memory + in get_lock_data(). + */ + m_num_locks*= m_tot_parts; + file= m_file; ref_length= (*file)->ref_length; check_table_flags= (((*file)->ha_table_flags() & @@ -3543,7 +3579,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { error= HA_ERR_INITIALIZATION; /* set file to last handler, so all of them are closed */ - file = &m_file[m_tot_parts - 1]; + file= &m_file[m_tot_parts - 1]; goto err_handler; } } @@ -3583,6 +3619,8 @@ err_handler: (*file)->ha_close(); err_alloc: free_partition_bitmaps(); + my_free(m_range_info); + m_range_info= 0; DBUG_RETURN(error); } @@ -3660,7 +3698,7 @@ handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root) /* Allocate new_handler->ref here because otherwise ha_open will allocate it - on this->table->mem_root and we will not be able to reclaim that memory + on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ if (!(new_handler->ref= (uchar*) alloc_root(mem_root, @@ -3702,12 +3740,59 @@ int ha_partition::close(void) { bool first= TRUE; handler **file; + uint i; + st_partition_ft_info *tmp_ft_info; DBUG_ENTER("ha_partition::close"); - DBUG_ASSERT(table->s == table_share); + DBUG_ASSERT(m_part_info); + destroy_record_priority_queue(); free_partition_bitmaps(); - DBUG_ASSERT(m_part_info); + + for (; ft_first ; ft_first= tmp_ft_info) + { + tmp_ft_info= ft_first->next; + my_free(ft_first); + } + + /* Free active mrr_ranges */ + for (i= 0; i < m_tot_parts; i++) + { + if (m_part_mrr_range_first[i]) + { + PARTITION_PART_KEY_MULTI_RANGE *tmp_mrr_range_first= + m_part_mrr_range_first[i]; + do + { + PARTITION_PART_KEY_MULTI_RANGE *tmp_mrr_range_current; + tmp_mrr_range_current= tmp_mrr_range_first; + tmp_mrr_range_first= tmp_mrr_range_first->next; + my_free(tmp_mrr_range_current); + } while (tmp_mrr_range_first); + } + } + if (m_mrr_range_first) + { + do + { + m_mrr_range_current= m_mrr_range_first; + m_mrr_range_first= m_mrr_range_first->next; + if (m_mrr_range_current->key[0]) + my_free(m_mrr_range_current->key[0]); + if (m_mrr_range_current->key[1]) + my_free(m_mrr_range_current->key[1]); + my_free(m_mrr_range_current); + } while (m_mrr_range_first); + } + my_free(m_range_info); + m_range_info= NULL; // Safety + + if (m_mrr_full_buffer) + { + my_free(m_mrr_full_buffer); + m_mrr_full_buffer= NULL; + m_mrr_full_buffer_size= 0; + } file= m_file; repeat: @@ -3769,7 +3854,7 @@ repeat: int ha_partition::external_lock(THD *thd, int lock_type) { - uint error; + int error; uint i, first_used_partition; MY_BITMAP *used_partitions; DBUG_ENTER("ha_partition::external_lock"); @@ -3787,7 +3872,7 @@ int ha_partition::external_lock(THD *thd, int lock_type) i < m_tot_parts; i= bitmap_get_next_set(used_partitions, i)) { - DBUG_PRINT("info", ("external_lock(thd, %d) part %d", lock_type, i)); + DBUG_PRINT("info", ("external_lock(thd, %d) part %u", lock_type, i)); if ((error= m_file[i]->ha_external_lock(thd, lock_type))) { if (lock_type != F_UNLCK) @@ -3903,7 +3988,7 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, i < m_tot_parts; i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) { - DBUG_PRINT("info", ("store lock %d iteration", i)); + DBUG_PRINT("info", ("store lock %u iteration", i)); to= m_file[i]->store_lock(thd, to, lock_type); } } @@ -3964,25 +4049,14 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type) @returns Number of locks returned in call to store_lock @desc - Returns the number of store locks needed in call to store lock. - We return number of partitions we will lock multiplied with number of - locks needed by each partition. Assists the above functions in allocating - sufficient space for lock structures. + Returns the maxinum possible number of store locks needed in call to + store lock. */ uint ha_partition::lock_count() const { DBUG_ENTER("ha_partition::lock_count"); - /* - The caller want to know the upper bound, to allocate enough memory. - There is no performance lost if we simply return maximum number locks - needed, only some minor over allocation of memory in get_lock_data(). - - Also notice that this may be called for another thread != table->in_use, - when mysql_lock_abort_for_thread() is called. So this is more safe, then - using number of partitions after pruning. - */ - DBUG_RETURN(m_tot_parts * m_num_locks); + DBUG_RETURN(m_num_locks); } @@ -4061,7 +4135,7 @@ void ha_partition::try_semi_consistent_read(bool yes) { uint i; DBUG_ENTER("ha_partition::try_semi_consistent_read"); - + i= bitmap_get_first_set(&(m_part_info->read_partitions)); DBUG_ASSERT(i != MY_BIT_NONE); for (; @@ -4126,7 +4200,7 @@ int ha_partition::write_row(uchar * buf) sql_mode_t saved_sql_mode= thd->variables.sql_mode; bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null; DBUG_ENTER("ha_partition::write_row"); - DBUG_ASSERT(buf == m_rec0); + DBUG_PRINT("enter", ("partition this: %p", this)); /* If we have an auto_increment column and we are writing a changed row @@ -4134,15 +4208,8 @@ int ha_partition::write_row(uchar * buf) */ if (have_auto_increment) { - if (!part_share->auto_inc_initialized && - !table_share->next_number_keypart) - { - /* - If auto_increment in table_share is not initialized, start by - initializing it. - */ - info(HA_STATUS_AUTO); - } + if (!table_share->next_number_keypart) + update_next_auto_inc_val(); error= update_auto_increment(); /* @@ -4186,7 +4253,7 @@ int ha_partition::write_row(uchar * buf) goto exit; } m_last_part= part_id; - DBUG_PRINT("info", ("Insert in partition %d", part_id)); + DBUG_PRINT("info", ("Insert in partition %u", part_id)); start_part_bulk_insert(thd, part_id); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ @@ -4278,7 +4345,7 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) start_part_bulk_insert(thd, new_part_id); if (new_part_id == old_part_id) { - DBUG_PRINT("info", ("Update in partition %d", new_part_id)); + DBUG_PRINT("info", ("Update in partition %u", (uint) new_part_id)); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ error= m_file[new_part_id]->ha_update_row(old_data, new_data); reenable_binlog(thd); @@ -4298,8 +4365,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) This gives the same behavior for partitioned vs non partitioned tables. */ table->next_number_field= NULL; - DBUG_PRINT("info", ("Update from partition %d to partition %d", - old_part_id, new_part_id)); + DBUG_PRINT("info", ("Update from partition %u to partition %u", + (uint) old_part_id, (uint) new_part_id)); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ error= m_file[new_part_id]->ha_write_row((uchar*) new_data); reenable_binlog(thd); @@ -4335,8 +4402,11 @@ exit: bitmap_is_set(table->write_set, table->found_next_number_field->field_index)) { - if (!part_share->auto_inc_initialized) - info(HA_STATUS_AUTO); + update_next_auto_inc_val(); + /* + The following call is safe as part_share->auto_inc_initialized + (tested in the call) is guaranteed to be set for update statements. + */ set_auto_increment_if_higher(table->found_next_number_field); } DBUG_RETURN(error); @@ -4623,9 +4693,9 @@ void ha_partition::start_part_bulk_insert(THD *thd, uint part_id) DESCRIPTION If the estimated number of rows to insert is less than 10 (but not 0) the new buffer size is same as original buffer size. - In case of first partition of when partition function is monotonic + In case of first partition of when partition function is monotonic new buffer size is same as the original buffer size. - For rest of the partition total buffer of 10*original_size is divided + For rest of the partition total buffer of 10*original_size is divided equally if number of partition is more than 10 other wise each partition will be allowed to use original buffer size. */ @@ -4663,7 +4733,7 @@ long ha_partition::estimate_read_buffer_size(long original_size) If monotonic partitioning function was used guess that 50 % of the inserts goes to the first partition For all other cases, guess on equal distribution between the partitions -*/ +*/ ha_rows ha_partition::guess_bulk_insert_rows() { DBUG_ENTER("guess_bulk_insert_rows"); @@ -4672,7 +4742,7 @@ ha_rows ha_partition::guess_bulk_insert_rows() DBUG_RETURN(estimation_rows_to_insert); /* If first insert/partition and monotonic partition function, guess 50%. */ - if (!m_bulk_inserted_rows && + if (!m_bulk_inserted_rows && m_part_func_monotonicity_info != NON_MONOTONIC && m_tot_parts > 1) DBUG_RETURN(estimation_rows_to_insert / 2); @@ -4738,7 +4808,7 @@ int ha_partition::end_bulk_insert() >0 Error code 0 Success - DESCRIPTION + DESCRIPTION rnd_init() is called when the server wants the storage engine to do a table scan or when the server wants to access data through rnd_pos. @@ -4774,7 +4844,10 @@ int ha_partition::rnd_init(bool scan) */ if (bitmap_is_overlapping(&m_part_info->full_part_field_set, table->write_set)) + { + DBUG_PRINT("info", ("partition set full bitmap")); bitmap_set_all(table->read_set); + } else { /* @@ -4783,6 +4856,7 @@ int ha_partition::rnd_init(bool scan) fields of the partition functions are read such that we can calculate the partition id to place updated and deleted records. */ + DBUG_PRINT("info", ("partition set part_field bitmap")); bitmap_union(table->read_set, &m_part_info->full_part_field_set); } } @@ -4791,9 +4865,9 @@ int ha_partition::rnd_init(bool scan) DBUG_PRINT("info", ("m_part_info->read_partitions: %p", m_part_info->read_partitions.bitmap)); part_id= bitmap_get_first_set(&(m_part_info->read_partitions)); - DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id)); + DBUG_PRINT("info", ("m_part_spec.start_part: %u", (uint) part_id)); - if (MY_BIT_NONE == part_id) + if (part_id == MY_BIT_NONE) { error= 0; goto err1; @@ -4803,7 +4877,7 @@ int ha_partition::rnd_init(bool scan) We have a partition and we are scanning with rnd_next so we bump our cache */ - DBUG_PRINT("info", ("rnd_init on partition %d", part_id)); + DBUG_PRINT("info", ("rnd_init on partition: %u", (uint) part_id)); if (scan) { /* @@ -4812,26 +4886,29 @@ int ha_partition::rnd_init(bool scan) */ rnd_end(); late_extra_cache(part_id); - if ((error= m_file[part_id]->ha_rnd_init(scan))) - goto err; + + m_index_scan_type= partition_no_index_scan; } - else + + for (i= part_id; + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - for (i= part_id; - i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) - { - if ((error= m_file[i]->ha_rnd_init(scan))) - goto err; - } + if ((error= m_file[i]->ha_rnd_init(scan))) + goto err; } + m_scan_value= scan; m_part_spec.start_part= part_id; m_part_spec.end_part= m_tot_parts - 1; - DBUG_PRINT("info", ("m_scan_value=%d", m_scan_value)); + m_rnd_init_and_first= TRUE; + DBUG_PRINT("info", ("m_scan_value: %u", m_scan_value)); DBUG_RETURN(0); err: + if (scan) + late_extra_no_cache(part_id); + /* Call rnd_end for all previously inited partitions. */ for (; part_id < i; @@ -4863,13 +4940,10 @@ int ha_partition::rnd_end() switch (m_scan_value) { case 2: // Error break; - case 1: - if (NO_CURRENT_PART_ID != m_part_spec.start_part) // Table scan - { + case 1: // Table scan + if (m_part_spec.start_part != NO_CURRENT_PART_ID) late_extra_no_cache(m_part_spec.start_part); - m_file[m_part_spec.start_part]->ha_rnd_end(); - } - break; + /* fall through */ case 0: uint i; for (i= bitmap_get_first_set(&m_part_info->read_partitions); @@ -4885,6 +4959,7 @@ int ha_partition::rnd_end() DBUG_RETURN(0); } + /* read next row during full table scan (scan in random row order) @@ -4909,14 +4984,15 @@ int ha_partition::rnd_end() int ha_partition::rnd_next(uchar *buf) { handler *file; - int result= HA_ERR_END_OF_FILE; + int result= HA_ERR_END_OF_FILE, error; uint part_id= m_part_spec.start_part; DBUG_ENTER("ha_partition::rnd_next"); + DBUG_PRINT("enter", ("partition this: %p", this)); /* upper level will increment this once again at end of call */ decrement_statistics(&SSV::ha_read_rnd_next_count); - if (NO_CURRENT_PART_ID == part_id) + if (part_id == NO_CURRENT_PART_ID) { /* The original set of partitions to scan was empty and thus we report @@ -4924,16 +5000,26 @@ int ha_partition::rnd_next(uchar *buf) */ goto end; } - + DBUG_ASSERT(m_scan_value == 1); + + if (m_rnd_init_and_first) + { + m_rnd_init_and_first= FALSE; + error= handle_pre_scan(FALSE, check_parallel_search()); + if (m_pre_calling || error) + DBUG_RETURN(error); + } + file= m_file[part_id]; - + while (TRUE) { result= file->ha_rnd_next(buf); if (!result) { m_last_part= part_id; + DBUG_PRINT("info", ("partition m_last_part: %u", (uint) m_last_part)); m_part_spec.start_part= part_id; table->status= 0; DBUG_RETURN(0); @@ -4950,10 +5036,6 @@ int ha_partition::rnd_next(uchar *buf) /* End current partition */ late_extra_no_cache(part_id); - DBUG_PRINT("info", ("rnd_end on partition %d", part_id)); - if ((result= file->ha_rnd_end())) - break; - /* Shift to next partition */ part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id); if (part_id >= m_tot_parts) @@ -4962,15 +5044,14 @@ int ha_partition::rnd_next(uchar *buf) break; } m_last_part= part_id; + DBUG_PRINT("info", ("partition m_last_part: %u", (uint) m_last_part)); m_part_spec.start_part= part_id; file= m_file[part_id]; - DBUG_PRINT("info", ("rnd_init on partition %d", part_id)); - if ((result= file->ha_rnd_init(1))) - break; late_extra_cache(part_id); } end: + DBUG_PRINT("exit", ("reset start_part")); m_part_spec.start_part= NO_CURRENT_PART_ID; end_dont_reset_start_part: DBUG_RETURN(result); @@ -5122,6 +5203,7 @@ bool ha_partition::init_record_priority_queue() { uint alloc_len; uint used_parts= bitmap_bits_set(&m_part_info->read_partitions); + DBUG_ASSERT(used_parts > 0); /* Allocate record buffer for each used partition. */ m_priority_queue_rec_len= m_rec_length + PARTITION_BYTES_IN_POS; if (!m_using_extended_keys) @@ -5151,20 +5233,15 @@ bool ha_partition::init_record_priority_queue() ptr+= m_priority_queue_rec_len; } m_start_key.key= (const uchar*)ptr; - + /* Initialize priority queue, initialized to reading forward. */ int (*cmp_func)(void *, uchar *, uchar *); - void *cmp_arg; - if (!m_using_extended_keys) - { + void *cmp_arg= (void*) this; + if (!m_using_extended_keys && !(table_flags() & HA_CMP_REF_IS_EXPENSIVE)) cmp_func= cmp_key_rowid_part_id; - cmp_arg= (void*)this; - } else - { cmp_func= cmp_key_part_id; - cmp_arg= (void*)m_curr_key_info; - } + DBUG_PRINT("info", ("partition queue_init(1) used_parts: %u", used_parts)); if (init_queue(&m_queue, used_parts, 0, 0, cmp_func, cmp_arg, 0, 0)) { my_free(m_ordered_rec_buffer); @@ -5215,8 +5292,8 @@ int ha_partition::index_init(uint inx, bool sorted) int error= 0; uint i; DBUG_ENTER("ha_partition::index_init"); + DBUG_PRINT("enter", ("partition this: %p inx: %u sorted: %u", this, inx, sorted)); - DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted)); active_index= inx; m_part_spec.start_part= NO_CURRENT_PART_ID; m_start_key.length= 0; @@ -5251,11 +5328,14 @@ int ha_partition::index_init(uint inx, bool sorted) But this is required for operations that may need to change data only. */ if (get_lock_type() == F_WRLCK) + { + DBUG_PRINT("info", ("partition set part_field bitmap")); bitmap_union(table->read_set, &m_part_info->full_part_field_set); + } if (sorted) { /* - An ordered scan is requested. We must make sure all fields of the + An ordered scan is requested. We must make sure all fields of the used index are in the read set, as partitioning requires them for sorting (see ha_partition::handle_ordered_index_scan). @@ -5322,19 +5402,21 @@ err: int ha_partition::index_end() { int error= 0; - uint i; + handler **file; DBUG_ENTER("ha_partition::index_end"); active_index= MAX_KEY; m_part_spec.start_part= NO_CURRENT_PART_ID; - for (i= bitmap_get_first_set(&m_part_info->read_partitions); - i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + file= m_file; + do { - int tmp; - if ((tmp= m_file[i]->ha_index_end())) - error= tmp; - } + if ((*file)->inited == INDEX) + { + int tmp; + if ((tmp= (*file)->ha_index_end())) + error= tmp; + } + } while (*(++file)); destroy_record_priority_queue(); DBUG_RETURN(error); } @@ -5383,34 +5465,26 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key, /* Compare two part_no partition numbers */ static int cmp_part_ids(uchar *ref1, uchar *ref2) { - /* The following was taken from ha_partition::cmp_ref */ - my_ptrdiff_t diff1= ref2[1] - ref1[1]; - my_ptrdiff_t diff2= ref2[0] - ref1[0]; - if (!diff1 && !diff2) - return 0; - - if (diff1 > 0) - return(-1); - - if (diff1 < 0) - return(+1); - - if (diff2 > 0) - return(-1); - - return(+1); + uint32 diff2= uint2korr(ref2); + uint32 diff1= uint2korr(ref1); + if (diff2 > diff1) + return -1; + if (diff2 < diff1) + return 1; + return 0; } /* @brief - Provide ordering by (key_value, part_no). + Provide ordering by (key_value, part_no). */ -extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2) +extern "C" int cmp_key_part_id(void *ptr, uchar *ref1, uchar *ref2) { + ha_partition *file= (ha_partition*)ptr; int res; - if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS, + if ((res= key_rec_cmp(file->m_curr_key_info, ref1 + PARTITION_BYTES_IN_POS, ref2 + PARTITION_BYTES_IN_POS))) { return res; @@ -5420,7 +5494,7 @@ extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2) /* @brief - Provide ordering by (key_value, underying_table_rowid, part_no). + Provide ordering by (key_value, underying_table_rowid, part_no). */ extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2) { @@ -5445,26 +5519,26 @@ extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2) Common routine for a number of index_read variants @param buf Buffer where the record should be returned. - @param have_start_key TRUE <=> the left endpoint is available, i.e. + @param have_start_key TRUE <=> the left endpoint is available, i.e. we're in index_read call or in read_range_first call and the range has left endpoint. FALSE <=> there is no left endpoint (we're in read_range_first() call and the range has no left endpoint). - + @return Operation status - @retval 0 OK + @retval 0 OK @retval HA_ERR_END_OF_FILE Whole index scanned, without finding the record. @retval HA_ERR_KEY_NOT_FOUND Record not found, but index cursor positioned. @retval other error code. @details - Start scanning the range (when invoked from read_range_first()) or doing + Start scanning the range (when invoked from read_range_first()) or doing an index lookup (when invoked from index_read_XXX): - If possible, perform partition selection - Find the set of partitions we're going to use - Depending on whether we need ordering: - NO: Get the first record from first used partition (see + NO: Get the first record from first used partition (see handle_unordered_scan_next_partition) YES: Fill the priority queue and get the record that is the first in the ordering @@ -5482,7 +5556,7 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) if (have_start_key) { - m_start_key.length= key_len= calculate_key_len(table, active_index, + m_start_key.length= key_len= calculate_key_len(table, active_index, m_start_key.key, m_start_key.keypart_map); DBUG_PRINT("info", ("have_start_key map %lu find_flag %u len %u", @@ -5494,7 +5568,7 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) DBUG_RETURN(error); } - if (have_start_key && + if (have_start_key && (m_start_key.flag == HA_READ_PREFIX_LAST || m_start_key.flag == HA_READ_PREFIX_LAST_OR_PREV || m_start_key.flag == HA_READ_BEFORE_KEY)) @@ -5514,7 +5588,9 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) The unordered index scan will use the partition set created. */ DBUG_PRINT("info", ("doing unordered scan")); - error= handle_unordered_scan_next_partition(buf); + error= handle_pre_scan(FALSE, FALSE); + if (!error) + error= handle_unordered_scan_next_partition(buf); } else { @@ -5562,7 +5638,7 @@ int ha_partition::index_first(uchar * buf) /* Start an index scan from rightmost record and return first record - + SYNOPSIS index_last() buf Read row in MySQL Row Format @@ -5595,7 +5671,7 @@ int ha_partition::index_last(uchar * buf) SYNOPSIS ha_partition::common_first_last() - + see index_first for rest */ @@ -5607,7 +5683,11 @@ int ha_partition::common_first_last(uchar *buf) return error; if (!m_ordered_scan_ongoing && m_index_scan_type != partition_index_last) - return handle_unordered_scan_next_partition(buf); + { + if ((error= handle_pre_scan(FALSE, check_parallel_search()))) + return error; + return handle_unordered_scan_next_partition(buf); + } return handle_ordered_index_scan(buf, FALSE); } @@ -5635,7 +5715,7 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index, get_partition_set(table, buf, index, &m_start_key, &m_part_spec); - /* + /* We have either found exactly 1 partition (in which case start_part == end_part) or no matching partitions (start_part > end_part) @@ -5700,7 +5780,8 @@ int ha_partition::index_next(uchar * buf) and if direction changes, we must step back those partitions in the record queue so we don't return a value from the wrong direction. */ - DBUG_ASSERT(m_index_scan_type != partition_index_last); + if (m_index_scan_type == partition_index_last) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); if (!m_ordered_scan_ongoing) { DBUG_RETURN(handle_unordered_next(buf, FALSE)); @@ -5733,7 +5814,8 @@ int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen) decrement_statistics(&SSV::ha_read_next_count); DBUG_ASSERT(keylen == m_start_key.length); - DBUG_ASSERT(m_index_scan_type != partition_index_last); + if (m_index_scan_type == partition_index_last) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); if (!m_ordered_scan_ongoing) DBUG_RETURN(handle_unordered_next(buf, TRUE)); DBUG_RETURN(handle_ordered_next(buf, TRUE)); @@ -5761,7 +5843,8 @@ int ha_partition::index_prev(uchar * buf) decrement_statistics(&SSV::ha_read_prev_count); /* TODO: read comment in index_next */ - DBUG_ASSERT(m_index_scan_type != partition_index_first); + if (m_index_scan_type == partition_index_first) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); DBUG_RETURN(handle_ordered_prev(buf)); } @@ -5832,6 +5915,988 @@ int ha_partition::read_range_next() DBUG_RETURN(handle_unordered_next(table->record[0], eq_range)); } +/** + Create a copy of all keys used by multi_range_read() + + @retval 0 ok + @retval HA_ERR_END_OF_FILE no keys in range + @retval other value: error + + TODO to save memory: + - If (mrr_mode & HA_MRR_MATERIALIZED_KEYS) is set then the keys data is + stable and we don't have to copy the keys, only store a pointer to the + key. + - When allocating key data, store things in a MEM_ROOT buffer instead of + a malloc() per key. This will simplify and speed up the current code + and use less memory. +*/ + +int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq, + range_seq_t seq_it) +{ + uint i, length; + key_range *start_key, *end_key; + KEY_MULTI_RANGE *range; + DBUG_ENTER("ha_partition::multi_range_key_create_key"); + + bitmap_clear_all(&m_mrr_used_partitions); + m_mrr_range_length= 0; + bzero(m_part_mrr_range_length, + sizeof(*m_part_mrr_range_length) * m_tot_parts); + if (!m_mrr_range_first) + { + if (!(m_mrr_range_first= (PARTITION_KEY_MULTI_RANGE *) + my_multi_malloc(MYF(MY_WME), + &m_mrr_range_current, + sizeof(PARTITION_KEY_MULTI_RANGE), + NullS))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + m_mrr_range_first->id= 1; + m_mrr_range_first->key[0]= NULL; + m_mrr_range_first->key[1]= NULL; + m_mrr_range_first->next= NULL; + } + else + m_mrr_range_current= m_mrr_range_first; + + for (i= 0; i < m_tot_parts; i++) + { + if (!m_part_mrr_range_first[i]) + { + if (!(m_part_mrr_range_first[i]= (PARTITION_PART_KEY_MULTI_RANGE *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &m_part_mrr_range_current[i], + sizeof(PARTITION_PART_KEY_MULTI_RANGE), + NullS))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + else + { + m_part_mrr_range_current[i]= m_part_mrr_range_first[i]; + m_part_mrr_range_current[i]->partition_key_multi_range= NULL; + } + } + m_mrr_range_current->key_multi_range.start_key.key= NULL; + m_mrr_range_current->key_multi_range.end_key.key= NULL; + + while (!seq->next(seq_it, &m_mrr_range_current->key_multi_range)) + { + m_mrr_range_length++; + range= &m_mrr_range_current->key_multi_range; + + /* Copy start key */ + start_key= &range->start_key; + DBUG_PRINT("info",("partition range->range_flag: %u", range->range_flag)); + DBUG_PRINT("info",("partition start_key->key: %p", start_key->key)); + DBUG_PRINT("info",("partition start_key->length: %u", start_key->length)); + DBUG_PRINT("info",("partition start_key->keypart_map: %lu", + start_key->keypart_map)); + DBUG_PRINT("info",("partition start_key->flag: %u", start_key->flag)); + + if (start_key->key) + { + length= start_key->length; + if (!m_mrr_range_current->key[0] || + m_mrr_range_current->length[0] < length) + { + if (m_mrr_range_current->key[0]) + my_free(m_mrr_range_current->key[0]); + if (!(m_mrr_range_current->key[0]= + (uchar *) my_malloc(length, MYF(MY_WME)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + m_mrr_range_current->length[0]= length; + } + memcpy(m_mrr_range_current->key[0], start_key->key, length); + start_key->key= m_mrr_range_current->key[0]; + } + + /* Copy end key */ + end_key= &range->end_key; + DBUG_PRINT("info",("partition end_key->key: %p", end_key->key)); + DBUG_PRINT("info",("partition end_key->length: %u", end_key->length)); + DBUG_PRINT("info",("partition end_key->keypart_map: %lu", + end_key->keypart_map)); + DBUG_PRINT("info",("partition end_key->flag: %u", end_key->flag)); + if (end_key->key) + { + length= end_key->length; + if (!m_mrr_range_current->key[1] || + m_mrr_range_current->length[1] < length) + { + if (m_mrr_range_current->key[1]) + my_free(m_mrr_range_current->key[1]); + if (!(m_mrr_range_current->key[1]= + (uchar *) my_malloc(length, MYF(MY_WME)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + m_mrr_range_current->length[1]= length; + } + memcpy(m_mrr_range_current->key[1], end_key->key, length); + end_key->key= m_mrr_range_current->key[1]; + } + + m_mrr_range_current->ptr= m_mrr_range_current->key_multi_range.ptr; + m_mrr_range_current->key_multi_range.ptr= m_mrr_range_current; + + if (start_key->key && (start_key->flag & HA_READ_KEY_EXACT)) + get_partition_set(table, table->record[0], active_index, + start_key, &m_part_spec); + else + { + m_part_spec.start_part= 0; + m_part_spec.end_part= m_tot_parts - 1; + } + + /* Copy key to those partitions that needs it */ + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i)) + { + bitmap_set_bit(&m_mrr_used_partitions, i); + m_part_mrr_range_length[i]++; + m_part_mrr_range_current[i]->partition_key_multi_range= + m_mrr_range_current; + + if (!m_part_mrr_range_current[i]->next) + { + PARTITION_PART_KEY_MULTI_RANGE *tmp_part_mrr_range; + if (!(tmp_part_mrr_range= (PARTITION_PART_KEY_MULTI_RANGE *) + my_malloc(sizeof(PARTITION_PART_KEY_MULTI_RANGE), + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + m_part_mrr_range_current[i]->next= tmp_part_mrr_range; + m_part_mrr_range_current[i]= tmp_part_mrr_range; + } + else + { + m_part_mrr_range_current[i]= m_part_mrr_range_current[i]->next; + m_part_mrr_range_current[i]->partition_key_multi_range= NULL; + } + } + } + + if (!m_mrr_range_current->next) + { + /* Add end of range sentinel */ + PARTITION_KEY_MULTI_RANGE *tmp_mrr_range; + if (!(tmp_mrr_range= (PARTITION_KEY_MULTI_RANGE *) + my_malloc(sizeof(PARTITION_KEY_MULTI_RANGE), MYF(MY_WME)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + tmp_mrr_range->id= m_mrr_range_current->id + 1; + tmp_mrr_range->key[0]= NULL; + tmp_mrr_range->key[1]= NULL; + tmp_mrr_range->next= NULL; + m_mrr_range_current->next= tmp_mrr_range; + } + m_mrr_range_current= m_mrr_range_current->next; + } + + if (!m_mrr_range_length) + { + DBUG_PRINT("Warning",("No keys to use for mrr")); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + /* set start and end part */ + m_part_spec.start_part= bitmap_get_first_set(&m_mrr_used_partitions); + + for (i= m_tot_parts; i-- > 0;) + { + if (bitmap_is_set(&m_mrr_used_partitions, i)) + { + m_part_spec.end_part= i; + break; + } + } + for (i= 0; i < m_tot_parts; i++) + { + m_partition_part_key_multi_range_hld[i].partition= this; + m_partition_part_key_multi_range_hld[i].part_id= i; + m_partition_part_key_multi_range_hld[i].partition_part_key_multi_range= + m_part_mrr_range_first[i]; + } + DBUG_PRINT("return",("OK")); + DBUG_RETURN(0); +} + + +static void partition_multi_range_key_get_key_info(void *init_params, + uint *length, + key_part_map *map) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)init_params; + ha_partition *partition= hld->partition; + key_range *start_key= (&partition->m_mrr_range_first-> + key_multi_range.start_key); + DBUG_ENTER("partition_multi_range_key_get_key_info"); + *length= start_key->length; + *map= start_key->keypart_map; + DBUG_VOID_RETURN; +} + + +static range_seq_t partition_multi_range_key_init(void *init_params, + uint n_ranges, + uint flags) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)init_params; + ha_partition *partition= hld->partition; + uint i= hld->part_id; + DBUG_ENTER("partition_multi_range_key_init"); + partition->m_mrr_range_init_flags= flags; + hld->partition_part_key_multi_range= partition->m_part_mrr_range_first[i]; + DBUG_RETURN(init_params); +} + + +static bool partition_multi_range_key_next(range_seq_t seq, + KEY_MULTI_RANGE *range) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq; + PARTITION_KEY_MULTI_RANGE *partition_key_multi_range= + hld->partition_part_key_multi_range->partition_key_multi_range; + DBUG_ENTER("partition_multi_range_key_next"); + if (!partition_key_multi_range) + DBUG_RETURN(TRUE); + *range= partition_key_multi_range->key_multi_range; + hld->partition_part_key_multi_range= + hld->partition_part_key_multi_range->next; + DBUG_RETURN(FALSE); +} + + +static bool partition_multi_range_key_skip_record(range_seq_t seq, + range_id_t range_info, + uchar *rowid) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq; + DBUG_ENTER("partition_multi_range_key_skip_record"); + DBUG_RETURN(hld->partition->m_seq_if->skip_record(hld->partition->m_seq, + range_info, rowid)); +} + + +static bool partition_multi_range_key_skip_index_tuple(range_seq_t seq, + range_id_t range_info) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq; + DBUG_ENTER("partition_multi_range_key_skip_index_tuple"); + DBUG_RETURN(hld->partition->m_seq_if->skip_index_tuple(hld->partition->m_seq, + range_info)); +} + +ha_rows ha_partition::multi_range_read_info_const(uint keyno, + RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint *bufsz, + uint *mrr_mode, + Cost_estimate *cost) +{ + int error; + uint i; + handler **file; + ha_rows rows= 0; + uint ret_mrr_mode= 0; + range_seq_t seq_it; + part_id_range save_part_spec; + DBUG_ENTER("ha_partition::multi_range_read_info_const"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + m_mrr_new_full_buffer_size= 0; + save_part_spec= m_part_spec; + + seq_it= seq->init(seq_init_param, n_ranges, *mrr_mode); + if ((error= multi_range_key_create_key(seq, seq_it))) + { + if (error == HA_ERR_END_OF_FILE) // No keys in range + { + rows= 0; + goto calc_cost; + } + /* + This error means that we can't do multi_range_read for the moment + (probably running out of memory) and we need to fallback to + normal reads + */ + m_part_spec= save_part_spec; + DBUG_RETURN(HA_POS_ERROR); + } + m_part_seq_if.get_key_info= + seq->get_key_info ? partition_multi_range_key_get_key_info : NULL; + m_part_seq_if.init= partition_multi_range_key_init; + m_part_seq_if.next= partition_multi_range_key_next; + m_part_seq_if.skip_record= (seq->skip_record ? + partition_multi_range_key_skip_record : NULL); + m_part_seq_if.skip_index_tuple= (seq->skip_index_tuple ? + partition_multi_range_key_skip_index_tuple : + NULL); + file= m_file; + do + { + i= (uint)(file - m_file); + DBUG_PRINT("info",("partition part_id: %u", i)); + if (bitmap_is_set(&m_mrr_used_partitions, i)) + { + ha_rows tmp_rows; + uint tmp_mrr_mode; + m_mrr_buffer_size[i]= 0; + tmp_mrr_mode= *mrr_mode; + tmp_rows= (*file)-> + multi_range_read_info_const(keyno, &m_part_seq_if, + &m_partition_part_key_multi_range_hld[i], + m_part_mrr_range_length[i], + &m_mrr_buffer_size[i], + &tmp_mrr_mode, cost); + if (tmp_rows == HA_POS_ERROR) + { + m_part_spec= save_part_spec; + DBUG_RETURN(HA_POS_ERROR); + } + rows+= tmp_rows; + ret_mrr_mode|= tmp_mrr_mode; + m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i]; + } + } while (*(++file)); + *mrr_mode= ret_mrr_mode; + +calc_cost: + m_part_spec= save_part_spec; + cost->reset(); + cost->avg_io_cost= 1; + if ((*mrr_mode & HA_MRR_INDEX_ONLY) && rows > 2) + cost->io_count= keyread_time(keyno, n_ranges, (uint) rows); + else + cost->io_count= read_time(keyno, n_ranges, rows); + cost->cpu_cost= (double) rows / TIME_FOR_COMPARE + 0.01; + DBUG_RETURN(rows); +} + + +ha_rows ha_partition::multi_range_read_info(uint keyno, uint n_ranges, + uint keys, + uint key_parts, uint *bufsz, + uint *mrr_mode, + Cost_estimate *cost) +{ + uint i; + handler **file; + ha_rows rows; + DBUG_ENTER("ha_partition::multi_range_read_info"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + m_mrr_new_full_buffer_size= 0; + file= m_file; + do + { + i= (uint)(file - m_file); + if (bitmap_is_set(&(m_part_info->read_partitions), (i))) + { + m_mrr_buffer_size[i]= 0; + if ((rows= (*file)->multi_range_read_info(keyno, n_ranges, keys, + key_parts, + &m_mrr_buffer_size[i], + mrr_mode, cost))) + DBUG_RETURN(rows); + m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i]; + } + } while (*(++file)); + + cost->reset(); + cost->avg_io_cost= 1; + if (*mrr_mode & HA_MRR_INDEX_ONLY) + cost->io_count= keyread_time(keyno, n_ranges, (uint) rows); + else + cost->io_count= read_time(keyno, n_ranges, rows); + DBUG_RETURN(0); +} + + +int ha_partition::multi_range_read_init(RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint mrr_mode, + HANDLER_BUFFER *buf) +{ + int error; + uint i; + handler **file; + uchar *tmp_buffer; + DBUG_ENTER("ha_partition::multi_range_read_init"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + m_seq_if= seq; + m_seq= seq->init(seq_init_param, n_ranges, mrr_mode); + if ((error= multi_range_key_create_key(seq, m_seq))) + DBUG_RETURN(0); + + m_part_seq_if.get_key_info= (seq->get_key_info ? + partition_multi_range_key_get_key_info : + NULL); + m_part_seq_if.init= partition_multi_range_key_init; + m_part_seq_if.next= partition_multi_range_key_next; + m_part_seq_if.skip_record= (seq->skip_record ? + partition_multi_range_key_skip_record : + NULL); + m_part_seq_if.skip_index_tuple= (seq->skip_index_tuple ? + partition_multi_range_key_skip_index_tuple : + NULL); + + /* m_mrr_new_full_buffer_size was calculated in multi_range_read_info */ + if (m_mrr_full_buffer_size < m_mrr_new_full_buffer_size) + { + if (m_mrr_full_buffer) + my_free(m_mrr_full_buffer); + if (!(m_mrr_full_buffer= + (uchar *) my_malloc(m_mrr_new_full_buffer_size, MYF(MY_WME)))) + { + m_mrr_full_buffer_size= 0; + error= HA_ERR_OUT_OF_MEM; + goto error; + } + m_mrr_full_buffer_size= m_mrr_new_full_buffer_size; + } + + tmp_buffer= m_mrr_full_buffer; + file= m_file; + do + { + i= (uint)(file - m_file); + DBUG_PRINT("info",("partition part_id: %u", i)); + if (bitmap_is_set(&m_mrr_used_partitions, i)) + { + if (m_mrr_new_full_buffer_size) + { + if (m_mrr_buffer_size[i]) + { + m_mrr_buffer[i].buffer= tmp_buffer; + m_mrr_buffer[i].end_of_used_area= tmp_buffer; + tmp_buffer+= m_mrr_buffer_size[i]; + m_mrr_buffer[i].buffer_end= tmp_buffer; + } + } + else + m_mrr_buffer[i]= *buf; + + if ((error= (*file)-> + multi_range_read_init(&m_part_seq_if, + &m_partition_part_key_multi_range_hld[i], + m_part_mrr_range_length[i], + mrr_mode, + &m_mrr_buffer[i]))) + goto error; + m_stock_range_seq[i]= 0; + } + } while (*(++file)); + + m_multi_range_read_first= TRUE; + m_mrr_range_current= m_mrr_range_first; + m_index_scan_type= partition_read_multi_range; + m_mrr_mode= mrr_mode; + m_mrr_n_ranges= n_ranges; + DBUG_RETURN(0); + +error: + DBUG_RETURN(error); +} + + +int ha_partition::multi_range_read_next(range_id_t *range_info) +{ + int error; + DBUG_ENTER("ha_partition::multi_range_read_next"); + DBUG_PRINT("enter", ("partition this: %p partition m_mrr_mode: %u", + this, m_mrr_mode)); + + if ((m_mrr_mode & HA_MRR_SORTED)) + { + if (m_multi_range_read_first) + { + if ((error= handle_ordered_index_scan(table->record[0], FALSE))) + DBUG_RETURN(error); + if (!m_pre_calling) + m_multi_range_read_first= FALSE; + } + else if ((error= handle_ordered_next(table->record[0], eq_range))) + DBUG_RETURN(error); + *range_info= m_mrr_range_current->ptr; + } + else + { + if (m_multi_range_read_first) + { + if ((error= handle_unordered_scan_next_partition(table->record[0]))) + DBUG_RETURN(error); + if (!m_pre_calling) + m_multi_range_read_first= FALSE; + } + else if ((error= handle_unordered_next(table->record[0], FALSE))) + DBUG_RETURN(error); + + *range_info= + ((PARTITION_KEY_MULTI_RANGE *) m_range_info[m_last_part])->ptr; + } + DBUG_RETURN(0); +} + + +int ha_partition::multi_range_read_explain_info(uint mrr_mode, char *str, + size_t size) +{ + DBUG_ENTER("ha_partition::multi_range_read_explain_info"); + DBUG_RETURN(m_file[0]->multi_range_read_explain_info(mrr_mode, str, size)); +} + + +/** + Find and retrieve the Full Text Search relevance ranking for a search string + in a full text index. + + @param handler Full Text Search handler + @param record Search string + @param length Length of the search string + + @retval Relevance value +*/ + +float partition_ft_find_relevance(FT_INFO *handler, + uchar *record, uint length) +{ + st_partition_ft_info *info= (st_partition_ft_info *)handler; + uint m_last_part= ((ha_partition*) info->file)->last_part(); + FT_INFO *m_handler= info->part_ft_info[m_last_part]; + DBUG_ENTER("partition_ft_find_relevance"); + if (!m_handler) + DBUG_RETURN((float)-1.0); + DBUG_RETURN(m_handler->please->find_relevance(m_handler, record, length)); +} + + +/** + Retrieve the Full Text Search relevance ranking for the current + full text search. + + @param handler Full Text Search handler + + @retval Relevance value +*/ + +float partition_ft_get_relevance(FT_INFO *handler) +{ + st_partition_ft_info *info= (st_partition_ft_info *)handler; + uint m_last_part= ((ha_partition*) info->file)->last_part(); + FT_INFO *m_handler= info->part_ft_info[m_last_part]; + DBUG_ENTER("partition_ft_get_relevance"); + if (!m_handler) + DBUG_RETURN((float)-1.0); + DBUG_RETURN(m_handler->please->get_relevance(m_handler)); +} + + +/** + Free the memory for a full text search handler. + + @param handler Full Text Search handler +*/ + +void partition_ft_close_search(FT_INFO *handler) +{ + st_partition_ft_info *info= (st_partition_ft_info *)handler; + info->file->ft_close_search(handler); +} + + +/** + Free the memory for a full text search handler. + + @param handler Full Text Search handler +*/ + +void ha_partition::ft_close_search(FT_INFO *handler) +{ + uint i; + st_partition_ft_info *info= (st_partition_ft_info *)handler; + DBUG_ENTER("ha_partition::ft_close_search"); + + for (i= 0; i < m_tot_parts; i++) + { + FT_INFO *m_handler= info->part_ft_info[i]; + DBUG_ASSERT(!m_handler || + (m_handler->please && m_handler->please->close_search)); + if (m_handler && + m_handler->please && + m_handler->please->close_search) + m_handler->please->close_search(m_handler); + } + DBUG_VOID_RETURN; +} + + +/* Partition Full Text search function table */ +_ft_vft partition_ft_vft = +{ + NULL, // partition_ft_read_next + partition_ft_find_relevance, + partition_ft_close_search, + partition_ft_get_relevance, + NULL // partition_ft_reinit_search +}; + + +/** + Initialize a full text search. +*/ + +int ha_partition::ft_init() +{ + int error; + uint i= 0; + uint32 part_id; + DBUG_ENTER("ha_partition::ft_init"); + DBUG_PRINT("info", ("partition this: %p", this)); + + /* + For operations that may need to change data, we may need to extend + read_set. + */ + if (get_lock_type() == F_WRLCK) + { + /* + If write_set contains any of the fields used in partition and + subpartition expression, we need to set all bits in read_set because + the row may need to be inserted in a different [sub]partition. In + other words update_row() can be converted into write_row(), which + requires a complete record. + */ + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + table->write_set)) + bitmap_set_all(table->read_set); + else + { + /* + Some handlers only read fields as specified by the bitmap for the + read set. For partitioned handlers we always require that the + fields of the partition functions are read such that we can + calculate the partition id to place updated and deleted records. + */ + bitmap_union(table->read_set, &m_part_info->full_part_field_set); + } + } + + /* Now we see what the index of our first important partition is */ + DBUG_PRINT("info", ("m_part_info->read_partitions: %p", + (void *) m_part_info->read_partitions.bitmap)); + part_id= bitmap_get_first_set(&(m_part_info->read_partitions)); + DBUG_PRINT("info", ("m_part_spec.start_part %u", (uint) part_id)); + + if (part_id == MY_BIT_NONE) + { + error= 0; + goto err1; + } + + DBUG_PRINT("info", ("ft_init on partition %u", (uint) part_id)); + /* + ft_end() is needed for partitioning to reset internal data if scan + is already in use + */ + if (m_pre_calling) + { + if ((error= pre_ft_end())) + goto err1; + } + else + ft_end(); + m_index_scan_type= partition_ft_read; + for (i= part_id; i < m_tot_parts; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i)) + { + error= m_pre_calling ? m_file[i]->pre_ft_init() : m_file[i]->ft_init(); + if (error) + goto err2; + } + } + m_scan_value= 1; + m_part_spec.start_part= part_id; + m_part_spec.end_part= m_tot_parts - 1; + m_ft_init_and_first= TRUE; + DBUG_PRINT("info", ("m_scan_value: %u", m_scan_value)); + DBUG_RETURN(0); + +err2: + late_extra_no_cache(part_id); + while ((int)--i >= (int)part_id) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i)) + { + if (m_pre_calling) + m_file[i]->pre_ft_end(); + else + m_file[i]->ft_end(); + } + } +err1: + m_scan_value= 2; + m_part_spec.start_part= NO_CURRENT_PART_ID; + DBUG_RETURN(error); +} + + +/** + Initialize a full text search during a bulk access request. +*/ + +int ha_partition::pre_ft_init() +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_ft_init"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= ft_init(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Terminate a full text search. +*/ + +void ha_partition::ft_end() +{ + handler **file; + DBUG_ENTER("ha_partition::ft_end"); + DBUG_PRINT("info", ("partition this: %p", this)); + + switch (m_scan_value) { + case 2: // Error + break; + case 1: // Table scan + if (NO_CURRENT_PART_ID != m_part_spec.start_part) + late_extra_no_cache(m_part_spec.start_part); + file= m_file; + do + { + if (bitmap_is_set(&(m_part_info->read_partitions), (uint)(file - m_file))) + { + if (m_pre_calling) + (*file)->pre_ft_end(); + else + (*file)->ft_end(); + } + } while (*(++file)); + break; + } + m_scan_value= 2; + m_part_spec.start_part= NO_CURRENT_PART_ID; + ft_current= 0; + DBUG_VOID_RETURN; +} + + +/** + Terminate a full text search during a bulk access request. +*/ + +int ha_partition::pre_ft_end() +{ + bool save_m_pre_calling; + DBUG_ENTER("ha_partition::pre_ft_end"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + ft_end(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(0); +} + + +/** + Initialize a full text search using the extended API. + + @param flags Search flags + @param inx Key number + @param key Key value + + @return FT_INFO structure if successful + NULL otherwise +*/ + +FT_INFO *ha_partition::ft_init_ext(uint flags, uint inx, String *key) +{ + FT_INFO *ft_handler; + handler **file; + st_partition_ft_info *ft_target, **parent; + DBUG_ENTER("ha_partition::ft_init_ext"); + + if (ft_current) + parent= &ft_current->next; + else + parent= &ft_first; + + if (!(ft_target= *parent)) + { + FT_INFO **tmp_ft_info; + if (!(ft_target= (st_partition_ft_info *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &ft_target, + sizeof(st_partition_ft_info), + &tmp_ft_info, + sizeof(FT_INFO *) * m_tot_parts, + NullS))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + DBUG_RETURN(NULL); + } + ft_target->part_ft_info= tmp_ft_info; + (*parent)= ft_target; + } + + ft_current= ft_target; + file= m_file; + do + { + if (bitmap_is_set(&(m_part_info->read_partitions), (uint)(file - m_file))) + { + if ((ft_handler= (*file)->ft_init_ext(flags, inx, key))) + (*file)->ft_handler= ft_handler; + else + (*file)->ft_handler= NULL; + ft_target->part_ft_info[file - m_file]= ft_handler; + } + else + { + (*file)->ft_handler= NULL; + ft_target->part_ft_info[file - m_file]= NULL; + } + } while (*(++file)); + + ft_target->please= &partition_ft_vft; + ft_target->file= this; + DBUG_RETURN((FT_INFO*)ft_target); +} + + +/** + Return the next record from the FT result set during an ordered index + pre-scan + + @param use_parallel Is it a parallel search + + @return >0 Error code + 0 Success +*/ + +int ha_partition::pre_ft_read(bool use_parallel) +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_ft_read"); + DBUG_PRINT("info", ("partition this: %p", this)); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + m_pre_call_use_parallel= use_parallel; + error= ft_read(table->record[0]); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Return the first or next record in a full text search. + + @param buf Buffer where the record should be returned + + @return >0 Error code + 0 Success +*/ + +int ha_partition::ft_read(uchar *buf) +{ + handler *file; + int result= HA_ERR_END_OF_FILE, error; + uint part_id= m_part_spec.start_part; + DBUG_ENTER("ha_partition::ft_read"); + DBUG_PRINT("info", ("partition this: %p", this)); + DBUG_PRINT("info", ("part_id: %u", part_id)); + + if (part_id == NO_CURRENT_PART_ID) + { + /* + The original set of partitions to scan was empty and thus we report + the result here. + */ + DBUG_PRINT("info", ("NO_CURRENT_PART_ID")); + goto end; + } + + DBUG_ASSERT(m_scan_value == 1); + + if (m_ft_init_and_first) // First call to ft_read() + { + m_ft_init_and_first= FALSE; + if (!bulk_access_executing) + { + error= handle_pre_scan(FALSE, check_parallel_search()); + if (m_pre_calling || error) + DBUG_RETURN(error); + } + late_extra_cache(part_id); + } + + file= m_file[part_id]; + + while (TRUE) + { + if (!(result= file->ft_read(buf))) + { + /* Found row: remember position and return it. */ + m_part_spec.start_part= m_last_part= part_id; + table->status= 0; + DBUG_RETURN(0); + } + + /* + if we get here, then the current partition ft_next returned failure + */ + if (result == HA_ERR_RECORD_DELETED) + continue; // Probably MyISAM + + if (result != HA_ERR_END_OF_FILE) + goto end_dont_reset_start_part; // Return error + + /* End current partition */ + late_extra_no_cache(part_id); + DBUG_PRINT("info", ("stopping using partition %u", (uint) part_id)); + + /* Shift to next partition */ + while (++part_id < m_tot_parts && + !bitmap_is_set(&(m_part_info->read_partitions), part_id)) + ; + if (part_id >= m_tot_parts) + { + result= HA_ERR_END_OF_FILE; + break; + } + m_part_spec.start_part= m_last_part= part_id; + file= m_file[part_id]; + DBUG_PRINT("info", ("now using partition %u", (uint) part_id)); + late_extra_cache(part_id); + } + +end: + m_part_spec.start_part= NO_CURRENT_PART_ID; +end_dont_reset_start_part: + table->status= STATUS_NOT_FOUND; + DBUG_RETURN(result); +} + /* Common routine to set up index scans @@ -5842,7 +6907,7 @@ int ha_partition::read_range_next() needs it to calculcate partitioning function values) - idx_read_flag TRUE <=> m_start_key has range start endpoint which + idx_read_flag TRUE <=> m_start_key has range start endpoint which probably can be used to determine the set of partitions to scan. FALSE <=> there is no start endpoint. @@ -5864,7 +6929,7 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) DBUG_ENTER("ha_partition::partition_scan_set_up"); if (idx_read_flag) - get_partition_set(table,buf,active_index,&m_start_key,&m_part_spec); + get_partition_set(table, buf, active_index, &m_start_key, &m_part_spec); else { m_part_spec.start_part= 0; @@ -5885,8 +6950,8 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) We discovered a single partition to scan, this never needs to be performed using the ordered index scan. */ - DBUG_PRINT("info", ("index scan using the single partition %d", - m_part_spec.start_part)); + DBUG_PRINT("info", ("index scan using the single partition %u", + (uint) m_part_spec.start_part)); m_ordered_scan_ongoing= FALSE; } else @@ -5913,6 +6978,206 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) DBUG_RETURN(0); } +/** + Check if we can search partitions in parallel + + @retval TRUE yes + @retval FALSE no +*/ + +bool ha_partition::check_parallel_search() +{ + TABLE_LIST *table_list= table->pos_in_table_list; + st_select_lex *select_lex; + JOIN *join; + DBUG_ENTER("ha_partition::check_parallel_search"); + if (!table_list) + goto not_parallel; + + while (table_list->parent_l) + table_list= table_list->parent_l; + + select_lex= table_list->select_lex; + DBUG_PRINT("info",("partition select_lex: %p", select_lex)); + if (!select_lex) + goto not_parallel; + if (!select_lex->explicit_limit) + { + DBUG_PRINT("info",("partition not using explicit_limit")); + goto parallel; + } + + join= select_lex->join; + DBUG_PRINT("info",("partition join: %p", join)); + if (join && join->skip_sort_order) + { + DBUG_PRINT("info",("partition order_list.elements: %u", + select_lex->order_list.elements)); + if (select_lex->order_list.elements) + { + Item *item= *select_lex->order_list.first->item; + DBUG_PRINT("info",("partition item: %p", item)); + DBUG_PRINT("info",("partition item->type(): %u", item->type())); + DBUG_PRINT("info",("partition m_part_info->part_type: %u", + m_part_info->part_type)); + DBUG_PRINT("info",("partition m_is_sub_partitioned: %s", + m_is_sub_partitioned ? "TRUE" : "FALSE")); + DBUG_PRINT("info",("partition m_part_info->part_expr: %p", + m_part_info->part_expr)); + if (item->type() == Item::FIELD_ITEM && + m_part_info->part_type == RANGE_PARTITION && + !m_is_sub_partitioned && + (!m_part_info->part_expr || + m_part_info->part_expr->type() == Item::FIELD_ITEM)) + { + Field *order_field= ((Item_field *)item)->field; + DBUG_PRINT("info",("partition order_field: %p", order_field)); + if (order_field && order_field->table == table_list->table) + { + Field *part_field= m_part_info->full_part_field_array[0]; + if (set_top_table_fields) + order_field= top_table_field[order_field->field_index]; + DBUG_PRINT("info",("partition order_field: %p", order_field)); + DBUG_PRINT("info",("partition part_field: %p", part_field)); + if (part_field == order_field) + { + /* + We are using ORDER BY partition_field LIMIT # + In this case, let's not do things in parallel as it's + likely that the query can be satisfied from the first + partition + */ + DBUG_PRINT("info",("partition with ORDER on partition field")); + goto not_parallel; + } + } + } + DBUG_PRINT("info",("partition have order")); + goto parallel; + } + + DBUG_PRINT("info",("partition group_list.elements: %u", + select_lex->group_list.elements)); + if (select_lex->group_list.elements) + { + Item *item= *select_lex->group_list.first->item; + DBUG_PRINT("info",("partition item: %p", item)); + DBUG_PRINT("info",("partition item->type(): %u", item->type())); + DBUG_PRINT("info",("partition m_part_info->part_type: %u", + m_part_info->part_type)); + DBUG_PRINT("info",("partition m_is_sub_partitioned: %s", + m_is_sub_partitioned ? "TRUE" : "FALSE")); + DBUG_PRINT("info",("partition m_part_info->part_expr: %p", + m_part_info->part_expr)); + if (item->type() == Item::FIELD_ITEM && + m_part_info->part_type == RANGE_PARTITION && + !m_is_sub_partitioned && + (!m_part_info->part_expr || + m_part_info->part_expr->type() == Item::FIELD_ITEM)) + { + Field *group_field= ((Item_field *)item)->field; + DBUG_PRINT("info",("partition group_field: %p", group_field)); + if (group_field && group_field->table == table_list->table) + { + Field *part_field= m_part_info->full_part_field_array[0]; + if (set_top_table_fields) + group_field= top_table_field[group_field->field_index]; + DBUG_PRINT("info",("partition group_field: %p", group_field)); + DBUG_PRINT("info",("partition part_field: %p", part_field)); + if (part_field == group_field) + { + DBUG_PRINT("info",("partition with GROUP BY on partition field")); + goto not_parallel; + } + } + } + DBUG_PRINT("info",("partition with GROUP BY")); + goto parallel; + } + } + else if (select_lex->order_list.elements || + select_lex->group_list.elements) + { + DBUG_PRINT("info",("partition is not skip_order")); + DBUG_PRINT("info",("partition order_list.elements: %u", + select_lex->order_list.elements)); + DBUG_PRINT("info",("partition group_list.elements: %u", + select_lex->group_list.elements)); + goto parallel; + } + DBUG_PRINT("info",("partition is not skip_order")); + +not_parallel: + DBUG_PRINT("return",("partition FALSE")); + DBUG_RETURN(FALSE); + +parallel: + DBUG_PRINT("return",("partition TRUE")); + DBUG_RETURN(TRUE); +} + + +int ha_partition::handle_pre_scan(bool reverse_order, bool use_parallel) +{ + uint i; + DBUG_ENTER("ha_partition::handle_pre_scan"); + DBUG_PRINT("enter", + ("m_part_spec.start_part: %u m_part_spec.end_part: %u", + (uint) m_part_spec.start_part, (uint) m_part_spec.end_part)); + + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (!(bitmap_is_set(&(m_part_info->read_partitions), i))) + continue; + int error; + handler *file= m_file[i]; + + switch (m_index_scan_type) { + case partition_index_read: + error= file->pre_index_read_map(m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag, + use_parallel); + break; + case partition_index_first: + error= file->pre_index_first(use_parallel); + break; + case partition_index_last: + error= file->pre_index_last(use_parallel); + break; + case partition_index_read_last: + error= file->pre_index_read_last_map(m_start_key.key, + m_start_key.keypart_map, + use_parallel); + break; + case partition_read_range: + error= file->pre_read_range_first(m_start_key.key? &m_start_key: NULL, + end_range, eq_range, TRUE, use_parallel); + break; + case partition_read_multi_range: + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + error= file->pre_multi_range_read_next(use_parallel); + break; + case partition_ft_read: + error= file->pre_ft_read(use_parallel); + break; + case partition_no_index_scan: + error= file->pre_rnd_next(use_parallel); + break; + default: + DBUG_ASSERT(FALSE); + DBUG_RETURN(0); + } + if (error == HA_ERR_END_OF_FILE) + error= 0; + if (error) + DBUG_RETURN(error); + } + table->status= 0; + DBUG_RETURN(0); +} + /**************************************************************************** Unordered Index Scan Routines @@ -5959,7 +7224,16 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) partition_read_range is_next_same are always local constants */ - if (m_index_scan_type == partition_read_range) + if (m_index_scan_type == partition_read_multi_range) + { + if (!(error= file-> + multi_range_read_next(&m_range_info[m_part_spec.start_part]))) + { + m_last_part= m_part_spec.start_part; + DBUG_RETURN(0); + } + } + else if (m_index_scan_type == partition_read_range) { if (!(error= file->read_range_next())) { @@ -5976,7 +7250,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) DBUG_RETURN(0); } } - else + else { if (!(error= file->ha_index_next(buf))) { @@ -5999,7 +7273,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) SYNOPSIS handle_unordered_scan_next_partition() - buf Read row in MySQL Row Format + buf Read row in MariaDB Row Format RETURN VALUE HA_ERR_END_OF_FILE End of scan @@ -6017,6 +7291,7 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int saved_error= HA_ERR_END_OF_FILE; DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition"); + /* Read next partition that includes start_part */ if (i) i= bitmap_get_next_set(&m_part_info->read_partitions, i - 1); else @@ -6029,34 +7304,29 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int error; handler *file= m_file[i]; m_part_spec.start_part= i; + switch (m_index_scan_type) { + case partition_read_multi_range: + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + DBUG_PRINT("info", ("read_multi_range on partition %u", i)); + error= file->multi_range_read_next(&m_range_info[i]); + break; case partition_read_range: - DBUG_PRINT("info", ("read_range_first on partition %d", i)); + DBUG_PRINT("info", ("read_range_first on partition %u", i)); error= file->read_range_first(m_start_key.key? &m_start_key: NULL, end_range, eq_range, FALSE); break; case partition_index_read: - DBUG_PRINT("info", ("index_read on partition %d", i)); + DBUG_PRINT("info", ("index_read on partition %u", i)); error= file->ha_index_read_map(buf, m_start_key.key, m_start_key.keypart_map, m_start_key.flag); break; case partition_index_first: - DBUG_PRINT("info", ("index_first on partition %d", i)); + DBUG_PRINT("info", ("index_first on partition %u", i)); error= file->ha_index_first(buf); break; - case partition_index_first_unordered: - /* - We perform a scan without sorting and this means that we - should not use the index_first since not all handlers - support it and it is also unnecessary to restrict sort - order. - */ - DBUG_PRINT("info", ("read_range_first on partition %d", i)); - table->record[0]= buf; - error= file->read_range_first(0, end_range, eq_range, 0); - table->record[0]= m_rec0; - break; default: DBUG_ASSERT(FALSE); DBUG_RETURN(1); @@ -6070,12 +7340,12 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) DBUG_RETURN(error); /* - If HA_ERR_KEY_NOT_FOUND, we must return that error instead of + If HA_ERR_KEY_NOT_FOUND, we must return that error instead of HA_ERR_END_OF_FILE, to be able to continue search. */ if (saved_error != HA_ERR_KEY_NOT_FOUND) saved_error= error; - DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %d", i)); + DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %u", i)); } if (saved_error == HA_ERR_END_OF_FILE) m_part_spec.start_part= NO_CURRENT_PART_ID; @@ -6086,7 +7356,7 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) /** Common routine to start index scan with ordered results. - @param[out] buf Read row in MySQL Row Format + @param[out] buf Read row in MariaDB Row Format @return Operation status @retval HA_ERR_END_OF_FILE End of scan @@ -6112,19 +7382,31 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) { + int error; uint i; uint j= queue_first_element(&m_queue); + uint smallest_range_seq= 0; bool found= FALSE; uchar *part_rec_buf_ptr= m_ordered_rec_buffer; int saved_error= HA_ERR_END_OF_FILE; DBUG_ENTER("ha_partition::handle_ordered_index_scan"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + if (m_pre_calling) + error= handle_pre_scan(reverse_order, m_pre_call_use_parallel); + else + error= handle_pre_scan(reverse_order, check_parallel_search()); + if (error) + DBUG_RETURN(error); if (m_key_not_found) { + /* m_key_not_found was set in the previous call to this function */ m_key_not_found= false; bitmap_clear_all(&m_key_not_found_partitions); } m_top_entry= NO_CURRENT_PART_ID; + DBUG_PRINT("info", ("partition queue_remove_all(1)")); queue_remove_all(&m_queue); DBUG_ASSERT(bitmap_is_set(&m_part_info->read_partitions, m_part_spec.start_part)); @@ -6144,14 +7426,14 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u", m_part_spec.start_part, i)); for (/* continue from above */ ; - i <= m_part_spec.end_part; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + i <= m_part_spec.end_part ; + i= bitmap_get_next_set(&m_part_info->read_partitions, i), + part_rec_buf_ptr+= m_priority_queue_rec_len) { DBUG_PRINT("info", ("reading from part %u (scan_type: %u)", i, m_index_scan_type)); DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); uchar *rec_buf_ptr= part_rec_buf_ptr + PARTITION_BYTES_IN_POS; - int error; handler *file= m_file[i]; switch (m_index_scan_type) { @@ -6160,6 +7442,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) m_start_key.key, m_start_key.keypart_map, m_start_key.flag); + /* Caller has specified reverse_order */ break; case partition_index_first: error= file->ha_index_first(rec_buf_ptr); @@ -6171,16 +7454,49 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) break; case partition_read_range: { - /* + /* This can only read record to table->record[0], as it was set when the table was being opened. We have to memcpy data ourselves. */ error= file->read_range_first(m_start_key.key? &m_start_key: NULL, end_range, eq_range, TRUE); - memcpy(rec_buf_ptr, table->record[0], m_rec_length); + if (!error) + memcpy(rec_buf_ptr, table->record[0], m_rec_length); reverse_order= FALSE; break; } + case partition_read_multi_range: + { + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + DBUG_PRINT("info", ("partition %u", i)); + error= file->multi_range_read_next(&m_range_info[i]); + DBUG_PRINT("info", ("error: %d", error)); + if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) + { + bitmap_clear_bit(&m_mrr_used_partitions, i); + continue; + } + if (!error) + { + memcpy(rec_buf_ptr, table->record[0], m_rec_length); + reverse_order= FALSE; + m_stock_range_seq[i]= (((PARTITION_KEY_MULTI_RANGE *) + m_range_info[i])->id); + /* Test if the key is in the first key range */ + if (m_stock_range_seq[i] != m_mrr_range_current->id) + { + /* + smallest_range_seq contains the smallest key range we have seen + so far + */ + if (!smallest_range_seq || smallest_range_seq > m_stock_range_seq[i]) + smallest_range_seq= m_stock_range_seq[i]; + continue; + } + } + break; + } default: DBUG_ASSERT(FALSE); DBUG_RETURN(HA_ERR_END_OF_FILE); @@ -6198,10 +7514,6 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) */ queue_element(&m_queue, j++)= part_rec_buf_ptr; } - else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) - { - DBUG_RETURN(error); - } else if (error == HA_ERR_KEY_NOT_FOUND) { DBUG_PRINT("info", ("HA_ERR_KEY_NOT_FOUND from partition %u", i)); @@ -6209,7 +7521,53 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) m_key_not_found= true; saved_error= error; } - part_rec_buf_ptr+= m_priority_queue_rec_len; + else if (error != HA_ERR_END_OF_FILE) + { + DBUG_RETURN(error); + } + } + + if (!found && smallest_range_seq) + { + /* We know that there is an existing row based on code above */ + found= TRUE; + part_rec_buf_ptr= m_ordered_rec_buffer; + + /* + No key found in the first key range + Collect all partitions that has a key in smallest_range_seq + */ + DBUG_PRINT("info", ("partition !found && smallest_range_seq")); + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i <= m_part_spec.end_part; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + { + DBUG_PRINT("info", ("partition current_part: %u", i)); + if (i < m_part_spec.start_part) + { + part_rec_buf_ptr+= m_priority_queue_rec_len; + DBUG_PRINT("info", ("partition i < m_part_spec.start_part")); + continue; + } + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + { + part_rec_buf_ptr+= m_priority_queue_rec_len; + DBUG_PRINT("info", ("partition !bitmap_is_set(&m_mrr_used_partitions, i)")); + continue; + } + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); + if (smallest_range_seq == m_stock_range_seq[i]) + { + m_stock_range_seq[i]= 0; + queue_element(&m_queue, j++)= (uchar *) part_rec_buf_ptr; + DBUG_PRINT("info", ("partition smallest_range_seq == m_stock_range_seq[i]")); + } + part_rec_buf_ptr+= m_priority_queue_rec_len; + } + + /* Update global m_mrr_range_current to the current range */ + while (m_mrr_range_current->id < smallest_range_seq) + m_mrr_range_current= m_mrr_range_current->next; } if (found) { @@ -6218,12 +7576,11 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) after that read the first entry and copy it to the buffer to return in. */ queue_set_max_at_top(&m_queue, reverse_order); - queue_set_cmp_arg(&m_queue, m_using_extended_keys? m_curr_key_info : (void*)this); + queue_set_cmp_arg(&m_queue, (void*) this); m_queue.elements= j - queue_first_element(&m_queue); queue_fix(&m_queue); return_top_record(buf); - table->status= 0; - DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); + DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry)); DBUG_RETURN(0); } DBUG_RETURN(saved_error); @@ -6246,11 +7603,28 @@ void ha_partition::return_top_record(uchar *buf) uint part_id; uchar *key_buffer= queue_top(&m_queue); uchar *rec_buffer= key_buffer + PARTITION_BYTES_IN_POS; + DBUG_ENTER("ha_partition::return_top_record"); + DBUG_PRINT("enter", ("partition this: %p", this)); part_id= uint2korr(key_buffer); memcpy(buf, rec_buffer, m_rec_length); m_last_part= part_id; + DBUG_PRINT("info", ("partition m_last_part: %u", m_last_part)); m_top_entry= part_id; + table->status= 0; // Found an existing row + m_file[part_id]->return_record_by_parent(); + DBUG_VOID_RETURN; +} + +/* + This function is only used if the partitioned table has own partitions. + This can happen if the partitioned VP engine is used (part of spider). +*/ + +void ha_partition::return_record_by_parent() +{ + m_file[m_last_part]->return_record_by_parent(); + DBUG_ASSERT(0); } @@ -6269,6 +7643,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found() uchar *part_buf= m_ordered_rec_buffer; uchar *curr_rec_buf= NULL; DBUG_ENTER("ha_partition::handle_ordered_index_scan_key_not_found"); + DBUG_PRINT("enter", ("partition this: %p", this)); DBUG_ASSERT(m_key_not_found); /* Loop over all used partitions to get the correct offset @@ -6289,7 +7664,10 @@ int ha_partition::handle_ordered_index_scan_key_not_found() /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */ DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND); if (!error) + { + DBUG_PRINT("info", ("partition queue_insert(1)")); queue_insert(&m_queue, part_buf); + } else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND) DBUG_RETURN(error); } @@ -6326,11 +7704,15 @@ int ha_partition::handle_ordered_index_scan_key_not_found() int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) { int error; + DBUG_ENTER("ha_partition::handle_ordered_next"); + + if (m_top_entry == NO_CURRENT_PART_ID) + DBUG_RETURN(HA_ERR_END_OF_FILE); + uint part_id= m_top_entry; uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS; handler *file; - DBUG_ENTER("ha_partition::handle_ordered_next"); - + if (m_key_not_found) { if (is_next_same) @@ -6372,6 +7754,122 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) error= file->read_range_next(); memcpy(rec_buf, table->record[0], m_rec_length); } + else if (m_index_scan_type == partition_read_multi_range) + { + DBUG_PRINT("info", ("partition_read_multi_range route")); + DBUG_PRINT("info", ("part_id: %u", part_id)); + bool get_next= FALSE; + error= file->multi_range_read_next(&m_range_info[part_id]); + DBUG_PRINT("info", ("error: %d", error)); + if (error == HA_ERR_KEY_NOT_FOUND) + error= HA_ERR_END_OF_FILE; + if (error == HA_ERR_END_OF_FILE) + { + bitmap_clear_bit(&m_mrr_used_partitions, part_id); + DBUG_PRINT("info", ("partition m_queue.elements: %u", m_queue.elements)); + if (m_queue.elements) + { + DBUG_PRINT("info", ("partition queue_remove_top(1)")); + queue_remove_top(&m_queue); + if (m_queue.elements) + { + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %u (3)", + m_top_entry)); + DBUG_RETURN(0); + } + } + get_next= TRUE; + } + else if (!error) + { + DBUG_PRINT("info", ("m_range_info[%u])->id: %u", part_id, + ((PARTITION_KEY_MULTI_RANGE *) + m_range_info[part_id])->id)); + DBUG_PRINT("info", ("m_mrr_range_current->id: %u", + m_mrr_range_current->id)); + memcpy(rec_buf, table->record[0], m_rec_length); + if (((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id != + m_mrr_range_current->id) + { + m_stock_range_seq[part_id]= + ((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id; + DBUG_PRINT("info", ("partition queue_remove_top(2)")); + queue_remove_top(&m_queue); + if (!m_queue.elements) + get_next= TRUE; + } + } + if (get_next) + { + DBUG_PRINT("info", ("get_next route")); + uint i, j= 0, smallest_range_seq= UINT_MAX32; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (!(bitmap_is_set(&(m_part_info->read_partitions), i))) + continue; + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + if (smallest_range_seq > m_stock_range_seq[i]) + smallest_range_seq= m_stock_range_seq[i]; + } + + DBUG_PRINT("info", ("smallest_range_seq: %u", smallest_range_seq)); + if (smallest_range_seq != UINT_MAX32) + { + uchar *part_rec_buf_ptr= m_ordered_rec_buffer; + DBUG_PRINT("info", ("partition queue_remove_all(2)")); + queue_remove_all(&m_queue); + DBUG_PRINT("info", ("m_part_spec.start_part: %u", + m_part_spec.start_part)); + + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i <= m_part_spec.end_part; + i= bitmap_get_next_set(&m_part_info->read_partitions, i), + part_rec_buf_ptr+= m_priority_queue_rec_len) + { + DBUG_PRINT("info",("partition part_id: %u", i)); + if (i < m_part_spec.start_part) + { + DBUG_PRINT("info",("partition i < m_part_spec.start_part")); + continue; + } + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + { + DBUG_PRINT("info",("partition !bitmap_is_set(&m_mrr_used_partitions, i)")); + continue; + } + DBUG_PRINT("info",("partition uint2korr: %u", + uint2korr(part_rec_buf_ptr))); + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); + DBUG_PRINT("info", ("partition m_stock_range_seq[%u]: %u", + i, m_stock_range_seq[i])); + if (smallest_range_seq == m_stock_range_seq[i]) + { + m_stock_range_seq[i]= 0; + DBUG_PRINT("info", ("partition queue_insert(2)")); + queue_insert(&m_queue, part_rec_buf_ptr); + j++; + } + } + while (m_mrr_range_current->id < smallest_range_seq) + m_mrr_range_current= m_mrr_range_current->next; + + DBUG_PRINT("info",("partition m_mrr_range_current: %p", + m_mrr_range_current)); + DBUG_PRINT("info",("partition m_mrr_range_current->id: %u", + m_mrr_range_current ? m_mrr_range_current->id : 0)); + queue_set_max_at_top(&m_queue, FALSE); + queue_set_cmp_arg(&m_queue, (void*) this); + m_queue.elements= j; + queue_fix(&m_queue); + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %u (4)", + m_top_entry)); + DBUG_RETURN(0); + } + } + } else if (!is_next_same) error= file->ha_index_next(rec_buf); else @@ -6380,16 +7878,16 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) if (error) { - if (error == HA_ERR_END_OF_FILE) + if (error == HA_ERR_END_OF_FILE && m_queue.elements) { /* Return next buffered row */ + DBUG_PRINT("info", ("partition queue_remove_top(3)")); queue_remove_top(&m_queue); if (m_queue.elements) { + return_top_record(buf); DBUG_PRINT("info", ("Record returned from partition %u (2)", m_top_entry)); - return_top_record(buf); - table->status= 0; error= 0; } } @@ -6425,30 +7923,35 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) int ha_partition::handle_ordered_prev(uchar *buf) { int error; + DBUG_ENTER("ha_partition::handle_ordered_prev"); + DBUG_PRINT("enter", ("partition: %p", this)); + + if (m_top_entry == NO_CURRENT_PART_ID) + DBUG_RETURN(HA_ERR_END_OF_FILE); + uint part_id= m_top_entry; uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS; handler *file= m_file[part_id]; - DBUG_ENTER("ha_partition::handle_ordered_prev"); if ((error= file->ha_index_prev(rec_buf))) { - if (error == HA_ERR_END_OF_FILE) + if (error == HA_ERR_END_OF_FILE && m_queue.elements) { + DBUG_PRINT("info", ("partition queue_remove_top(4)")); queue_remove_top(&m_queue); if (m_queue.elements) { return_top_record(buf); - DBUG_PRINT("info", ("Record returned from partition %d (2)", + DBUG_PRINT("info", ("Record returned from partition %u (2)", m_top_entry)); error= 0; - table->status= 0; } } DBUG_RETURN(error); } queue_replace_top(&m_queue); return_top_record(buf); - DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); + DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry)); DBUG_RETURN(0); } @@ -6600,7 +8103,8 @@ int ha_partition::info(uint flag) { set_if_bigger(part_share->next_auto_inc_val, auto_increment_value); - part_share->auto_inc_initialized= true; + if (can_use_for_auto_inc_init()) + part_share->auto_inc_initialized= true; DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu", (ulong) part_share->next_auto_inc_val)); } @@ -7109,7 +8613,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, HA_EXTRA_DELETE_CANNOT_BATCH: HA_EXTRA_UPDATE_CANNOT_BATCH: Inform handler that delete_row()/update_row() cannot batch deletes/updates - and should perform them immediately. This may be needed when table has + and should perform them immediately. This may be needed when table has AFTER DELETE/UPDATE triggers which access to subject table. These flags are reset by the handler::extra(HA_EXTRA_RESET) call. @@ -7125,7 +8629,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, int ha_partition::extra(enum ha_extra_function operation) { DBUG_ENTER("ha_partition:extra"); - DBUG_PRINT("info", ("operation: %d", (int) operation)); + DBUG_PRINT("enter", ("operation: %d", (int) operation)); switch (operation) { /* Category 1), used by most handlers */ @@ -7146,13 +8650,13 @@ int ha_partition::extra(enum ha_extra_function operation) { if (!m_myisam) DBUG_RETURN(loop_extra(operation)); - break; } + break; /* Category 3), used by MyISAM handlers */ case HA_EXTRA_PREPARE_FOR_UPDATE: /* - Needs to be run on the first partition in the range now, and + Needs to be run on the first partition in the range now, and later in late_extra_cache, when switching to a new partition to scan. */ m_extra_prepare_for_update= TRUE; @@ -7221,18 +8725,9 @@ int ha_partition::extra(enum ha_extra_function operation) with row being inserted by PK/unique key without reporting error to the SQL-layer. - This optimization is not safe for partitioned table in general case - since we may have to put new version of row into partition which is - different from partition in which old version resides (for example - when we partition by non-PK column or by some column which is not - part of unique key which were violated). - And since NDB which is the only engine at the moment that supports - this optimization handles partitioning on its own we simple disable - it here. (BTW for NDB this optimization is safe since it supports - only KEY partitioning and won't use this optimization for tables - which have additional unique constraints). + At this time, this is safe by limitation of ha_partition */ - break; + DBUG_RETURN(loop_extra(operation)); } /* Category 7), used by federated handlers */ case HA_EXTRA_INSERT_WITH_UPDATE: @@ -7246,20 +8741,39 @@ int ha_partition::extra(enum ha_extra_function operation) } /* Category 9) Operations only used by MERGE */ case HA_EXTRA_ADD_CHILDREN_LIST: + DBUG_RETURN(loop_extra(operation)); case HA_EXTRA_ATTACH_CHILDREN: - case HA_EXTRA_IS_ATTACHED_CHILDREN: - case HA_EXTRA_DETACH_CHILDREN: { - /* Special actions for MERGE tables. Ignore. */ + int result; + uint num_locks; + handler **file; + if ((result= loop_extra(operation))) + DBUG_RETURN(result); + + /* Recalculate lock count as each child may have different set of locks */ + num_locks= 0; + file= m_file; + do + { + num_locks+= (*file)->lock_count(); + } while (*(++file)); + + m_num_locks= num_locks; break; } + case HA_EXTRA_IS_ATTACHED_CHILDREN: + DBUG_RETURN(loop_extra(operation)); + case HA_EXTRA_DETACH_CHILDREN: + DBUG_RETURN(loop_extra(operation)); + case HA_EXTRA_MARK_AS_LOG_TABLE: /* http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html says we no longer support logging to partitioned tables, so we fail here. */ - case HA_EXTRA_MARK_AS_LOG_TABLE: DBUG_RETURN(ER_UNSUPORTED_LOG_ENGINE); + case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN: + DBUG_RETURN(loop_extra(operation)); default: { /* Temporary crash to discover what is wrong */ @@ -7340,7 +8854,7 @@ int ha_partition::extra_opt(enum ha_extra_function operation, ulong cachesize) void ha_partition::prepare_extra_cache(uint cachesize) { DBUG_ENTER("ha_partition::prepare_extra_cache()"); - DBUG_PRINT("info", ("cachesize %u", cachesize)); + DBUG_PRINT("enter", ("cachesize %u", cachesize)); m_extra_cache= TRUE; m_extra_cache_size= cachesize; @@ -7407,7 +8921,7 @@ int ha_partition::loop_extra(enum ha_extra_function operation) int result= 0, tmp; uint i; DBUG_ENTER("ha_partition::loop_extra()"); - + for (i= bitmap_get_first_set(&m_part_info->lock_partitions); i < m_tot_parts; i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) @@ -7436,9 +8950,9 @@ void ha_partition::late_extra_cache(uint partition_id) { handler *file; DBUG_ENTER("ha_partition::late_extra_cache"); - DBUG_PRINT("info", ("extra_cache %u prepare %u partid %u size %u", - m_extra_cache, m_extra_prepare_for_update, - partition_id, m_extra_cache_size)); + DBUG_PRINT("enter", ("extra_cache %u prepare %u partid %u size %u", + m_extra_cache, m_extra_prepare_for_update, + partition_id, m_extra_cache_size)); if (!m_extra_cache && !m_extra_prepare_for_update) DBUG_VOID_RETURN; @@ -7635,7 +9149,7 @@ ha_rows ha_partition::records_in_range(uint inx, key_range *min_key, != NO_CURRENT_PART_ID) { rows= m_file[part_id]->records_in_range(inx, min_key, max_key); - + DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows, (ulong) m_file[part_id]->stats.records)); @@ -7737,7 +9251,8 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows) ha_rows ha_partition::records() { - ha_rows rows, tot_rows= 0; + int error; + ha_rows tot_rows= 0; uint i; DBUG_ENTER("ha_partition::records"); @@ -7745,11 +9260,13 @@ ha_rows ha_partition::records() i < m_tot_parts; i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - rows= m_file[i]->records(); - if (rows == HA_POS_ERROR) + ha_rows rows; + if ((error= m_file[i]->pre_records()) || + (rows= m_file[i]->records()) == HA_POS_ERROR) DBUG_RETURN(HA_POS_ERROR); tot_rows+= rows; } + DBUG_PRINT("exit", ("records: %lld", (longlong) tot_rows)); DBUG_RETURN(tot_rows); } @@ -7773,7 +9290,7 @@ bool ha_partition::can_switch_engines() { handler **file; DBUG_ENTER("ha_partition::can_switch_engines"); - + file= m_file; do { @@ -8008,10 +9525,9 @@ void ha_partition::print_error(int error, myf errflag) { THD *thd= ha_thd(); DBUG_ENTER("ha_partition::print_error"); - - /* Should probably look for my own errors first */ DBUG_PRINT("enter", ("error: %d", error)); + /* Should probably look for my own errors first */ if ((error == HA_ERR_NO_PARTITION_FOUND) && ! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION)) { @@ -8514,7 +10030,7 @@ uint ha_partition::min_record_length(uint options) const the same record. Otherwise we use the particular handler to decide if they are the same. Sort in partition id order if not equal. - MariaDB note: + MariaDB note: Please don't merge the code from MySQL that does this: We get two references and need to check if those records are the same. @@ -8528,15 +10044,18 @@ uint ha_partition::min_record_length(uint options) const int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) { int cmp; - my_ptrdiff_t diff1, diff2; + uint32 diff1, diff2; DBUG_ENTER("ha_partition::cmp_ref"); - cmp = m_file[0]->cmp_ref((ref1 + PARTITION_BYTES_IN_POS), - (ref2 + PARTITION_BYTES_IN_POS)); + cmp= m_file[0]->cmp_ref((ref1 + PARTITION_BYTES_IN_POS), + (ref2 + PARTITION_BYTES_IN_POS)); if (cmp) DBUG_RETURN(cmp); - if ((ref1[0] == ref2[0]) && (ref1[1] == ref2[1])) + diff2= uint2korr(ref2); + diff1= uint2korr(ref1); + + if (diff1 == diff2) { /* This means that the references are same and are in same partition.*/ DBUG_RETURN(0); @@ -8549,22 +10068,7 @@ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) Remove this assert if DB_ROW_ID is changed to be per partition. */ DBUG_ASSERT(!m_innodb); - - diff1= ref2[1] - ref1[1]; - diff2= ref2[0] - ref1[0]; - if (diff1 > 0) - { - DBUG_RETURN(-1); - } - if (diff1 < 0) - { - DBUG_RETURN(+1); - } - if (diff2 > 0) - { - DBUG_RETURN(-1); - } - DBUG_RETURN(+1); + DBUG_RETURN(diff2 > diff1 ? -1 : 1); } @@ -8573,6 +10077,82 @@ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) ****************************************************************************/ +/** + Retreive new values for part_share->next_auto_inc_val if needed + + This is needed if the value has not been initialized or if one of + the underlying partitions require that the value should be re-calculated +*/ + +void ha_partition::update_next_auto_inc_val() +{ + if (!part_share->auto_inc_initialized || + need_info_for_auto_inc()) + info(HA_STATUS_AUTO); +} + + +/** + Determine whether a partition needs auto-increment initialization. + + @return + TRUE A partition needs auto-increment initialization + FALSE No partition needs auto-increment initialization + + Resets part_share->auto_inc_initialized if next auto_increment needs to be + recalculated. +*/ + +bool ha_partition::need_info_for_auto_inc() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::need_info_for_auto_inc"); + + do + { + if ((*file)->need_info_for_auto_inc()) + { + /* We have to get new auto_increment values from handler */ + part_share->auto_inc_initialized= FALSE; + DBUG_RETURN(TRUE); + } + } while (*(++file)); + DBUG_RETURN(FALSE); +} + + +/** + Determine if all partitions can use the current auto-increment value for + auto-increment initialization. + + @return + TRUE All partitions can use the current auto-increment + value for auto-increment initialization + FALSE All partitions cannot use the current + auto-increment value for auto-increment + initialization + + Notes + This function is only called for ::info(HA_STATUS_AUTO) and is + mainly used by the Spider engine, which returns false + except in the case of DROP TABLE or ALTER TABLE when it returns TRUE. + Other engines always returns TRUE for this call. +*/ + +bool ha_partition::can_use_for_auto_inc_init() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::can_use_for_auto_inc_init"); + + do + { + if (!(*file)->can_use_for_auto_inc_init()) + DBUG_RETURN(FALSE); + } while (*(++file)); + DBUG_RETURN(TRUE); +} + + int ha_partition::reset_auto_increment(ulonglong value) { handler **file= m_file; @@ -8606,8 +10186,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, ulonglong *nb_reserved_values) { DBUG_ENTER("ha_partition::get_auto_increment"); - DBUG_PRINT("info", ("offset: %lu inc: %lu desired_values: %lu " - "first_value: %lu", (ulong) offset, (ulong) increment, + DBUG_PRINT("enter", ("offset: %lu inc: %lu desired_values: %lu " + "first_value: %lu", (ulong) offset, (ulong) increment, (ulong) nb_desired_values, (ulong) *first_value)); DBUG_ASSERT(increment && nb_desired_values); *first_value= 0; @@ -8651,7 +10231,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, /* Get a lock for handling the auto_increment in part_share for avoiding two concurrent statements getting the same number. - */ + */ lock_auto_increment(); @@ -8662,8 +10242,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, based replication. Because the statement-based binary log contains only the first generated value used by the statement, and slaves assumes all other generated values used by this statement were consecutive to - this first one, we must exclusively lock the generator until the statement - is done. + this first one, we must exclusively lock the generator until the + statement is done. */ if (!auto_increment_safe_stmt_log_lock && thd->lex->sql_command != SQLCOM_INSERT && @@ -8926,8 +10506,8 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) } else { - DBUG_PRINT("info", ("Moving row from partition %d to %d", - read_part_id, correct_part_id)); + DBUG_PRINT("info", ("Moving row from partition %u to %u", + (uint) read_part_id, (uint) correct_part_id)); /* Insert row into correct partition. Notice that there are no commit @@ -8958,19 +10538,19 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) { /* Log this error, so the DBA can notice it and fix it! */ sql_print_error("Table '%-192s' failed to move/insert a row" - " from part %d into part %d:\n%s", + " from part %u into part %u:\n%s", table->s->table_name.str, - read_part_id, - correct_part_id, + (uint) read_part_id, + (uint) correct_part_id, str.c_ptr_safe()); } print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error", table_share->db.str, table->alias, opt_op_name[REPAIR_PARTS], "Failed to move/insert a row" - " from part %d into part %d:\n%s", - read_part_id, - correct_part_id, + " from part %u into part %u:\n%s", + (uint) read_part_id, + (uint) correct_part_id, str.c_ptr_safe()); break; } @@ -8991,14 +10571,14 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) append_row_to_str(str); /* Log this error, so the DBA can notice it and fix it! */ - sql_print_error("Table '%-192s': Delete from part %d failed with" + sql_print_error("Table '%-192s': Delete from part %u failed with" " error %d. But it was already inserted into" - " part %d, when moving the misplaced row!" + " part %u, when moving the misplaced row!" "\nPlease manually fix the duplicate row:\n%s", table->s->table_name.str, - read_part_id, + (uint) read_part_id, result, - correct_part_id, + (uint) correct_part_id, str.c_ptr_safe()); break; } @@ -9128,6 +10708,670 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt) } +TABLE_LIST *ha_partition::get_next_global_for_child() +{ + handler **file; + DBUG_ENTER("ha_partition::get_next_global_for_child"); + for (file= m_file; *file; file++) + { + TABLE_LIST *table_list; + if ((table_list= (*file)->get_next_global_for_child())) + DBUG_RETURN(table_list); + } + DBUG_RETURN(0); +} + + +const COND *ha_partition::cond_push(const COND *cond) +{ + handler **file= m_file; + COND *res_cond= NULL; + DBUG_ENTER("ha_partition::cond_push"); + + if (set_top_table_fields) + { + /* + We want to do this in a separate loop to not come into a situation + where we have only done cond_push() to some of the tables + */ + do + { + if (((*file)->set_top_table_and_fields(top_table, + top_table_field, + top_table_fields))) + DBUG_RETURN(cond); // Abort cond push, no error + } while (*(++file)); + file= m_file; + } + + do + { + if ((*file)->pushed_cond != cond) + { + if ((*file)->cond_push(cond)) + res_cond= (COND *) cond; + else + (*file)->pushed_cond= cond; + } + } while (*(++file)); + DBUG_RETURN(res_cond); +} + + +void ha_partition::cond_pop() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::cond_push"); + + do + { + (*file)->cond_pop(); + } while (*(++file)); + DBUG_VOID_RETURN; +} + + +/** + Perform bulk update preparation on each partition. + + SYNOPSIS + start_bulk_update() + + RETURN VALUE + TRUE Error + FALSE Success +*/ + +bool ha_partition::start_bulk_update() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::start_bulk_update"); + + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + table->write_set)) + DBUG_RETURN(TRUE); + + do + { + if ((*file)->start_bulk_update()) + DBUG_RETURN(TRUE); + } while (*(++file)); + DBUG_RETURN(FALSE); +} + + +/** + Perform bulk update execution on each partition. A bulk update allows + a handler to batch the updated rows instead of performing the updates + one row at a time. + + SYNOPSIS + exec_bulk_update() + + RETURN VALUE + TRUE Error + FALSE Success +*/ + +int ha_partition::exec_bulk_update(ha_rows *dup_key_found) +{ + int error; + handler **file= m_file; + DBUG_ENTER("ha_partition::exec_bulk_update"); + + do + { + if ((error= (*file)->exec_bulk_update(dup_key_found))) + DBUG_RETURN(error); + } while (*(++file)); + DBUG_RETURN(0); +} + + +/** + Perform bulk update cleanup on each partition. + + SYNOPSIS + end_bulk_update() + + RETURN VALUE + NONE +*/ + +int ha_partition::end_bulk_update() +{ + int error= 0; + handler **file= m_file; + DBUG_ENTER("ha_partition::end_bulk_update"); + + do + { + int tmp; + if ((tmp= (*file)->end_bulk_update())) + error= tmp; + } while (*(++file)); + DBUG_RETURN(error); +} + + +/** + Add the row to the bulk update on the partition on which the row is stored. + A bulk update allows a handler to batch the updated rows instead of + performing the updates one row at a time. + + SYNOPSIS + bulk_update_row() + old_data Old record + new_data New record + dup_key_found Number of duplicate keys found + + RETURN VALUE + >1 Error + 1 Bulk update not used, normal operation used + 0 Bulk update used by handler +*/ + +int ha_partition::bulk_update_row(const uchar *old_data, const uchar *new_data, + ha_rows *dup_key_found) +{ + int error= 0; + uint32 part_id; + longlong func_value; + my_bitmap_map *old_map; + DBUG_ENTER("ha_partition::bulk_update_row"); + + old_map= dbug_tmp_use_all_columns(table, table->read_set); + error= m_part_info->get_partition_id(m_part_info, &part_id, + &func_value); + dbug_tmp_restore_column_map(table->read_set, old_map); + if (unlikely(error)) + { + m_part_info->err_value= func_value; + goto end; + } + + error= m_file[part_id]->ha_bulk_update_row(old_data, new_data, + dup_key_found); + +end: + DBUG_RETURN(error); +} + + +/** + Perform bulk delete preparation on each partition. + + SYNOPSIS + start_bulk_delete() + + RETURN VALUE + TRUE Error + FALSE Success +*/ + +bool ha_partition::start_bulk_delete() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::start_bulk_delete"); + + do + { + if ((*file)->start_bulk_delete()) + DBUG_RETURN(TRUE); + } while (*(++file)); + DBUG_RETURN(FALSE); +} + + +/** + Perform bulk delete cleanup on each partition. + + SYNOPSIS + end_bulk_delete() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::end_bulk_delete() +{ + int error= 0; + handler **file= m_file; + DBUG_ENTER("ha_partition::end_bulk_delete"); + + do + { + int tmp; + if ((tmp= (*file)->end_bulk_delete())) + error= tmp; + } while (*(++file)); + DBUG_RETURN(error); +} + + +/** + Perform initialization for a direct update request. + + SYNOPSIS + direct_update_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_update_rows_init() +{ + int error; + uint i, found; + handler *file; + DBUG_ENTER("ha_partition::direct_update_rows_init"); + + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + table->write_set)) + { + DBUG_PRINT("info", ("partition FALSE by updating part_key")); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + + m_part_spec.start_part= 0; + m_part_spec.end_part= m_tot_parts - 1; + m_direct_update_part_spec= m_part_spec; + + found= 0; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + file= m_file[i]; + if ((error= (m_pre_calling ? + file->pre_direct_update_rows_init() : + file->direct_update_rows_init()))) + { + DBUG_PRINT("info", ("partition FALSE by storage engine")); + DBUG_RETURN(error); + } + found++; + } + } + + TABLE_LIST *table_list= table->pos_in_table_list; + if (found != 1 && table_list) + { + while (table_list->parent_l) + table_list= table_list->parent_l; + st_select_lex *select_lex= table_list->select_lex; + DBUG_PRINT("info", ("partition select_lex: %p", select_lex)); + if (select_lex && select_lex->explicit_limit) + { + DBUG_PRINT("info", ("partition explicit_limit=TRUE")); + DBUG_PRINT("info", ("partition offset_limit: %p", + select_lex->offset_limit)); + DBUG_PRINT("info", ("partition select_limit: %p", + select_lex->select_limit)); + DBUG_PRINT("info", ("partition FALSE by select_lex")); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + } + DBUG_PRINT("info", ("partition OK")); + DBUG_RETURN(0); +} + + +/** + Do initialization for performing parallel direct update + for a handlersocket update request. + + SYNOPSIS + pre_direct_update_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_update_rows_init() +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_direct_update_rows_init"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_update_rows_init(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Execute a direct update request. A direct update request updates all + qualified rows in a single operation, rather than one row at a time. + The direct update operation is pushed down to each individual + partition. + + SYNOPSIS + direct_update_rows() + update_rows Number of updated rows + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_update_rows(ha_rows *update_rows_result) +{ + int error; + bool rnd_seq= FALSE; + ha_rows update_rows= 0; + uint32 i; + DBUG_ENTER("ha_partition::direct_update_rows"); + + /* If first call to direct_update_rows with RND scan */ + if ((m_pre_calling ? pre_inited : inited) == RND && m_scan_value == 1) + { + rnd_seq= TRUE; + m_scan_value= 2; + } + + *update_rows_result= 0; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + handler *file= m_file[i]; + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + if (rnd_seq && (m_pre_calling ? file->pre_inited : file->inited) == NONE) + { + if ((error= (m_pre_calling ? + file->ha_pre_rnd_init(TRUE) : + file->ha_rnd_init(TRUE)))) + DBUG_RETURN(error); + } + if ((error= (m_pre_calling ? + (file)->pre_direct_update_rows() : + (file)->ha_direct_update_rows(&update_rows)))) + { + if (rnd_seq) + { + if (m_pre_calling) + file->ha_pre_rnd_end(); + else + file->ha_rnd_end(); + } + DBUG_RETURN(error); + } + *update_rows_result+= update_rows; + } + if (rnd_seq) + { + if ((error= (m_pre_calling ? + file->ha_pre_index_or_rnd_end() : + file->ha_index_or_rnd_end()))) + DBUG_RETURN(error); + } + } + DBUG_RETURN(0); +} + + +/** + Start parallel execution of a direct update for a handlersocket update + request. A direct update request updates all qualified rows in a single + operation, rather than one row at a time. The direct update operation + is pushed down to each individual partition. + + SYNOPSIS + pre_direct_update_rows() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_update_rows() +{ + bool save_m_pre_calling; + int error; + ha_rows not_used= 0; + DBUG_ENTER("ha_partition::pre_direct_update_rows"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_update_rows(¬_used); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Perform initialization for a direct delete request. + + SYNOPSIS + direct_delete_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_delete_rows_init() +{ + int error; + uint i, found; + DBUG_ENTER("ha_partition::direct_delete_rows_init"); + + m_part_spec.start_part= 0; + m_part_spec.end_part= m_tot_parts - 1; + m_direct_update_part_spec= m_part_spec; + + found= 0; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + handler *file= m_file[i]; + if ((error= (m_pre_calling ? + file->pre_direct_delete_rows_init() : + file->direct_delete_rows_init()))) + { + DBUG_PRINT("exit", ("error in direct_delete_rows_init")); + DBUG_RETURN(error); + } + found++; + } + } + + TABLE_LIST *table_list= table->pos_in_table_list; + if (found != 1 && table_list) + { + while (table_list->parent_l) + table_list= table_list->parent_l; + st_select_lex *select_lex= table_list->select_lex; + DBUG_PRINT("info", ("partition select_lex: %p", select_lex)); + if (select_lex && select_lex->explicit_limit) + { + DBUG_PRINT("info", ("partition explicit_limit: TRUE")); + DBUG_PRINT("info", ("partition offset_limit: %p", + select_lex->offset_limit)); + DBUG_PRINT("info", ("partition select_limit: %p", + select_lex->select_limit)); + DBUG_PRINT("info", ("partition FALSE by select_lex")); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + } + DBUG_PRINT("exit", ("OK")); + DBUG_RETURN(0); +} + + +/** + Do initialization for performing parallel direct delete + for a handlersocket delete request. + + SYNOPSIS + pre_direct_delete_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_delete_rows_init() +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_direct_delete_rows_init"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_delete_rows_init(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Execute a direct delete request. A direct delete request deletes all + qualified rows in a single operation, rather than one row at a time. + The direct delete operation is pushed down to each individual + partition. + + SYNOPSIS + direct_delete_rows() + delete_rows Number of deleted rows + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_delete_rows(ha_rows *delete_rows_result) +{ + int error; + bool rnd_seq= FALSE; + ha_rows delete_rows= 0; + uint32 i; + handler *file; + DBUG_ENTER("ha_partition::direct_delete_rows"); + + if ((m_pre_calling ? pre_inited : inited) == RND && m_scan_value == 1) + { + rnd_seq= TRUE; + m_scan_value= 2; + } + + *delete_rows_result= 0; + m_part_spec= m_direct_update_part_spec; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + file= m_file[i]; + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + if (rnd_seq && (m_pre_calling ? file->pre_inited : file->inited) == NONE) + { + if ((error= (m_pre_calling ? + file->ha_pre_rnd_init(TRUE) : + file->ha_rnd_init(TRUE)))) + DBUG_RETURN(error); + } + if ((error= (m_pre_calling ? + file->pre_direct_delete_rows() : + file->ha_direct_delete_rows(&delete_rows)))) + { + if (m_pre_calling) + file->ha_pre_rnd_end(); + else + file->ha_rnd_end(); + DBUG_RETURN(error); + } + delete_rows_result+= delete_rows; + } + if (rnd_seq) + { + if ((error= (m_pre_calling ? + file->ha_pre_index_or_rnd_end() : + file->ha_index_or_rnd_end()))) + DBUG_RETURN(error); + } + } + DBUG_RETURN(0); +} + + +/** + Start parallel execution of a direct delete for a handlersocket delete + request. A direct delete request deletes all qualified rows in a single + operation, rather than one row at a time. The direct delete operation + is pushed down to each individual partition. + + SYNOPSIS + pre_direct_delete_rows() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_delete_rows() +{ + bool save_m_pre_calling; + int error; + ha_rows not_used; + DBUG_ENTER("ha_partition::pre_direct_delete_rows"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_delete_rows(¬_used); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + +/** + Push metadata for the current operation down to each partition. + + SYNOPSIS + info_push() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::info_push(uint info_type, void *info) +{ + int error= 0; + handler **file= m_file; + DBUG_ENTER("ha_partition::info_push"); + + do + { + int tmp; + if ((tmp= (*file)->info_push(info_type, info))) + error= tmp; + } while (*(++file)); + DBUG_RETURN(error); +} + + +void ha_partition::clear_top_table_fields() +{ + handler **file; + DBUG_ENTER("ha_partition::clear_top_table_fields"); + + if (set_top_table_fields) + { + set_top_table_fields= FALSE; + top_table= NULL; + top_table_field= NULL; + top_table_fields= 0; + for (file= m_file; *file; file++) + (*file)->clear_top_table_fields(); + } + DBUG_VOID_RETURN; +} + + struct st_mysql_storage_engine partition_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 42081546988..a13d9bd7618 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -48,10 +48,8 @@ public: { uint i; for (i= 0; i < num_parts; i++) - if (ha_shares[i]) - delete ha_shares[i]; - if (ha_shares) - delete [] ha_shares; + delete ha_shares[i]; + delete[] ha_shares; } bool init(uint arg_num_parts) { @@ -69,6 +67,17 @@ public: } }; +class ha_partition; + +/* Partition Full Text Search info */ +struct st_partition_ft_info +{ + struct _ft_vft *please; + st_partition_ft_info *next; + ha_partition *file; + FT_INFO **part_ft_info; +}; + /** Partition specific Handler_share. @@ -86,7 +95,7 @@ public: bool partition_name_hash_initialized; HASH partition_name_hash; /** Storage for each partitions Handler_share */ - Parts_share_refs *partitions_share_refs; + Parts_share_refs partitions_share_refs; Partition_share() {} ~Partition_share() { @@ -94,8 +103,6 @@ public: mysql_mutex_destroy(&auto_inc_mutex); if (partition_name_hash_initialized) my_hash_free(&partition_name_hash); - if (partitions_share_refs) - delete partitions_share_refs; DBUG_VOID_RETURN; } bool init(uint num_parts); @@ -109,7 +116,34 @@ public: } }; +typedef struct st_partition_key_multi_range +{ + uint id; + uchar *key[2]; + uint length[2]; + KEY_MULTI_RANGE key_multi_range; + range_id_t ptr; + st_partition_key_multi_range *next; +} PARTITION_KEY_MULTI_RANGE; + +typedef struct st_partition_part_key_multi_range +{ + PARTITION_KEY_MULTI_RANGE *partition_key_multi_range; + st_partition_part_key_multi_range *next; +} PARTITION_PART_KEY_MULTI_RANGE; + + +class ha_partition; +typedef struct st_partition_part_key_multi_range_hld +{ + ha_partition *partition; + uint32 part_id; + PARTITION_PART_KEY_MULTI_RANGE *partition_part_key_multi_range; +} PARTITION_PART_KEY_MULTI_RANGE_HLD; + + +extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2); extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); class ha_partition :public handler @@ -119,16 +153,17 @@ private: { partition_index_read= 0, partition_index_first= 1, - partition_index_first_unordered= 2, partition_index_last= 3, partition_index_read_last= 4, partition_read_range = 5, - partition_no_index_scan= 6 + partition_no_index_scan= 6, + partition_read_multi_range = 7, + partition_ft_read= 8 }; /* Data for the partition handler */ int m_mode; // Open mode uint m_open_test_lock; // Open test_if_locked - uchar *m_file_buffer; // Content of the .par file + uchar *m_file_buffer; // Content of the .par file char *m_name_buffer_ptr; // Pointer to first partition name MEM_ROOT m_mem_root; plugin_ref *m_engine_array; // Array of types of the handlers @@ -141,6 +176,8 @@ private: partition_info *m_part_info; // local reference to partition Field **m_part_field_array; // Part field array locally to save acc uchar *m_ordered_rec_buffer; // Row and key buffer for ord. idx scan + st_partition_ft_info *ft_first; + st_partition_ft_info *ft_current; /* Current index. When used in key_rec_cmp: If clustered pk, index compare @@ -157,7 +194,7 @@ private: Length of an element in m_ordered_rec_buffer. The elements are composed of [part_no] [table->record copy] [underlying_table_rowid] - + underlying_table_rowid is only stored when the table has no extended keys. */ uint m_priority_queue_rec_len; @@ -205,14 +242,16 @@ private: bool m_create_handler; // Handler used to create table bool m_is_sub_partitioned; // Is subpartitioned bool m_ordered_scan_ongoing; + bool m_rnd_init_and_first; + bool m_ft_init_and_first; - /* + /* If set, this object was created with ha_partition::clone and doesn't "own" the m_part_info structure. */ ha_partition *m_is_clone_of; MEM_ROOT *m_clone_mem_root; - + /* We keep track if all underlying handlers are MyISAM since MyISAM has a great number of extra flags not needed by other handlers. @@ -257,6 +296,12 @@ private: ha_rows m_bulk_inserted_rows; /** used for prediction of start_bulk_insert rows */ enum_monotonicity_info m_part_func_monotonicity_info; + part_id_range m_direct_update_part_spec; + bool m_pre_calling; + bool m_pre_call_use_parallel; + /* Keep track of bulk access requests */ + bool bulk_access_executing; + /** keep track of locked partitions */ MY_BITMAP m_locked_partitions; /** Stores shared auto_increment etc. */ @@ -275,6 +320,18 @@ private: MY_BITMAP m_key_not_found_partitions; bool m_key_not_found; public: + handler **get_child_handlers() + { + return m_file; + } + virtual part_id_range *get_part_spec() + { + return &m_part_spec; + } + virtual uint get_no_current_part_id() + { + return NO_CURRENT_PART_ID; + } Partition_share *get_part_share() { return part_share; } handler *clone(const char *name, MEM_ROOT *mem_root); virtual void set_part_info(partition_info *part_info) @@ -282,6 +339,9 @@ public: m_part_info= part_info; m_is_sub_partitioned= part_info->is_sub_partitioned(); } + + virtual void return_record_by_parent(); + /* ------------------------------------------------------------------------- MODULE create/delete handler object @@ -300,6 +360,7 @@ public: ha_partition *clone_arg, MEM_ROOT *clone_mem_root_arg); ~ha_partition(); + void ha_partition_init(); /* A partition handler has no characteristics in itself. It only inherits those from the underlying handlers. Here we set-up those constants to @@ -483,8 +544,23 @@ public: number of calls to write_row. */ virtual int write_row(uchar * buf); + virtual bool start_bulk_update(); + virtual int exec_bulk_update(ha_rows *dup_key_found); + virtual int end_bulk_update(); + virtual int bulk_update_row(const uchar *old_data, const uchar *new_data, + ha_rows *dup_key_found); virtual int update_row(const uchar * old_data, const uchar * new_data); + virtual int direct_update_rows_init(); + virtual int pre_direct_update_rows_init(); + virtual int direct_update_rows(ha_rows *update_rows); + virtual int pre_direct_update_rows(); + virtual bool start_bulk_delete(); + virtual int end_bulk_delete(); virtual int delete_row(const uchar * buf); + virtual int direct_delete_rows_init(); + virtual int pre_direct_delete_rows_init(); + virtual int direct_delete_rows(ha_rows *delete_rows); + virtual int pre_direct_delete_rows(); virtual int delete_all_rows(void); virtual int truncate(); virtual void start_bulk_insert(ha_rows rows, uint flags); @@ -607,16 +683,8 @@ public: read_first_row is virtual method but is only implemented by handler.cc, no storage engine has implemented it so neither will the partition handler. - - virtual int read_first_row(uchar *buf, uint primary_key); - */ - /* - We don't implement multi read range yet, will do later. - virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, - KEY_MULTI_RANGE *ranges, uint range_count, - bool sorted, HANDLER_BUFFER *buffer); - virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); + virtual int read_first_row(uchar *buf, uint primary_key); */ @@ -625,12 +693,59 @@ public: bool eq_range, bool sorted); virtual int read_range_next(); + + HANDLER_BUFFER *m_mrr_buffer; + uint *m_mrr_buffer_size; + uchar *m_mrr_full_buffer; + uint m_mrr_full_buffer_size; + uint m_mrr_new_full_buffer_size; + MY_BITMAP m_mrr_used_partitions; + uint *m_stock_range_seq; + uint m_current_range_seq; + uint m_mrr_mode; + uint m_mrr_n_ranges; + range_id_t *m_range_info; + bool m_multi_range_read_first; + uint m_mrr_range_init_flags; + uint m_mrr_range_length; + PARTITION_KEY_MULTI_RANGE *m_mrr_range_first; + PARTITION_KEY_MULTI_RANGE *m_mrr_range_current; + uint *m_part_mrr_range_length; + PARTITION_PART_KEY_MULTI_RANGE **m_part_mrr_range_first; + PARTITION_PART_KEY_MULTI_RANGE **m_part_mrr_range_current; + PARTITION_PART_KEY_MULTI_RANGE_HLD *m_partition_part_key_multi_range_hld; + range_seq_t m_seq; + RANGE_SEQ_IF *m_seq_if; + RANGE_SEQ_IF m_part_seq_if; + + virtual int multi_range_key_create_key( + RANGE_SEQ_IF *seq, + range_seq_t seq_it + ); + virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint *bufsz, + uint *mrr_mode, + Cost_estimate *cost); + virtual ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys, + uint key_parts, uint *bufsz, + uint *mrr_mode, Cost_estimate *cost); + virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, + uint n_ranges, uint mrr_mode, + HANDLER_BUFFER *buf); + virtual int multi_range_read_next(range_id_t *range_info); + virtual int multi_range_read_explain_info(uint mrr_mode, char *str, + size_t size); + uint last_part() { return m_last_part; } + private: bool init_record_priority_queue(); void destroy_record_priority_queue(); int common_index_read(uchar * buf, bool have_start_key); int common_first_last(uchar * buf); int partition_scan_set_up(uchar * buf, bool idx_read_flag); + bool check_parallel_search(); + int handle_pre_scan(bool reverse_order, bool use_parallel); int handle_unordered_next(uchar * buf, bool next_same); int handle_unordered_scan_next_partition(uchar * buf); int handle_ordered_index_scan(uchar * buf, bool reverse_order); @@ -671,7 +786,7 @@ private: Query_cache_block_table **block_table, handler *file, uint *n); - static const uint NO_CURRENT_PART_ID; + static const uint NO_CURRENT_PART_ID= NOT_A_PARTITION_ID; int loop_extra(enum ha_extra_function operation); int loop_extra_alter(enum ha_extra_function operations); void late_extra_cache(uint partition_id); @@ -920,7 +1035,7 @@ public: special file for handling names of partitions, engine types. HA_REC_NOT_IN_SEQ is always set for partition handler since we cannot guarantee that the records will be returned in sequence. - HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPLICATE_POS, + HA_DUPLICATE_POS, HA_CAN_INSERT_DELAYED, HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is disabled until further investigated. */ @@ -1002,7 +1117,7 @@ public: The maximum supported values is the minimum of all handlers in the table */ - uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const; + uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const; virtual uint max_supported_record_length() const; virtual uint max_supported_keys() const; virtual uint max_supported_key_parts() const; @@ -1047,6 +1162,8 @@ public: auto_increment_column_changed ------------------------------------------------------------------------- */ + virtual bool need_info_for_auto_inc(); + virtual bool can_use_for_auto_inc_init(); virtual void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, @@ -1054,16 +1171,17 @@ public: virtual void release_auto_increment(); private: virtual int reset_auto_increment(ulonglong value); + void update_next_auto_inc_val(); virtual void lock_auto_increment() { /* lock already taken */ if (auto_increment_safe_stmt_log_lock) return; - DBUG_ASSERT(!auto_increment_lock); - if(table_share->tmp_table == NO_TMP_TABLE) + if (table_share->tmp_table == NO_TMP_TABLE) { - auto_increment_lock= TRUE; part_share->lock_auto_inc(); + DBUG_ASSERT(!auto_increment_lock); + auto_increment_lock= TRUE; } } virtual void unlock_auto_increment() @@ -1073,10 +1191,10 @@ private: It will be set to false and thus unlocked at the end of the statement by ha_partition::release_auto_increment. */ - if(auto_increment_lock && !auto_increment_safe_stmt_log_lock) + if (auto_increment_lock && !auto_increment_safe_stmt_log_lock) { - part_share->unlock_auto_inc(); auto_increment_lock= FALSE; + part_share->unlock_auto_inc(); } } virtual void set_auto_increment_if_higher(Field *field) @@ -1084,7 +1202,8 @@ private: ulonglong nr= (((Field_num*) field)->unsigned_flag || field->val_int() > 0) ? field->val_int() : 0; lock_auto_increment(); - DBUG_ASSERT(part_share->auto_inc_initialized); + DBUG_ASSERT(part_share->auto_inc_initialized || + !can_use_for_auto_inc_init()); /* must check when the mutex is taken */ if (nr >= part_share->next_auto_inc_val) part_share->next_auto_inc_val= nr + 1; @@ -1133,14 +1252,15 @@ public: ------------------------------------------------------------------------- MODULE fulltext index ------------------------------------------------------------------------- - Fulltext stuff not yet. - ------------------------------------------------------------------------- - virtual int ft_init() { return HA_ERR_WRONG_COMMAND; } - virtual FT_INFO *ft_init_ext(uint flags,uint inx,const uchar *key, - uint keylen) - { return NULL; } - virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } */ + void ft_close_search(FT_INFO *handler); + virtual int ft_init(); + virtual int pre_ft_init(); + virtual void ft_end(); + virtual int pre_ft_end(); + virtual FT_INFO *ft_init_ext(uint flags, uint inx, String *key); + virtual int ft_read(uchar *buf); + virtual int pre_ft_read(bool use_parallel); /* ------------------------------------------------------------------------- @@ -1202,6 +1322,16 @@ public: virtual bool is_crashed() const; virtual int check_for_upgrade(HA_CHECK_OPT *check_opt); + /* + ------------------------------------------------------------------------- + MODULE condition pushdown + ------------------------------------------------------------------------- + */ + virtual const COND *cond_push(const COND *cond); + virtual void cond_pop(); + virtual void clear_top_table_fields(); + virtual int info_push(uint info_type, void *info); + private: int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags); int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, uint part_id, @@ -1229,6 +1359,7 @@ public: /* Enabled keycache for performance reasons, WL#4571 */ virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt); virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt); + virtual TABLE_LIST *get_next_global_for_child(); /* ------------------------------------------------------------------------- @@ -1276,6 +1407,6 @@ public: } friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); + friend int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2); }; - #endif /* HA_PARTITION_INCLUDED */ diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc index 6923997cf07..93f6f32d473 100644 --- a/sql/ha_sequence.cc +++ b/sql/ha_sequence.cc @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_list.h" #include "table.h" #include "sql_sequence.h" diff --git a/sql/handler.cc b/sql/handler.cc index 10c2b65bbc0..b16f8b88c20 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -20,10 +20,10 @@ Handler-calling-functions */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" -#include "rpl_handler.h" +#include "rpl_rli.h" #include "sql_cache.h" // query_cache, query_cache_* #include "sql_connect.h" // global_table_stats #include "key.h" // key_copy, key_unpack, key_cmp_if_same, key_cmp @@ -50,6 +50,7 @@ #ifdef WITH_ARIA_STORAGE_ENGINE #include "../storage/maria/ha_maria.h" #endif +#include "semisync_master.h" #include "wsrep_mysqld.h" #include "wsrep.h" @@ -1484,7 +1485,10 @@ done: mysql_mutex_assert_not_owner(mysql_bin_log.get_log_lock()); mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - RUN_HOOK(transaction, after_commit, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_commit(thd, all); + DEBUG_SYNC(thd, "after_group_after_commit"); +#endif goto end; /* Come here if error and we need to rollback. */ @@ -1558,6 +1562,7 @@ static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) { int error= 0; + uint count= 0; Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; DBUG_ENTER("commit_one_phase_2"); if (is_real_trans) @@ -1575,6 +1580,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) } /* Should this be done only if is_real_trans is set ? */ status_var_increment(thd->status_var.ha_commit_count); + if (is_real_trans && ht != binlog_hton && ha_info->is_trx_read_write()) + ++count; ha_info_next= ha_info->next(); ha_info->reset(); /* keep it conveniently zero-filled */ } @@ -1593,6 +1600,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) { thd->has_waiter= false; thd->transaction.cleanup(); + if (count >= 2) + statistic_increment(transactions_multi_engine, LOCK_status); } DBUG_RETURN(error); @@ -1724,7 +1733,9 @@ int ha_rollback_trans(THD *thd, bool all) push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER_THD(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK)); - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, all); +#endif DBUG_RETURN(error); } @@ -2509,6 +2520,18 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, /**************************************************************************** ** General handler functions ****************************************************************************/ + + +/** + Clone a handler + + @param name name of new table instance + @param mem_root Where 'this->ref' should be allocated. It can't be + in this->table->mem_root as otherwise we will not be + able to reclaim that memory when the clone handler + object is destroyed. +*/ + handler *handler::clone(const char *name, MEM_ROOT *mem_root) { handler *new_handler= get_new_handler(table->s, mem_root, ht); @@ -2519,16 +2542,6 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) goto err; /* - Allocate handler->ref here because otherwise ha_open will allocate it - on this->table->mem_root and we will not be able to reclaim that memory - when the clone handler object is destroyed. - */ - - if (!(new_handler->ref= (uchar*) alloc_root(mem_root, - ALIGN_SIZE(ref_length)*2))) - goto err; - - /* TODO: Implement a more efficient way to have more than one index open for the same table instance. The ha_open call is not cachable for clone. @@ -2536,7 +2549,7 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) and should be able to use the original instance of the table. */ if (new_handler->ha_open(table, name, table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) + HA_OPEN_IGNORE_IF_LOCKED, mem_root)) goto err; return new_handler; @@ -2614,7 +2627,7 @@ PSI_table_share *handler::ha_table_share_psi() const Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */ int handler::ha_open(TABLE *table_arg, const char *name, int mode, - uint test_if_locked) + uint test_if_locked, MEM_ROOT *mem_root) { int error; DBUG_ENTER("handler::ha_open"); @@ -2661,9 +2674,9 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, table->db_stat|=HA_READ_ONLY; (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL - /* ref is already allocated for us if we're called from handler::clone() */ - if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root, - ALIGN_SIZE(ref_length)*2))) + /* Allocate ref in thd or on the table's mem_root */ + if (!(ref= (uchar*) alloc_root(mem_root ? mem_root : &table->mem_root, + ALIGN_SIZE(ref_length)*2))) { ha_close(); error=HA_ERR_OUT_OF_MEM; @@ -3449,6 +3462,10 @@ void handler::ha_release_auto_increment() @param msg Error message template to which key value should be added. @param errflag Flags for my_error() call. + + @notes + The error message is from ER_DUP_ENTRY_WITH_KEY_NAME but to keep things compatibly + with old code, the error number is ER_DUP_ENTRY */ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag) @@ -3473,7 +3490,8 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag) str.length(max_length-4); str.append(STRING_WITH_LEN("...")); } - my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), key->name); + my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), + key->name.str); } } @@ -3708,7 +3726,7 @@ void handler::print_error(int error, myf errflag) const char *ptr= "???"; uint key_nr= get_dup_key(error); if ((int) key_nr >= 0) - ptr= table->key_info[key_nr].name; + ptr= table->key_info[key_nr].name.str; my_error(ER_DROP_INDEX_FK, errflag, ptr); DBUG_VOID_RETURN; } @@ -4170,7 +4188,7 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) int handler::ha_bulk_update_row(const uchar *old_data, const uchar *new_data, - uint *dup_key_found) + ha_rows *dup_key_found) { DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || m_lock_type == F_WRLCK); @@ -4784,7 +4802,7 @@ void handler::update_global_index_stats() DBUG_ASSERT(key_info->cache_name); if (!key_info->cache_name) continue; - key_length= table->s->table_cache_key.length + key_info->name_length + 1; + key_length= table->s->table_cache_key.length + key_info->name.length + 1; mysql_mutex_lock(&LOCK_global_index_stats); // Gets the global index stats, creating one if necessary. if (!(index_stats= (INDEX_STATS*) my_hash_search(&global_index_stats, @@ -5290,6 +5308,13 @@ static int cmp_table_names(LEX_CSTRING * const *a, LEX_CSTRING * const *b) (uchar*)((*b)->str), (*b)->length); } +#ifndef DBUG_OFF +static int cmp_table_names_desc(LEX_CSTRING * const *a, LEX_CSTRING * const *b) +{ + return -cmp_table_names(a, b); +} +#endif + } Discovered_table_list::Discovered_table_list(THD *thd_arg, @@ -5342,6 +5367,15 @@ void Discovered_table_list::sort() tables->sort(cmp_table_names); } + +#ifndef DBUG_OFF +void Discovered_table_list::sort_desc() +{ + tables->sort(cmp_table_names_desc); +} +#endif + + void Discovered_table_list::remove_duplicates() { LEX_CSTRING **src= tables->front(); @@ -5440,6 +5474,27 @@ int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp, } +/* +int handler::pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, + uint range_count, + bool sorted, HANDLER_BUFFER *buffer, + bool use_parallel) +{ + int result; + DBUG_ENTER("handler::pre_read_multi_range_first"); + result = pre_read_range_first(ranges->start_key.keypart_map ? + &ranges->start_key : 0, + ranges->end_key.keypart_map ? + &ranges->end_key : 0, + test(ranges->range_flag & EQ_RANGE), + sorted, + use_parallel); + DBUG_RETURN(result); +} +*/ + + /** Read first row between two ranges. Store ranges for future calls to read_range_next. @@ -5838,7 +5893,7 @@ bool handler::check_table_binlog_row_based_internal(bool binlog_row) } #endif - return (table->s->cached_row_logging_check && + return (table->s->can_do_row_logging && thd->is_current_stmt_binlog_format_row() && /* Wsrep partially enables binary logging if it have not been @@ -6096,6 +6151,7 @@ int handler::ha_reset() /* Reset information about pushed engine conditions */ cancel_pushed_idx_cond(); /* Reset information about pushed index conditions */ + clear_top_table_fields(); DBUG_RETURN(reset()); } @@ -6237,6 +6293,59 @@ int handler::ha_delete_row(const uchar *buf) } +/** + Execute a direct update request. A direct update request updates all + qualified rows in a single operation, rather than one row at a time. + In a Spider cluster the direct update operation is pushed down to the + child levels of the cluster. + + Note that this can't be used in case of statment logging + + @param update_rows Number of updated rows. + + @retval 0 Success. + @retval != 0 Failure. +*/ + +int handler::ha_direct_update_rows(ha_rows *update_rows) +{ + int error; + + MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); + mark_trx_read_write(); + + error = direct_update_rows(update_rows); + MYSQL_UPDATE_ROW_DONE(error); + return error; +} + + +/** + Execute a direct delete request. A direct delete request deletes all + qualified rows in a single operation, rather than one row at a time. + In a Spider cluster the direct delete operation is pushed down to the + child levels of the cluster. + + @param delete_rows Number of deleted rows. + + @retval 0 Success. + @retval != 0 Failure. +*/ + +int handler::ha_direct_delete_rows(ha_rows *delete_rows) +{ + int error; + /* Ensure we are not using binlog row */ + DBUG_ASSERT(!table->in_use->is_current_stmt_binlog_format_row()); + + MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str); + mark_trx_read_write(); + + error = direct_delete_rows(delete_rows); + MYSQL_DELETE_ROW_DONE(error); + return error; +} + /** @brief use_hidden_primary_key() is called in case of an update/delete when @@ -6698,7 +6807,7 @@ end: int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) { INDEX_STATS *index_stats; - uint key_length= table->s->table_cache_key.length + key_info->name_length + 1; + uint key_length= table->s->table_cache_key.length + key_info->name.length + 1; int res = 0; DBUG_ENTER("del_global_index_stat"); mysql_mutex_lock(&LOCK_global_index_stats); diff --git a/sql/handler.h b/sql/handler.h index 38fe44b8ccd..da18a8954de 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -25,7 +25,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_global.h> /* For handlers */ #include "sql_const.h" #include "sql_basic_types.h" #include "mysqld.h" /* server_id */ @@ -162,6 +161,7 @@ enum enum_alter_inplace_result { */ #define HA_BINLOG_ROW_CAPABLE (1ULL << 34) #define HA_BINLOG_STMT_CAPABLE (1ULL << 35) + /* When a multiple key conflict happens in a REPLACE command mysql expects the conflicts to be reported in the ascending order of @@ -287,6 +287,17 @@ enum enum_alter_inplace_result { */ #define HA_BINLOG_FLAGS (HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE) +/* The following are used by Spider */ +#define HA_CAN_FORCE_BULK_UPDATE (1ULL << 50) +#define HA_CAN_FORCE_BULK_DELETE (1ULL << 51) +#define HA_CAN_DIRECT_UPDATE_AND_DELETE (1ULL << 52) + +/* The following is for partition handler */ +#define HA_CAN_MULTISTEP_MERGE (1LL << 53) + +/* calling cmp_ref() on the engine is expensive */ +#define HA_CMP_REF_IS_EXPENSIVE (1ULL << 54) + /* bits in index_flags(index_number) for what you can do with index */ #define HA_READ_NEXT 1 /* TODO really use this flag */ #define HA_READ_PREV 2 /* supports ::index_prev */ @@ -436,6 +447,12 @@ static const uint MYSQL_START_TRANS_OPT_READ_WRITE = 4; #define HA_CHECK_DUP (HA_CHECK_DUP_KEY + HA_CHECK_DUP_UNIQUE) #define HA_CHECK_ALL (~0U) +/* Options for info_push() */ +#define INFO_KIND_UPDATE_FIELDS 101 +#define INFO_KIND_UPDATE_VALUES 102 +#define INFO_KIND_FORCE_LIMIT_BEGIN 103 +#define INFO_KIND_FORCE_LIMIT_END 104 + enum legacy_db_type { /* note these numerical values are fixed and can *not* be changed */ @@ -1443,6 +1460,10 @@ handlerton *ha_default_tmp_handlerton(THD *thd); // MySQL compatibility. Unused. #define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported. +#define HTON_CAN_MERGE (1 <<11) //Merge type table +// Engine needs to access the main connect string in partitions +#define HTON_CAN_READ_CONNECT_STRING_IN_PARTITION (1 <<12) + class Ha_trx_info; struct THD_TRANS @@ -1520,6 +1541,7 @@ struct THD_TRANS bool trans_did_wait() const { return (m_unsafe_rollback_flags & DID_WAIT) != 0; } + bool is_trx_read_write() const; void mark_trans_did_ddl() { m_unsafe_rollback_flags|= DID_DDL; } bool trans_did_ddl() const { return (m_unsafe_rollback_flags & DID_DDL) != 0; @@ -1624,6 +1646,16 @@ private: }; +inline bool THD_TRANS::is_trx_read_write() const +{ + Ha_trx_info *ha_info; + for (ha_info= ha_list; ha_info; ha_info= ha_info->next()) + if (ha_info->is_trx_read_write()) + return TRUE; + return FALSE; +} + + enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, ISO_REPEATABLE_READ, ISO_SERIALIZABLE}; @@ -2714,7 +2746,8 @@ public: /** Length of ref (1-8 or the clustered key length) */ uint ref_length; FT_INFO *ft_handler; - enum {NONE=0, INDEX, RND} inited; + enum init_stat { NONE=0, INDEX, RND }; + init_stat inited, pre_inited; const COND *pushed_cond; /** @@ -2780,6 +2813,11 @@ public: virtual void unbind_psi(); virtual void rebind_psi(); + bool set_top_table_fields; + struct TABLE *top_table; + Field **top_table_field; + uint top_table_fields; + private: /** The lock type set by when calling::ha_external_lock(). This is @@ -2808,13 +2846,15 @@ public: key_used_on_scan(MAX_KEY), active_index(MAX_KEY), keyread(MAX_KEY), ref_length(sizeof(my_off_t)), - ft_handler(0), inited(NONE), + ft_handler(0), inited(NONE), pre_inited(NONE), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), tracker(NULL), pushed_idx_cond(NULL), pushed_idx_cond_keyno(MAX_KEY), auto_inc_intervals_count(0), - m_psi(NULL), m_lock_type(F_UNLCK), ha_share(NULL) + m_psi(NULL), set_top_table_fields(FALSE), top_table(0), + top_table_field(0), top_table_fields(0), + m_lock_type(F_UNLCK), ha_share(NULL) { DBUG_PRINT("info", ("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d", @@ -2834,7 +2874,8 @@ public: } /* ha_ methods: pubilc wrappers for private virtual API */ - int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked); + int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked, + MEM_ROOT *mem_root= 0); int ha_index_init(uint idx, bool sorted) { DBUG_EXECUTE_IF("ha_index_init_fail", return HA_ERR_TABLE_DEF_CHANGED;); @@ -2946,7 +2987,7 @@ public: DBUG_RETURN(ret); } int ha_bulk_update_row(const uchar *old_data, const uchar *new_data, - uint *dup_key_found); + ha_rows *dup_key_found); int ha_delete_all_rows(); int ha_truncate(); int ha_reset_auto_increment(ulonglong value); @@ -3087,6 +3128,7 @@ public: Number of rows in table. It will only be called if (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0 */ + virtual int pre_records() { return 0; } virtual ha_rows records() { return stats.records; } /** Return upper bound of current number of records in the table @@ -3143,7 +3185,7 @@ public: @retval 0 Success @retval >0 Error code */ - virtual int exec_bulk_update(uint *dup_key_found) + virtual int exec_bulk_update(ha_rows *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; @@ -3152,7 +3194,7 @@ public: Perform any needed clean-up, no outstanding updates are there at the moment. */ - virtual void end_bulk_update() { return; } + virtual int end_bulk_update() { return 0; } /** Execute all outstanding deletes and close down the bulk delete. @@ -3164,6 +3206,79 @@ public: DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } + virtual int pre_index_read_map(const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag, + bool use_parallel) + { return 0; } + virtual int pre_index_first(bool use_parallel) + { return 0; } + virtual int pre_index_last(bool use_parallel) + { return 0; } + virtual int pre_index_read_last_map(const uchar *key, + key_part_map keypart_map, + bool use_parallel) + { return 0; } +/* + virtual int pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, + uint range_count, + bool sorted, HANDLER_BUFFER *buffer, + bool use_parallel); +*/ + virtual int pre_multi_range_read_next(bool use_parallel) + { return 0; } + virtual int pre_read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted, + bool use_parallel) + { return 0; } + virtual int pre_ft_read(bool use_parallel) + { return 0; } + virtual int pre_rnd_next(bool use_parallel) + { return 0; } + int ha_pre_rnd_init(bool scan) + { + int result; + DBUG_ENTER("ha_pre_rnd_init"); + DBUG_ASSERT(pre_inited==NONE || (pre_inited==RND && scan)); + pre_inited= (result= pre_rnd_init(scan)) ? NONE: RND; + DBUG_RETURN(result); + } + int ha_pre_rnd_end() + { + DBUG_ENTER("ha_pre_rnd_end"); + DBUG_ASSERT(pre_inited==RND); + pre_inited=NONE; + DBUG_RETURN(pre_rnd_end()); + } + virtual int pre_rnd_init(bool scan) { return 0; } + virtual int pre_rnd_end() { return 0; } + virtual int pre_index_init(uint idx, bool sorted) { return 0; } + virtual int pre_index_end() { return 0; } + int ha_pre_index_init(uint idx, bool sorted) + { + int result; + DBUG_ENTER("ha_pre_index_init"); + DBUG_ASSERT(pre_inited==NONE); + if (!(result= pre_index_init(idx, sorted))) + pre_inited=INDEX; + DBUG_RETURN(result); + } + int ha_pre_index_end() + { + DBUG_ENTER("ha_pre_index_end"); + DBUG_ASSERT(pre_inited==INDEX); + pre_inited=NONE; + DBUG_RETURN(pre_index_end()); + } + int ha_pre_index_or_rnd_end() + { + return (pre_inited == INDEX ? + ha_pre_index_end() : + pre_inited == RND ? ha_pre_rnd_end() : 0 ); + } + /** @brief Positions an index cursor to the index specified in the @@ -3272,7 +3387,9 @@ public: int compare_key(key_range *range); int compare_key2(key_range *range) const; virtual int ft_init() { return HA_ERR_WRONG_COMMAND; } - void ft_end() { ft_handler=NULL; } + virtual int pre_ft_init() { return HA_ERR_WRONG_COMMAND; } + virtual void ft_end() {} + virtual int pre_ft_end() { return 0; } virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key) { return NULL; } private: @@ -3295,6 +3412,7 @@ public: /* Same as above, but with statistics */ inline int ha_ft_read(uchar *buf); + inline void ha_ft_end() { ft_end(); ft_handler=NULL; } int ha_rnd_next(uchar *buf); int ha_rnd_pos(uchar *buf, uchar *pos); inline int ha_rnd_pos_by_record(uchar *buf); @@ -3352,6 +3470,8 @@ public: virtual void try_semi_consistent_read(bool) {} virtual void unlock_row() {} virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;} + virtual bool need_info_for_auto_inc() { return 0; } + virtual bool can_use_for_auto_inc_init() { return 1; } virtual void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, @@ -3459,6 +3579,7 @@ public: return 0; } virtual void set_part_info(partition_info *part_info) {return;} + virtual void return_record_by_parent() { return; } virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0; @@ -3663,6 +3784,41 @@ public: virtual void cond_pop() { return; }; /** + Push metadata for the current operation down to the table handler. + */ + virtual int info_push(uint info_type, void *info) { return 0; }; + + /** + This function is used to get correlating of a parent (table/column) + and children (table/column). When conditions are pushed down to child + table (like child of myisam_merge), child table needs to know about + which table/column is my parent for understanding conditions. + */ + virtual int set_top_table_and_fields(TABLE *top_table, + Field **top_table_field, + uint top_table_fields) + { + if (!set_top_table_fields) + { + set_top_table_fields= TRUE; + this->top_table= top_table; + this->top_table_field= top_table_field; + this->top_table_fields= top_table_fields; + } + return 0; + } + virtual void clear_top_table_fields() + { + if (set_top_table_fields) + { + set_top_table_fields= FALSE; + top_table= NULL; + top_table_field= NULL; + top_table_fields= 0; + } + } + + /** Push down an index condition to the handler. The server will use this method to push down a condition it wants @@ -3695,6 +3851,10 @@ public: pushed_idx_cond_keyno= MAX_KEY; in_range_check_pushed_down= false; } + + /* Needed for partition / spider */ + virtual TABLE_LIST *get_next_global_for_child() { return NULL; } + /** Part of old, deprecated in-place ALTER API. */ @@ -4078,6 +4238,49 @@ private: { return HA_ERR_WRONG_COMMAND; } + + /* Perform initialization for a direct update request */ +public: + int ha_direct_update_rows(ha_rows *update_rows); + virtual int direct_update_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } +private: + virtual int pre_direct_update_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } + virtual int direct_update_rows(ha_rows *update_rows __attribute__((unused))) + { + return HA_ERR_WRONG_COMMAND; + } + virtual int pre_direct_update_rows() + { + return HA_ERR_WRONG_COMMAND; + } + + /* Perform initialization for a direct delete request */ +public: + int ha_direct_delete_rows(ha_rows *delete_rows); + virtual int direct_delete_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } +private: + virtual int pre_direct_delete_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } + virtual int direct_delete_rows(ha_rows *delete_rows __attribute__((unused))) + { + return HA_ERR_WRONG_COMMAND; + } + virtual int pre_direct_delete_rows() + { + return HA_ERR_WRONG_COMMAND; + } + /** Reset state of file to after 'open'. This function is called after every statement for all tables used @@ -4152,7 +4355,7 @@ public: @retval 1 Bulk delete not used, normal operation used */ virtual int bulk_update_row(const uchar *old_data, const uchar *new_data, - uint *dup_key_found) + ha_rows *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; @@ -4361,6 +4564,13 @@ public: void sort(); void remove_duplicates(); // assumes that the list is sorted +#ifndef DBUG_OFF + /* + Used to find unstable mtr tests querying + INFORMATION_SCHEMA.TABLES without ORDER BY. + */ + void sort_desc(); +#endif }; int ha_discover_table(THD *thd, TABLE_SHARE *share); diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc index fc89bb83a9d..641d1a20f73 100644 --- a/sql/hash_filo.cc +++ b/sql/hash_filo.cc @@ -23,7 +23,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "hash_filo.h" diff --git a/sql/hostname.cc b/sql/hostname.cc index e8d6780d095..0e60dde893c 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -24,7 +24,7 @@ Hostnames are checked with reverse name lookup and checked that they doesn't resemble an IP address. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" // SPECIAL_NO_HOST_CACHE #include "hostname.h" diff --git a/sql/hostname.h b/sql/hostname.h index d6137b7c260..3e1669b29ed 100644 --- a/sql/hostname.h +++ b/sql/hostname.h @@ -16,7 +16,6 @@ #ifndef HOSTNAME_INCLUDED #define HOSTNAME_INCLUDED -#include "my_global.h" /* uint */ #include "my_net.h" #include "hash_filo.h" diff --git a/sql/init.cc b/sql/init.cc index 8001e60b65e..0d00e3cf846 100644 --- a/sql/init.cc +++ b/sql/init.cc @@ -21,10 +21,9 @@ Init and dummy functions for interface with unireg */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "init.h" -#include "my_sys.h" #include "mysqld.h" // abort_loop, ... #include "my_time.h" // my_init_time #include "unireg.h" // SPECIAL_SAME_DB_NAME diff --git a/sql/init.h b/sql/init.h index af2621e5e70..e8dec0c1e2e 100644 --- a/sql/init.h +++ b/sql/init.h @@ -16,8 +16,6 @@ #ifndef INIT_INCLUDED #define INIT_INCLUDED -#include "my_global.h" /* ulong */ - void unireg_init(ulong options); ATTRIBUTE_NORETURN void unireg_end(void); diff --git a/sql/item.cc b/sql/item.cc index a753913a537..ddc0813933a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -19,7 +19,7 @@ #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include <mysql.h> #include <m_ctype.h> @@ -78,6 +78,12 @@ inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select) select->nest_level - 1); } + +MEM_ROOT *get_thd_memroot(THD *thd) +{ + return thd->mem_root; +} + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -1260,8 +1266,7 @@ bool Item::eq(const Item *item, bool binary_cmp) const type() can be only among basic constant types. */ return type() == item->type() && name.str && item->name.str && - name.length == item->name.length && - !my_strcasecmp(system_charset_info, name.str, item->name.str); + !lex_string_cmp(system_charset_info, &name, &item->name); } @@ -1270,7 +1275,7 @@ Item *Item::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) if (!needs_charset_converter(tocs)) return this; Item_func_conv_charset *conv= new (thd->mem_root) Item_func_conv_charset(thd, this, tocs, 1); - return conv->safe ? conv : NULL; + return conv && conv->safe ? conv : NULL; } @@ -1823,7 +1828,7 @@ Item_splocal::this_item_addr(THD *thd, Item **) void Item_splocal::print(String *str, enum_query_type) { str->reserve(m_name.length+8); - str->append(m_name.str, m_name.length); + str->append(&m_name); str->append('@'); str->qs_append(m_var_idx); } @@ -1948,9 +1953,9 @@ Item_splocal_row_field::this_item_addr(THD *thd, Item **) void Item_splocal_row_field::print(String *str, enum_query_type) { str->reserve(m_name.length + m_field_name.length + 8); - str->append(m_name.str, m_name.length); + str->append(&m_name); str->append('.'); - str->append(m_field_name.str, m_field_name.length); + str->append(&m_field_name); str->append('@'); str->qs_append(m_var_idx); str->append('['); @@ -1986,13 +1991,13 @@ void Item_splocal_row_field_by_name::print(String *str, enum_query_type) // +16 should be enough for .NNN@[""] if (str->reserve(m_name.length + 2 * m_field_name.length + 16)) return; - str->qs_append(m_name.str, m_name.length); + str->qs_append(&m_name); str->qs_append('.'); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append('@'); str->qs_append(m_var_idx); str->qs_append("[\"", 2); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append("\"]", 2); } @@ -2711,15 +2716,15 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll, 0 if an error occured */ -Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) +Item* Item_func_or_sum::build_clone(THD *thd) { - Item_func_or_sum *copy= (Item_func_or_sum *) get_copy(thd, mem_root); + Item_func_or_sum *copy= (Item_func_or_sum *) get_copy(thd); if (!copy) return 0; if (arg_count > 2) { copy->args= - (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count); + (Item**) alloc_root(thd->mem_root, sizeof(Item*) * arg_count); if (!copy->args) return 0; } @@ -2729,7 +2734,7 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) for (uint i= 0; i < arg_count; i++) { - Item *arg_clone= args[i]->build_clone(thd, mem_root); + Item *arg_clone= args[i]->build_clone(thd); if (!arg_clone) return 0; copy->args[i]= arg_clone; @@ -2737,6 +2742,252 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) return copy; } +Item_sp::Item_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg) : + context(context_arg), m_name(name_arg), m_sp(NULL), func_ctx(NULL), + sp_result_field(NULL) +{ + dummy_table= (TABLE*) thd->calloc(sizeof(TABLE) + sizeof(TABLE_SHARE) + + sizeof(Query_arena)); + dummy_table->s= (TABLE_SHARE*) (dummy_table + 1); + /* TODO(cvicentiu) Move this sp_query_arena in the class as a direct member. + Currently it can not be done due to header include dependencies. */ + sp_query_arena= (Query_arena *) (dummy_table->s + 1); + memset(&sp_mem_root, 0, sizeof(sp_mem_root)); +} + +const char * +Item_sp::func_name(THD *thd) const +{ + /* Calculate length to avoid reallocation of string for sure */ + uint len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) + + m_name->m_name.length)*2 + //characters*quoting + 2 + // ` and ` + (m_name->m_explicit_name ? + 3 : 0) + // '`', '`' and '.' for the db + 1 + // end of string + ALIGN_SIZE(1)); // to avoid String reallocation + String qname((char *)alloc_root(thd->mem_root, len), len, + system_charset_info); + + qname.length(0); + if (m_name->m_explicit_name) + { + append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length); + qname.append('.'); + } + append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length); + return qname.c_ptr_safe(); +} + +void +Item_sp::cleanup() +{ + delete sp_result_field; + sp_result_field= NULL; + m_sp= NULL; + delete func_ctx; + func_ctx= NULL; + free_root(&sp_mem_root, MYF(0)); + dummy_table->alias.free(); +} + +/** + @brief Checks if requested access to function can be granted to user. + If function isn't found yet, it searches function first. + If function can't be found or user don't have requested access + error is raised. + + @param thd thread handler + + @return Indication if the access was granted or not. + @retval FALSE Access is granted. + @retval TRUE Requested access can't be granted or function doesn't exists. + +*/ +bool +Item_sp::sp_check_access(THD *thd) +{ + DBUG_ENTER("Item_sp::sp_check_access"); + DBUG_ASSERT(m_sp); + DBUG_RETURN(m_sp->check_execute_access(thd)); +} + +/** + @brief Execute function & store value in field. + + @return Function returns error status. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ +bool Item_sp::execute(THD *thd, bool *null_value, Item **args, uint arg_count) +{ + if (execute_impl(thd, args, arg_count)) + { + *null_value= 1; + context->process_error(thd); + if (thd->killed) + thd->send_kill_message(); + return true; + } + + /* Check that the field (the value) is not NULL. */ + + *null_value= sp_result_field->is_null(); + return (*null_value); +} + +/** + @brief Execute function and store the return value in the field. + + @note This function was intended to be the concrete implementation of + the interface function execute. This was never realized. + + @return The error state. + @retval FALSE on success + @retval TRUE if an error occurred. +*/ +bool +Item_sp::execute_impl(THD *thd, Item **args, uint arg_count) +{ + Sub_statement_state statement_state; + Security_context *save_security_ctx= thd->security_ctx; + enum enum_sp_data_access access= + (m_sp->daccess() == SP_DEFAULT_ACCESS) ? + SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess(); + + DBUG_ENTER("Item_sp::execute_impl"); + + if (context->security_ctx) + { + /* Set view definer security context */ + thd->security_ctx= context->security_ctx; + } + + if (sp_check_access(thd)) + { + thd->security_ctx= save_security_ctx; + DBUG_RETURN(TRUE); + } + + /* + Throw an error if a non-deterministic function is called while + statement-based replication (SBR) is active. + */ + + if (!m_sp->detistic() && !trust_function_creators && + (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) && + (mysql_bin_log.is_open() && + thd->variables.binlog_format == BINLOG_FORMAT_STMT)) + { + my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0)); + thd->security_ctx= save_security_ctx; + DBUG_RETURN(TRUE); + } + + /* + Disable the binlogging if this is not a SELECT statement. If this is a + SELECT, leave binlogging on, so execute_function() code writes the + function call into binlog. + */ + thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); + + /* + If this function is an aggregate function, we want to initialise the + mem_root only once per group. For a regular stored function, we will + initialise once for each call to execute_function. + */ + m_sp->agg_type(); + DBUG_ASSERT(m_sp->agg_type() == GROUP_AGGREGATE || + (m_sp->agg_type() == NOT_AGGREGATE && !func_ctx)); + if (!func_ctx) + { + init_sql_alloc(&sp_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); + *sp_query_arena= Query_arena(&sp_mem_root, + Query_arena::STMT_INITIALIZED_FOR_SP); + } + + bool err_status= m_sp->execute_function(thd, args, arg_count, + sp_result_field, &func_ctx, + sp_query_arena); + /* + We free the function context when the function finished executing normally + (quit_func == TRUE) or the function has exited with an error. + */ + if (err_status || func_ctx->quit_func) + { + /* Free Items allocated during function execution. */ + delete func_ctx; + func_ctx= NULL; + sp_query_arena->free_items(); + free_root(&sp_mem_root, MYF(0)); + memset(&sp_mem_root, 0, sizeof(sp_mem_root)); + } + thd->restore_sub_statement_state(&statement_state); + + thd->security_ctx= save_security_ctx; + DBUG_RETURN(err_status); +} + + +/** + @brief Initialize the result field by creating a temporary dummy table + and assign it to a newly created field object. Meta data used to + create the field is fetched from the sp_head belonging to the stored + proceedure found in the stored procedure functon cache. + + @note This function should be called from fix_fields to init the result + field. It is some what related to Item_field. + + @see Item_field + + @param thd A pointer to the session and thread context. + + @return Function return error status. + @retval TRUE is returned on an error + @retval FALSE is returned on success. +*/ + +bool +Item_sp::init_result_field(THD *thd, uint max_length, uint maybe_null, + bool *null_value, LEX_CSTRING *name) +{ + DBUG_ENTER("Item_sp::init_result_field"); + + DBUG_ASSERT(m_sp != NULL); + DBUG_ASSERT(sp_result_field == NULL); + + /* + A Field needs to be attached to a Table. + Below we "create" a dummy table by initializing + the needed pointers. + */ + dummy_table->alias.set("", 0, table_alias_charset); + dummy_table->in_use= thd; + dummy_table->copy_blobs= TRUE; + dummy_table->s->table_cache_key= empty_clex_str; + dummy_table->s->table_name= empty_clex_str; + dummy_table->maybe_null= maybe_null; + + if (!(sp_result_field= m_sp->create_result_field(max_length, name, + dummy_table))) + DBUG_RETURN(TRUE); + + if (sp_result_field->pack_length() > sizeof(result_buf)) + { + void *tmp; + if (!(tmp= thd->alloc(sp_result_field->pack_length()))) + DBUG_RETURN(TRUE); + sp_result_field->move_field((uchar*) tmp); + } + else + sp_result_field->move_field(result_buf); + + sp_result_field->null_ptr= (uchar *) null_value; + sp_result_field->null_bit= 1; + + DBUG_RETURN(FALSE); +} /** @brief @@ -2754,19 +3005,13 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) 0 if an error occured */ -Item* Item_ref::build_clone(THD *thd, MEM_ROOT *mem_root) +Item* Item_ref::build_clone(THD *thd) { - Item_ref *copy= (Item_ref *) get_copy(thd, mem_root); - if (!copy) + Item_ref *copy= (Item_ref *) get_copy(thd); + if (!copy || + !(copy->ref= (Item**) alloc_root(thd->mem_root, sizeof(Item*))) || + !(*copy->ref= (* ref)->build_clone(thd))) return 0; - copy->ref= - (Item**) alloc_root(mem_root, sizeof(Item*)); - if (!copy->ref) - return 0; - Item *item_clone= (* ref)->build_clone(thd, mem_root); - if (!item_clone) - return 0; - *copy->ref= item_clone; return copy; } @@ -3225,8 +3470,8 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const (In cases where we would choose wrong we would have to generate a ER_NON_UNIQ_ERROR). */ - return (!my_strcasecmp(system_charset_info, item_field->name.str, - field_name.str) && + return (!lex_string_cmp(system_charset_info, &item_field->name, + &field_name) && (!item_field->table_name || !table_name || (!my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && @@ -3248,6 +3493,11 @@ table_map Item_field::all_used_tables() const return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map); } + +/* + @Note thd->fatal_error can be set in case of OOM +*/ + void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge) { @@ -3307,6 +3557,8 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref, } Name_resolution_context *ctx= new Name_resolution_context(); + if (!ctx) + return; // Fatal error set if (context->select_lex == new_parent) { /* @@ -5173,8 +5425,8 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) /* SELECT list element with explicit alias */ if ((*(cur_group->item))->name.str && !table_name && !(*(cur_group->item))->is_autogenerated_name && - !my_strcasecmp(system_charset_info, - (*(cur_group->item))->name.str, field_name->str)) + !lex_string_cmp(system_charset_info, + &(*(cur_group->item))->name, field_name)) { ++cur_match_degree; } @@ -5189,8 +5441,8 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) DBUG_ASSERT(l_field_name->str != 0); - if (!my_strcasecmp(system_charset_info, - l_field_name->str, field_name->str)) + if (!lex_string_cmp(system_charset_info, + l_field_name, field_name)) ++cur_match_degree; else continue; @@ -5794,6 +6046,13 @@ bool Item_field::fix_fields(THD *thd, Item **reference) DBUG_ASSERT(fixed == 0); Field *from_field= (Field *)not_found_field; bool outer_fixed= false; + + if (thd->lex->current_select->in_tvc) + { + my_error(ER_FIELD_REFERENCE_IN_TVC, MYF(0), + full_name(), thd->where); + return(1); + } if (!field) // If field is not checked { @@ -7330,7 +7589,7 @@ Item *Item_field::derived_field_transformer_for_where(THD *thd, uchar *arg) st_select_lex *sel= (st_select_lex *)arg; Item *producing_item= find_producing_item(this, sel); if (producing_item) - return producing_item->build_clone(thd, thd->mem_root); + return producing_item->build_clone(thd); return this; } @@ -7342,7 +7601,7 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd, st_select_lex *sel= (st_select_lex *)arg; Item *producing_item= find_producing_item(this, sel); DBUG_ASSERT (producing_item != NULL); - return producing_item->build_clone(thd, thd->mem_root); + return producing_item->build_clone(thd); } return this; } @@ -7388,7 +7647,7 @@ Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd, st_select_lex *sel= (st_select_lex *)arg; Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); if (gr_field) - return gr_field->producing_item->build_clone(thd, thd->mem_root); + return gr_field->producing_item->build_clone(thd); return this; } @@ -7401,7 +7660,7 @@ Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd, return this; st_select_lex *sel= (st_select_lex *)arg; Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); - return gr_field->producing_item->build_clone(thd, thd->mem_root); + return gr_field->producing_item->build_clone(thd); } void Item_field::print(String *str, enum_query_type query_type) @@ -8962,7 +9221,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) goto error; memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of()); - IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED + IF_DBUG_ASSERT(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED if (def_field->default_value && def_field->default_value->flags) { uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length()); @@ -9211,7 +9470,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) void Item_insert_value::print(String *str, enum_query_type query_type) { - str->append(STRING_WITH_LEN("values(")); + str->append(STRING_WITH_LEN("value(")); arg->print(str, query_type); str->append(')'); } @@ -9265,8 +9524,8 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const { return item->type() == TRIGGER_FIELD_ITEM && row_version == ((Item_trigger_field *)item)->row_version && - !my_strcasecmp(system_charset_info, field_name.str, - ((Item_trigger_field *)item)->field_name.str); + !lex_string_cmp(system_charset_info, &field_name, + &((Item_trigger_field *)item)->field_name); } diff --git a/sql/item.h b/sql/item.h index 46a65b31b38..fb7edfd3d1a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -128,8 +128,6 @@ enum precedence { HIGHEST_PRECEDENCE }; -typedef Bounds_checked_array<Item*> Ref_ptr_array; - bool mark_unsupported_function(const char *where, void *store, uint result); /* convenience helper for mark_unsupported_function() above */ @@ -1196,7 +1194,7 @@ public: virtual bool basic_const_item() const { return 0; } /* cloning of constant items (0 if it is not const) */ virtual Item *clone_item(THD *thd) { return 0; } - virtual Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return get_copy(thd, mem_root); } + virtual Item* build_clone(THD *thd) { return get_copy(thd); } virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} @@ -1666,7 +1664,7 @@ public: virtual bool set_fields_as_dependent_processor(void *arg) { return 0; } /*============== End of Item processor list ======================*/ - virtual Item *get_copy(THD *thd, MEM_ROOT *mem_root)=0; + virtual Item *get_copy(THD *thd)=0; bool cache_const_expr_analyzer(uchar **arg); Item* cache_const_expr_transformer(THD *thd, uchar *arg); @@ -1756,6 +1754,8 @@ public: virtual Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg) { return this; } + virtual Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg) + { return this; } virtual bool expr_cache_is_needed(THD *) { return FALSE; } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const @@ -1937,12 +1937,14 @@ public: } }; +MEM_ROOT *get_thd_memroot(THD *thd); template <class T> -inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item) +inline Item* get_item_copy (THD *thd, T* item) { - Item *copy= new (mem_root) T(*item); - copy->register_in(thd); + Item *copy= new (get_thd_memroot(thd)) T(*item); + if (copy) + copy->register_in(thd); return copy; } @@ -2281,7 +2283,7 @@ public: LEX_CSTRING m_name; public: -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS /* Routine to which this Item_splocal belongs. Used for checking if correct runtime context is used for variable handling. @@ -2389,7 +2391,7 @@ public: bool append_for_log(THD *thd, String *str); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } /* Override the inherited create_field_for_create_select(), @@ -2517,7 +2519,7 @@ public: purposes. */ virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } private: uint m_case_expr_id; @@ -2587,8 +2589,8 @@ public: { return mark_unsupported_function("name_const()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_name_const>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_name_const>(thd, this); } }; class Item_num: public Item_basic_constant @@ -2724,8 +2726,8 @@ public: const Type_handler *handler= field->type_handler(); return handler->type_handler_for_item_field(); } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_ident_for_show>(thd, mem_root, this); } + Item* get_copy(THD *thd) + { return get_item_copy<Item_ident_for_show>(thd, this); } }; @@ -2909,8 +2911,8 @@ public: bool cleanup_excluding_const_fields_processor(void *arg) { return field && const_item() ? 0 : cleanup_processor(arg); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_field>(thd, this); } bool is_outer_field() const { DBUG_ASSERT(fixed); @@ -2938,8 +2940,8 @@ public: :Item_field(thd, field), Item_args() { } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_field_row>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_field_row>(thd, this); } const Type_handler *type_handler() const { return &type_handler_row; } uint cols() const { return arg_count; } @@ -3034,8 +3036,8 @@ public: Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); bool check_partition_func_processor(void *int_arg) {return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_null>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_null>(thd, this); } }; class Item_null_result :public Item_null @@ -3419,7 +3421,7 @@ public: bool append_for_log(THD *thd, String *str); bool check_vcol_func_processor(void *int_arg) {return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } private: void invalid_default_param() const; @@ -3483,8 +3485,8 @@ public: { return (uint) (max_length - MY_TEST(value < 0)); } bool eq(const Item *item, bool binary_cmp) const { return int_eq(value, item); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_int>(thd, this); } }; @@ -3516,8 +3518,8 @@ public: virtual void print(String *str, enum_query_type query_type); Item *neg(THD *thd); uint decimal_precision() const { return max_length; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_uint>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_uint>(thd, this); } }; @@ -3563,8 +3565,8 @@ public: uint decimal_precision() const { return decimal_value.precision(); } bool eq(const Item *, bool binary_cmp) const; void set_decimal_value(my_decimal *value_par); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_decimal>(thd, this); } }; @@ -3614,8 +3616,8 @@ public: virtual void print(String *str, enum_query_type query_type); bool eq(const Item *item, bool binary_cmp) const { return real_eq(value, item); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_float>(thd, this); } }; @@ -3805,8 +3807,8 @@ public: return MYSQL_TYPE_STRING; // Not a temporal literal } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_string>(thd, this); } }; @@ -4060,8 +4062,8 @@ public: return &type_handler_longlong; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_hex_hybrid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_hex_hybrid>(thd, this); } }; @@ -4101,8 +4103,8 @@ public: collation.collation); } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_hex_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_hex_string>(thd, this); } }; @@ -4182,8 +4184,8 @@ public: void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_date_literal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_date_literal>(thd, this); } }; @@ -4203,8 +4205,8 @@ public: void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_time_literal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_time_literal>(thd, this); } }; @@ -4226,8 +4228,8 @@ public: void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_datetime_literal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_datetime_literal>(thd, this); } }; @@ -4466,9 +4468,38 @@ public: virtual void fix_length_and_dec()= 0; bool const_item() const { return const_item_cache; } table_map used_tables() const { return used_tables_cache; } - Item* build_clone(THD *thd, MEM_ROOT *mem_root); + Item* build_clone(THD *thd); }; +class sp_head; +class sp_name; +struct st_sp_security_context; + +class Item_sp +{ +public: + Name_resolution_context *context; + sp_name *m_name; + sp_head *m_sp; + TABLE *dummy_table; + uchar result_buf[64]; + sp_rcontext *func_ctx; + MEM_ROOT sp_mem_root; + Query_arena *sp_query_arena; + + /* + The result field of the stored function. + */ + Field *sp_result_field; + Item_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg); + const char *func_name(THD *thd) const; + void cleanup(); + bool sp_check_access(THD *thd); + bool execute(THD *thd, bool *null_value, Item **args, uint arg_count); + bool execute_impl(THD *thd, Item **args, uint arg_count); + bool init_result_field(THD *thd, uint max_length, uint maybe_null, + bool *null_value, LEX_CSTRING *name); +}; class Item_ref :public Item_ident { @@ -4649,7 +4680,7 @@ public: return (*ref)->is_outer_field(); } - Item* build_clone(THD *thd, MEM_ROOT *mem_root); + Item* build_clone(THD *thd); /** Checks if the item tree that ref points to contains a subquery. @@ -4658,8 +4689,8 @@ public: { return (*ref)->with_subquery(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_ref>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_ref>(thd, this); } bool excl_dep_on_table(table_map tab_map) { table_map used= used_tables(); @@ -4727,8 +4758,8 @@ public: bool is_null(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); virtual Ref_Type ref_type() { return DIRECT_REF; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_direct_ref>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_direct_ref>(thd, this); } }; @@ -4893,9 +4924,9 @@ public: { return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_wrapper>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_wrapper>(thd, this); } + Item *build_clone(THD *thd) { return 0; } }; @@ -5063,8 +5094,8 @@ public: item_equal= NULL; Item_direct_ref::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_direct_view_ref>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_direct_view_ref>(thd, this); } }; @@ -5157,8 +5188,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); virtual void print(String *str, enum_query_type query_type); table_map used_tables() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_ref_null_helper>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_ref_null_helper>(thd, this); } }; /* @@ -5320,8 +5351,8 @@ public: longlong val_int(); void copy(); int save_in_field(Field *field, bool no_conversions); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_string>(thd, this); } }; @@ -5344,8 +5375,8 @@ public: return null_value ? 0 : cached_value; } virtual void copy(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_int>(thd, this); } }; @@ -5362,8 +5393,8 @@ public: { return null_value ? 0.0 : (double) (ulonglong) cached_value; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_uint>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_uint>(thd, this); } }; @@ -5390,8 +5421,8 @@ public: cached_value= item->val_real(); null_value= item->null_value; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_float>(thd, this); } }; @@ -5411,8 +5442,8 @@ public: double val_real(); longlong val_int(); void copy(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_decimal>(thd, this); } }; @@ -5649,7 +5680,7 @@ public: bool update_vcol_processor(void *arg) { return 0; } bool check_vcol_func_processor(void *arg) { - return mark_unsupported_function("values()", arg, VCOL_IMPOSSIBLE); + return mark_unsupported_function("value()", arg, VCOL_IMPOSSIBLE); } }; @@ -5846,6 +5877,7 @@ public: } virtual void store(Item *item); + virtual Item *get_item() { return example; } virtual bool cache_value()= 0; bool basic_const_item() const { return MY_TEST(example && example->basic_const_item()); } @@ -5906,8 +5938,8 @@ public: bool cache_value(); int save_in_field(Field *field, bool no_conversions); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_int>(thd, this); } }; @@ -5942,8 +5974,8 @@ public: Item_cache_time(THD *thd) :Item_cache_temporal(thd, &type_handler_time2) { } bool cache_value(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_time>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_time>(thd, this); } }; @@ -5952,8 +5984,8 @@ class Item_cache_datetime: public Item_cache_temporal public: Item_cache_datetime(THD *thd) :Item_cache_temporal(thd, &type_handler_datetime2) { } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_datetime>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_datetime>(thd, this); } }; @@ -5962,8 +5994,8 @@ class Item_cache_date: public Item_cache_temporal public: Item_cache_date(THD *thd) :Item_cache_temporal(thd, &type_handler_newdate) { } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_date>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_date>(thd, this); } }; @@ -5980,8 +6012,8 @@ public: my_decimal *val_decimal(my_decimal *); bool cache_value(); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_real>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_real>(thd, this); } }; @@ -5998,8 +6030,8 @@ public: my_decimal *val_decimal(my_decimal *); bool cache_value(); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_decimal>(thd, this); } }; @@ -6026,8 +6058,8 @@ public: int save_in_field(Field *field, bool no_conversions); bool cache_value(); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_str>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_str>(thd, this); } }; @@ -6051,8 +6083,8 @@ public: */ return Item::safe_charset_converter(thd, tocs); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_str_for_nullif>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_str_for_nullif>(thd, this); } }; @@ -6122,8 +6154,8 @@ public: } bool cache_value(); virtual void set_null(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_row>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_row>(thd, this); } }; @@ -6194,7 +6226,7 @@ public: { Type_geometry_attributes::set_geometry_type(type); } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* get_copy(THD *thd) { return 0; } }; diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 488eb52fb77..4d03462d7c3 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -22,7 +22,7 @@ Buffers to save and compare item values */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 313bd457313..00ee42ac487 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include <m_ctype.h> #include "sql_select.h" @@ -4440,6 +4440,26 @@ longlong Item_func_in::val_int() } +void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding) +{ + THD *thd= current_thd; + + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (to_be_transformed_into_in_subq(thd)) + { + transform_into_subq= true; + thd->lex->current_select->in_funcs.push_back(this, thd->mem_root); + } + + if (arena) + thd->restore_active_arena(arena, &backup); + + emb_on_expr_nest= embedding; +} + + longlong Item_func_bit_or::val_int() { DBUG_ASSERT(fixed == 1); @@ -4930,20 +4950,20 @@ void Item_cond::neg_arguments(THD *thd) 0 if an error occured */ -Item *Item_cond::build_clone(THD *thd, MEM_ROOT *mem_root) +Item *Item_cond::build_clone(THD *thd) { List_iterator_fast<Item> li(list); Item *item; - Item_cond *copy= (Item_cond *) get_copy(thd, mem_root); + Item_cond *copy= (Item_cond *) get_copy(thd); if (!copy) return 0; copy->list.empty(); while ((item= li++)) { - Item *arg_clone= item->build_clone(thd, mem_root); + Item *arg_clone= item->build_clone(thd); if (!arg_clone) return 0; - if (copy->list.push_back(arg_clone, mem_root)) + if (copy->list.push_back(arg_clone, thd->mem_root)) return 0; } return copy; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index b4a833d2290..8b47f09497f 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -268,8 +268,8 @@ public: Item_func_istrue(THD *thd, Item *a): Item_func_truth(thd, a, true, true) {} ~Item_func_istrue() {} virtual const char* func_name() const { return "istrue"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_istrue>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_istrue>(thd, this); } }; @@ -284,8 +284,8 @@ public: Item_func_truth(thd, a, true, false) {} ~Item_func_isnottrue() {} virtual const char* func_name() const { return "isnottrue"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnottrue>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnottrue>(thd, this); } }; @@ -299,8 +299,8 @@ public: Item_func_isfalse(THD *thd, Item *a): Item_func_truth(thd, a, false, true) {} ~Item_func_isfalse() {} virtual const char* func_name() const { return "isfalse"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isfalse>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isfalse>(thd, this); } }; @@ -315,8 +315,8 @@ public: Item_func_truth(thd, a, false, false) {} ~Item_func_isnotfalse() {} virtual const char* func_name() const { return "isnotfalse"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnotfalse>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnotfalse>(thd, this); } }; @@ -378,8 +378,8 @@ public: void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); bool invisible_mode(); void reset_cache() { cache= NULL; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_in_optimizer>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_in_optimizer>(thd, this); } }; @@ -539,10 +539,10 @@ public: return add_key_fields_optimize_op(join, key_fields, and_level, usable_tables, sargables, false); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *build_clone(THD *thd) { Item_bool_rowready_func2 *clone= - (Item_bool_rowready_func2 *) Item_func::build_clone(thd, mem_root); + (Item_bool_rowready_func2 *) Item_func::build_clone(thd); if (clone) { clone->cmp.comparators= 0; @@ -573,8 +573,8 @@ public: Item_args::propagate_equal_fields(thd, Context_boolean(), cond); return this; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xor>(thd, this); } }; class Item_func_not :public Item_bool_func @@ -592,8 +592,8 @@ public: Item *neg_transformer(THD *thd); bool fix_fields(THD *, Item **); virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_not>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_not>(thd, this); } }; class Item_maxmin_subselect; @@ -641,8 +641,8 @@ public: void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, SARGABLE_PARAM **sargables); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_trig_cond>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_trig_cond>(thd, this); } }; class Item_func_not_all :public Item_func_not @@ -679,8 +679,8 @@ public: longlong val_int(); const char *func_name() const { return "<nop>"; } Item *neg_transformer(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nop_all>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nop_all>(thd, this); } }; @@ -720,8 +720,8 @@ public: uint in_equality_no; virtual uint exists2in_reserved_items() { return 1; }; friend class Arg_comparator; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_eq>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_eq>(thd, this); } }; class Item_func_equal :public Item_bool_rowready_func2 @@ -744,8 +744,8 @@ public: return add_key_fields_optimize_op(join, key_fields, and_level, usable_tables, sargables, true); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_equal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_equal>(thd, this); } }; @@ -760,8 +760,8 @@ public: cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return ">="; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ge>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ge>(thd, this); } }; @@ -776,8 +776,8 @@ public: cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return ">"; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_gt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_gt>(thd, this); } }; @@ -792,8 +792,8 @@ public: cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<="; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_le>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_le>(thd, this); } }; @@ -808,8 +808,8 @@ public: cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return "<"; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lt>(thd, this); } }; @@ -833,8 +833,8 @@ public: Item *negated_item(THD *thd); void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, SARGABLE_PARAM **sargables); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ne>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ne>(thd, this); } }; @@ -923,8 +923,8 @@ public: cond); return this; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_between>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_between>(thd, this); } longlong val_int_cmp_string(); longlong val_int_cmp_temporal(); @@ -951,8 +951,8 @@ public: agg_arg_charsets_for_comparison(cmp_collation, args, 2); fix_char_length(2); // returns "1" or "0" or "-1" } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_strcmp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_strcmp>(thd, this); } }; @@ -986,8 +986,8 @@ public: str->append(func_name()); print_args(str, 0, query_type); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_interval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_interval>(thd, this); } }; @@ -1010,8 +1010,8 @@ public: } const char *func_name() const { return "coalesce"; } table_map not_null_tables() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_coalesce>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_coalesce>(thd, this); } }; @@ -1083,8 +1083,8 @@ public: const char *func_name() const { return "ifnull"; } table_map not_null_tables() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ifnull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ifnull>(thd, this); } }; @@ -1146,8 +1146,8 @@ public: const char *func_name() const { return "if"; } bool eval_not_null_tables(void *opt_arg); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_if>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_if>(thd, this); } private: void cache_type_info(Item *source); }; @@ -1167,8 +1167,8 @@ public: { fix_length_and_dec2_eliminate_null(args + 1); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nvl2>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nvl2>(thd, this); } }; @@ -1254,8 +1254,8 @@ public: cond, &args[2]); return this; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nullif>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nullif>(thd, this); } Item *derived_field_transformer_for_having(THD *thd, uchar *arg) { reset_first_arg_if_needed(); return this; } Item *derived_field_transformer_for_where(THD *thd, uchar *arg) @@ -2115,9 +2115,9 @@ public: enum precedence precedence() const { return BETWEEN_PRECEDENCE; } CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } bool need_parentheses_in_default() { return true; } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *build_clone(THD *thd) { - Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root); + Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd); if (clone) clone->arg_buffer= 0; return clone; @@ -2152,8 +2152,8 @@ public: return this; } Item *find_item(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_case_searched>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_case_searched>(thd, this); } }; @@ -2198,17 +2198,17 @@ public: void fix_length_and_dec(); Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); Item *find_item(); - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *build_clone(THD *thd) { Item_func_case_simple *clone= (Item_func_case_simple *) - Item_func_case::build_clone(thd, mem_root); + Item_func_case::build_clone(thd); uint ncases= when_count(); if (clone && clone->Predicant_to_list_comparator::init_clone(thd, ncases)) return NULL; return clone; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_case_simple>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_case_simple>(thd, this); } }; @@ -2223,8 +2223,8 @@ public: { Item_func::print(str, query_type); } void fix_length_and_dec(); Item *find_item(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_decode_oracle>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_decode_oracle>(thd, this); } }; @@ -2281,6 +2281,7 @@ class Item_func_in :public Item_func_opt_neg, protected: SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Field *field, Item *value); + bool transform_into_subq; public: /// An array of values, created when the bisection lookup method is used in_vector *array; @@ -2297,11 +2298,14 @@ public: */ bool arg_types_compatible; + TABLE_LIST *emb_on_expr_nest; + Item_func_in(THD *thd, List<Item> &list): Item_func_opt_neg(thd, list), Predicant_to_list_comparator(thd, arg_count - 1), + transform_into_subq(false), array(0), have_null(0), - arg_types_compatible(FALSE) + arg_types_compatible(FALSE), emb_on_expr_nest(0) { } longlong val_int(); bool fix_fields(THD *, Item **); @@ -2381,11 +2385,11 @@ public: bool eval_not_null_tables(void *opt_arg); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); bool count_sargable_conds(void *arg); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_in>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_in>(thd, this); } + Item *build_clone(THD *thd) { - Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd, mem_root); + Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd); if (clone) { clone->array= 0; @@ -2393,7 +2397,11 @@ public: return NULL; } return clone; - } + } + void mark_as_condition_AND_part(TABLE_LIST *embedding); + bool to_be_transformed_into_in_subq(THD *thd); + bool create_value_list_for_tvc(THD *thd, List< List<Item> > *values); + Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg); }; class cmp_item_row :public cmp_item @@ -2509,8 +2517,8 @@ public: bool top_level); table_map not_null_tables() const { return 0; } Item *neg_transformer(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnull>(thd, this); } }; /* Functions used by HAVING for rewriting IN subquery */ @@ -2557,8 +2565,8 @@ public: Item *neg_transformer(THD *thd); void print(String *str, enum_query_type query_type); void top_level_item() { abort_on_null=1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnotnull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnotnull>(thd, this); } }; @@ -2699,8 +2707,8 @@ public: bool find_selective_predicates_list_processor(void *arg); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_like>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_like>(thd, this); } }; @@ -2811,11 +2819,11 @@ public: void fix_length_and_dec(); const char *func_name() const { return "regexp"; } enum precedence precedence() const { return CMP_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_regex>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_regex>(thd, this); } + Item *build_clone(THD *thd) { - Item_func_regex *clone= (Item_func_regex*) Item_bool_func::build_clone(thd, mem_root); + Item_func_regex *clone= (Item_func_regex*) Item_bool_func::build_clone(thd); if (clone) clone->re.reset(); return clone; @@ -2860,8 +2868,8 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); const char *func_name() const { return "regexp_instr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_regexp_instr>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_regexp_instr>(thd, this); } }; @@ -2938,7 +2946,7 @@ public: Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t); bool eval_not_null_tables(void *opt_arg); - Item *build_clone(THD *thd, MEM_ROOT *mem_root); + Item *build_clone(THD *thd); }; template <template<class> class LI, class T> class Item_equal_iterator; @@ -3112,7 +3120,7 @@ public: void set_context_field(Item_field *ctx_field) { context_field= ctx_field; } void set_link_equal_fields(bool flag) { link_equal_fields= flag; } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* get_copy(THD *thd) { return 0; } /* This does not comply with the specification of the virtual method, but Item_equal items are processed distinguishly anyway @@ -3266,8 +3274,8 @@ public: void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, SARGABLE_PARAM **sargables); SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cond_and>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cond_and>(thd, this); } }; inline bool is_cond_and(Item *item) @@ -3293,8 +3301,8 @@ public: table_map not_null_tables() const { return and_tables_cache; } Item *copy_andor_structure(THD *thd); Item *neg_transformer(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cond_or>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cond_or>(thd, this); } }; class Item_func_dyncol_check :public Item_bool_func @@ -3304,8 +3312,8 @@ public: longlong val_int(); const char *func_name() const { return "column_check"; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_check>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_check>(thd, this); } }; class Item_func_dyncol_exists :public Item_bool_func @@ -3316,8 +3324,8 @@ public: longlong val_int(); const char *func_name() const { return "column_exists"; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_exists>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_exists>(thd, this); } }; @@ -3345,8 +3353,8 @@ public: :Item_func_cursor_bool_attr(thd, name, offset) { } const char *func_name() const { return "%ISOPEN"; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_isopen>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_isopen>(thd, this); } }; @@ -3357,8 +3365,8 @@ public: :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; } const char *func_name() const { return "%FOUND"; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_found>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_found>(thd, this); } }; @@ -3369,8 +3377,8 @@ public: :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; } const char *func_name() const { return "%NOTFOUND"; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_notfound>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_notfound>(thd, this); } }; diff --git a/sql/item_create.cc b/sql/item_create.cc index bc3ec0266ee..5d6d9742c7a 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -22,7 +22,7 @@ Functions to create an item. Used by sql_yac.yy */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there diff --git a/sql/item_func.cc b/sql/item_func.cc index a6134c009d5..ac7ed75e7f9 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -941,6 +941,7 @@ double Item_func_hybrid_field_type::val_real_from_date_op() ltime.time_type= mysql_timestamp_type(); return TIME_to_double(<ime); } + longlong Item_func_hybrid_field_type::val_int_from_date_op() { MYSQL_TIME ltime; @@ -3259,17 +3260,15 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, func->used_tables_and_const_cache_join(item); f_args.arg_type[i]=item->result_type(); } - //TODO: why all following memory is not allocated with 1 thd->alloc() call? - if (!(buffers=new String[arg_count]) || - !(f_args.args= (char**) thd->alloc(arg_count * sizeof(char *))) || - !(f_args.lengths= (ulong*) thd->alloc(arg_count * sizeof(long))) || - !(f_args.maybe_null= (char*) thd->alloc(arg_count * sizeof(char))) || - !(num_buffer= (char*) thd->alloc(arg_count * - ALIGN_SIZE(sizeof(double)))) || - !(f_args.attributes= (const char**) thd->alloc(arg_count * - sizeof(char *))) || - !(f_args.attribute_lengths= (ulong*) thd->alloc(arg_count * - sizeof(long)))) + if (!(buffers=new (thd->mem_root) String[arg_count]) || + !multi_alloc_root(thd->mem_root, + &f_args.args, arg_count * sizeof(char *), + &f_args.lengths, arg_count * sizeof(long), + &f_args.maybe_null, arg_count * sizeof(char), + &num_buffer, arg_count * sizeof(double), + &f_args.attributes, arg_count * sizeof(char *), + &f_args.attribute_lengths, arg_count * sizeof(long), + NullS)) { free_udf(u_d); DBUG_RETURN(TRUE); @@ -3281,6 +3280,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, initid.const_item=func->const_item_cache; initid.decimals=func->decimals; initid.ptr=0; + for (uint i1= 0 ; i1 < arg_count ; i1++) + buffers[i1].set_thread_specific(); if (u_d->func_init) { @@ -3730,7 +3731,9 @@ longlong Item_master_gtid_wait::val_int() timeout_us= (longlong)-1; result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us); -#endif +#else + null_value= 0; +#endif /* REPLICATION */ return result; } @@ -5055,7 +5058,7 @@ bool Item_func_set_user_var::is_null_result() void Item_func_set_user_var::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("@")); - str->append(name.str, name.length); + str->append(&name); str->append(STRING_WITH_LEN(":=")); args[0]->print_parenthesised(str, query_type, precedence()); } @@ -5065,7 +5068,7 @@ void Item_func_set_user_var::print_as_stmt(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("set @")); - str->append(name.str, name.length); + str->append(&name); str->append(STRING_WITH_LEN(":=")); args[0]->print_parenthesised(str, query_type, precedence()); } @@ -5303,8 +5306,8 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command, Item_func_set_user_var(thd, name, new (thd->mem_root) Item_null(thd))), thd->mem_root); - /* Create the variable */ - if (sql_set_variables(thd, &tmp_var_list, false)) + /* Create the variable if the above allocations succeeded */ + if (thd->is_fatal_error || sql_set_variables(thd, &tmp_var_list, false)) { thd->lex= sav_lex; goto err; @@ -5648,7 +5651,7 @@ void Item_func_get_system_var::fix_length_and_dec() void Item_func_get_system_var::print(String *str, enum_query_type query_type) { if (name.length) - str->append(name.str, name.length); + str->append(&name); else { str->append(STRING_WITH_LEN("@@")); @@ -5841,20 +5844,25 @@ void Item_func_get_system_var::cleanup() cached_strval.free(); } +/** + @retval + 0 ok + 1 OOM error +*/ -void Item_func_match::init_search(THD *thd, bool no_order) +bool Item_func_match::init_search(THD *thd, bool no_order) { DBUG_ENTER("Item_func_match::init_search"); if (!table->file->get_table()) // the handler isn't opened yet - DBUG_VOID_RETURN; + DBUG_RETURN(0); /* Check if init_search() has been called before */ if (ft_handler) { if (join_key) table->file->ft_handler= ft_handler; - DBUG_VOID_RETURN; + DBUG_RETURN(0); } if (key == NO_SUCH_KEY) @@ -5866,6 +5874,8 @@ void Item_func_match::init_search(THD *thd, bool no_order) for (uint i= 1; i < arg_count; i++) fields.push_back(args[i]); concat_ws= new (thd->mem_root) Item_func_concat_ws(thd, fields); + if (thd->is_fatal_error) + DBUG_RETURN(1); // OOM /* Above function used only to get value and do not need fix_fields for it: Item_string - basic constant @@ -5878,10 +5888,11 @@ void Item_func_match::init_search(THD *thd, bool no_order) if (master) { join_key= master->join_key= join_key | master->join_key; - master->init_search(thd, no_order); + if (master->init_search(thd, no_order)) + DBUG_RETURN(1); ft_handler= master->ft_handler; join_key= master->join_key; - DBUG_VOID_RETURN; + DBUG_RETURN(0); } String *ft_tmp= 0; @@ -5896,8 +5907,9 @@ void Item_func_match::init_search(THD *thd, bool no_order) if (ft_tmp->charset() != cmp_collation.collation) { uint dummy_errors; - search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(), - cmp_collation.collation, &dummy_errors); + if (search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(), + cmp_collation.collation, &dummy_errors)) + DBUG_RETURN(1); ft_tmp= &search_value; } @@ -5912,7 +5924,7 @@ void Item_func_match::init_search(THD *thd, bool no_order) if (join_key) table->file->ft_handler=ft_handler; - DBUG_VOID_RETURN; + DBUG_RETURN(0); } @@ -6182,39 +6194,41 @@ longlong Item_func_bit_xor::val_int() */ -Item *get_system_var(THD *thd, enum_var_type var_type, LEX_CSTRING name, - LEX_CSTRING component) +Item *get_system_var(THD *thd, enum_var_type var_type, + const LEX_CSTRING *name, + const LEX_CSTRING *component) { sys_var *var; - LEX_CSTRING *base_name, *component_name; + LEX_CSTRING base_name, component_name; - if (component.str) + if (component->str) { - base_name= &component; - component_name= &name; + base_name= *component; + component_name= *name; } else { - base_name= &name; - component_name= &component; // Empty string + base_name= *name; + component_name= *component; // Empty string } - if (!(var= find_sys_var(thd, base_name->str, base_name->length))) + if (!(var= find_sys_var(thd, base_name.str, base_name.length))) return 0; - if (component.str) + if (component->str) { if (!var->is_struct()) { - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name->str); + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name.str); return 0; } } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - set_if_smaller(component_name->length, MAX_SYS_VAR_LENGTH); + set_if_smaller(component_name.length, MAX_SYS_VAR_LENGTH); - return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type, component_name, - NULL, 0); + return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type, + &component_name, + NULL, 0); } @@ -6231,35 +6245,24 @@ longlong Item_func_row_count::val_int() Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name): - Item_func(thd), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL) + Item_func(thd), Item_sp(thd, context_arg, name) { maybe_null= 1; - dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); - dummy_table->s= (TABLE_SHARE*) (dummy_table+1); } Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg, List<Item> &list): - Item_func(thd, list), context(context_arg), m_name(name_arg), m_sp(NULL), - sp_result_field(NULL) + Item_func(thd, list), Item_sp(thd, context_arg, name_arg) { maybe_null= 1; - dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); - dummy_table->s= (TABLE_SHARE*) (dummy_table+1); } void Item_func_sp::cleanup() { - if (sp_result_field) - { - delete sp_result_field; - sp_result_field= NULL; - } - m_sp= NULL; - dummy_table->alias.free(); + Item_sp::cleanup(); Item_func::cleanup(); } @@ -6267,25 +6270,7 @@ const char * Item_func_sp::func_name() const { THD *thd= current_thd; - /* Calculate length to avoid reallocation of string for sure */ - uint len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) + - m_name->m_name.length)*2 + //characters*quoting - 2 + // ` and ` - (m_name->m_explicit_name ? - 3 : 0) + // '`', '`' and '.' for the db - 1 + // end of string - ALIGN_SIZE(1)); // to avoid String reallocation - String qname((char *)alloc_root(thd->mem_root, len), len, - system_charset_info); - - qname.length(0); - if (m_name->m_explicit_name) - { - append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length); - qname.append('.'); - } - append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length); - return qname.c_ptr_safe(); + return Item_sp::func_name(thd); } @@ -6299,75 +6284,6 @@ void my_missing_function_error(const LEX_CSTRING &token, const char *func_name) /** - @brief Initialize the result field by creating a temporary dummy table - and assign it to a newly created field object. Meta data used to - create the field is fetched from the sp_head belonging to the stored - proceedure found in the stored procedure functon cache. - - @note This function should be called from fix_fields to init the result - field. It is some what related to Item_field. - - @see Item_field - - @param thd A pointer to the session and thread context. - - @return Function return error status. - @retval TRUE is returned on an error - @retval FALSE is returned on success. -*/ - -bool -Item_func_sp::init_result_field(THD *thd, sp_head *sp) -{ - TABLE_SHARE *share; - DBUG_ENTER("Item_func_sp::init_result_field"); - - DBUG_ASSERT(m_sp == NULL); - DBUG_ASSERT(sp_result_field == NULL); - - if (!(m_sp= sp)) - { - my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr()); - context->process_error(thd); - DBUG_RETURN(TRUE); - } - - /* - A Field need to be attached to a Table. - Below we "create" a dummy table by initializing - the needed pointers. - */ - - share= dummy_table->s; - dummy_table->alias.set("", 0, table_alias_charset); - dummy_table->maybe_null = maybe_null; - dummy_table->in_use= thd; - dummy_table->copy_blobs= TRUE; - share->table_cache_key= empty_clex_str; - share->table_name= empty_clex_str; - - if (!(sp_result_field= m_sp->create_result_field(max_length, &name, dummy_table))) - { - DBUG_RETURN(TRUE); - } - - if (sp_result_field->pack_length() > sizeof(result_buf)) - { - void *tmp; - if (!(tmp= thd->alloc(sp_result_field->pack_length()))) - DBUG_RETURN(TRUE); - sp_result_field->move_field((uchar*) tmp); - } - else - sp_result_field->move_field(result_buf); - - sp_result_field->null_ptr= (uchar *) &null_value; - sp_result_field->null_bit= 1; - DBUG_RETURN(FALSE); -} - - -/** @note Deterministic stored procedures are considered inexpensive. Consequently such procedures may be evaluated during optimization, @@ -6401,95 +6317,11 @@ void Item_func_sp::fix_length_and_dec() } -/** - @brief Execute function & store value in field. - - @return Function returns error status. - @retval FALSE on success. - @retval TRUE if an error occurred. -*/ - bool Item_func_sp::execute() { - THD *thd= current_thd; - /* Execute function and store the return value in the field. */ - - if (execute_impl(thd)) - { - null_value= 1; - context->process_error(thd); - if (thd->killed) - thd->send_kill_message(); - return TRUE; - } - - /* Check that the field (the value) is not NULL. */ - - null_value= sp_result_field->is_null(); - - return null_value; -} - - -/** - @brief Execute function and store the return value in the field. - - @note This function was intended to be the concrete implementation of - the interface function execute. This was never realized. - - @return The error state. - @retval FALSE on success - @retval TRUE if an error occurred. -*/ -bool -Item_func_sp::execute_impl(THD *thd) -{ - bool err_status= TRUE; - Sub_statement_state statement_state; - Security_context *save_security_ctx= thd->security_ctx; - enum enum_sp_data_access access= - (m_sp->daccess() == SP_DEFAULT_ACCESS) ? - SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess(); - - DBUG_ENTER("Item_func_sp::execute_impl"); - - if (context->security_ctx) - { - /* Set view definer security context */ - thd->security_ctx= context->security_ctx; - } - if (sp_check_access(thd)) - goto error; - - /* - Throw an error if a non-deterministic function is called while - statement-based replication (SBR) is active. - */ - - if (!m_sp->detistic() && !trust_function_creators && - (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) && - (mysql_bin_log.is_open() && - thd->variables.binlog_format == BINLOG_FORMAT_STMT)) - { - my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0)); - goto error; - } - - /* - Disable the binlogging if this is not a SELECT statement. If this is a - SELECT, leave binlogging on, so execute_function() code writes the - function call into binlog. - */ - thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); - err_status= m_sp->execute_function(thd, args, arg_count, sp_result_field); - thd->restore_sub_statement_state(&statement_state); - -error: - thd->security_ctx= save_security_ctx; - - DBUG_RETURN(err_status); + return Item_sp::execute(current_thd, &null_value, args, arg_count); } @@ -6554,29 +6386,6 @@ longlong Item_func_sqlcode::val_int() } -/** - @brief Checks if requested access to function can be granted to user. - If function isn't found yet, it searches function first. - If function can't be found or user don't have requested access - error is raised. - - @param thd thread handler - - @return Indication if the access was granted or not. - @retval FALSE Access is granted. - @retval TRUE Requested access can't be granted or function doesn't exists. - -*/ - -bool -Item_func_sp::sp_check_access(THD *thd) -{ - DBUG_ENTER("Item_func_sp::sp_check_access"); - DBUG_ASSERT(m_sp); - DBUG_RETURN(m_sp->check_execute_access(thd)); -} - - bool Item_func_sp::fix_fields(THD *thd, Item **ref) { @@ -6613,20 +6422,66 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) } } + + /* Custom aggregates are transformed into an Item_sum_sp. We can not do this + earlier as we have no way of knowing what kind of Item we should create + when parsing the query. + + TODO(cvicentiu): See if this limitation can be lifted. + */ + + DBUG_ASSERT(m_sp == NULL); + if (!(m_sp= sp)) + { + my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); + context->process_error(thd); + DBUG_RETURN(TRUE); + } + /* - We must call init_result_field before Item_func::fix_fields() + We must call init_result_field before Item_func::fix_fields() to make m_sp and result_field members available to fix_length_and_dec(), which is called from Item_func::fix_fields(). */ - res= init_result_field(thd, sp); + res= init_result_field(thd, max_length, maybe_null, &null_value, &name); if (res) - DBUG_RETURN(res); + DBUG_RETURN(TRUE); + + if (m_sp->agg_type() == GROUP_AGGREGATE) + { + List<Item> list; + list.empty(); + for (uint i=0; i < arg_count; i++) + list.push_back(*(args+i)); + + Item_sum_sp *item_sp; + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (arg_count) + item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp, list); + else + item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp); + + if (arena) + thd->restore_active_arena(arena, &backup); + if (!item_sp) + DBUG_RETURN(TRUE); + *ref= item_sp; + item_sp->name= name; + bool err= item_sp->fix_fields(thd, ref); + if (err) + DBUG_RETURN(TRUE); + + list.empty(); + DBUG_RETURN(FALSE); + } res= Item_func::fix_fields(thd, ref); if (res) - DBUG_RETURN(res); + DBUG_RETURN(TRUE); if (thd->lex->is_view_context_analysis()) { @@ -6831,14 +6686,22 @@ longlong Item_func_nextval::val_int() longlong value; int error; const char *key; - TABLE *table= table_list->table; uint length= get_table_def_key(table_list, &key); - THD *thd= table->in_use; + THD *thd; SEQUENCE_LAST_VALUE *entry; char buff[80]; String key_buff(buff,sizeof(buff), &my_charset_bin); - DBUG_ASSERT(table && table->s->sequence); DBUG_ENTER("Item_func_nextval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } if (table->s->tmp_table != NO_TMP_TABLE) { @@ -6890,7 +6753,7 @@ void Item_func_nextval::print(String *str, enum_query_type query_type) char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; const char *d_name= table_list->db, *t_name= table_list->table_name; bool use_db_name= d_name && d_name[0]; - THD *thd= current_thd; + THD *thd= current_thd; // Don't trust 'table' str->append(func_name()); str->append('('); @@ -6930,12 +6793,14 @@ longlong Item_func_lastval::val_int() const char *key; SEQUENCE_LAST_VALUE *entry; uint length= get_table_def_key(table_list, &key); - THD *thd= table_list->table->in_use; + THD *thd; char buff[80]; String key_buff(buff,sizeof(buff), &my_charset_bin); DBUG_ENTER("Item_func_lastval::val_int"); + update_table(); + thd= table->in_use; - if (table_list->table->s->tmp_table != NO_TMP_TABLE) + if (table->s->tmp_table != NO_TMP_TABLE) { /* Temporary tables has an extra \0 at end to distinguish it from @@ -6954,7 +6819,7 @@ longlong Item_func_lastval::val_int() null_value= 1; DBUG_RETURN(0); } - if (entry->check_version(table_list->table)) + if (entry->check_version(table)) { /* Table droped and re-created, remove current version */ my_hash_delete(&thd->sequences, (uchar*) entry); @@ -6979,10 +6844,20 @@ longlong Item_func_setval::val_int() { longlong value; int error; - TABLE *table= table_list->table; - DBUG_ASSERT(table && table->s->sequence); + THD *thd; DBUG_ENTER("Item_func_setval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } + value= nextval; error= table->s->sequence->set_value(table, nextval, round, is_used); if (error) @@ -7001,7 +6876,7 @@ void Item_func_setval::print(String *str, enum_query_type query_type) char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; const char *d_name= table_list->db, *t_name= table_list->table_name; bool use_db_name= d_name && d_name[0]; - THD *thd= table_list->table->in_use; + THD *thd= current_thd; // Don't trust 'table' str->append(func_name()); str->append('('); diff --git a/sql/item_func.h b/sql/item_func.h index 58df7b8b3c6..86924384d74 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -819,8 +819,8 @@ public: { return Cursor_ref::print_func(str, func_name()); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_rowcount>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_rowcount>(thd, this); } }; @@ -839,8 +839,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_connection_id>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_connection_id>(thd, this); } }; @@ -896,8 +896,8 @@ public: virtual void print(String *str, enum_query_type query_type); uint decimal_precision() const { return args[0]->decimal_precision(); } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_signed>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_signed>(thd, this); } }; @@ -927,8 +927,8 @@ public: } uint decimal_precision() const { return max_length; } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_unsigned>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_unsigned>(thd, this); } }; @@ -957,8 +957,8 @@ public: const char *func_name() const { return "decimal_typecast"; } virtual void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_decimal_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_decimal_typecast>(thd, this); } }; @@ -980,8 +980,8 @@ public: const char *func_name() const { return "double_typecast"; } virtual void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_double_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_double_typecast>(thd, this); } }; @@ -1007,8 +1007,8 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_plus>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_plus>(thd, this); } }; class Item_func_minus :public Item_func_additive_op @@ -1038,8 +1038,8 @@ public: Item_func_additive_op::fix_length_and_dec_int(); fix_unsigned_flag(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_minus>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_minus>(thd, this); } }; @@ -1057,8 +1057,8 @@ public: void fix_length_and_dec(); bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_mul>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_mul>(thd, this); } }; @@ -1076,8 +1076,8 @@ public: void fix_length_and_dec_double(); void fix_length_and_dec_int(); void result_precision(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_div>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_div>(thd, this); } }; @@ -1100,8 +1100,8 @@ public: bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_int_div>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_int_div>(thd, this); } }; @@ -1135,8 +1135,8 @@ public: } bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_mod>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_mod>(thd, this); } }; @@ -1161,8 +1161,8 @@ public: void fix_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_neg>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_neg>(thd, this); } }; @@ -1178,8 +1178,8 @@ public: void fix_length_and_dec_double(); void fix_length_and_dec_decimal(); void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_abs>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_abs>(thd, this); } }; // A class to handle logarithmic and trigonometric functions @@ -1204,8 +1204,8 @@ public: Item_func_exp(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "exp"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_exp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_exp>(thd, this); } }; @@ -1215,8 +1215,8 @@ public: Item_func_ln(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "ln"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ln>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ln>(thd, this); } }; @@ -1227,8 +1227,8 @@ public: Item_func_log(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {} double val_real(); const char *func_name() const { return "log"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_log>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_log>(thd, this); } }; @@ -1238,8 +1238,8 @@ public: Item_func_log2(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "log2"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_log2>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_log2>(thd, this); } }; @@ -1249,8 +1249,8 @@ public: Item_func_log10(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "log10"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_log10>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_log10>(thd, this); } }; @@ -1260,8 +1260,8 @@ public: Item_func_sqrt(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "sqrt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sqrt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sqrt>(thd, this); } }; @@ -1271,8 +1271,8 @@ public: Item_func_pow(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {} double val_real(); const char *func_name() const { return "pow"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_pow>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_pow>(thd, this); } }; @@ -1282,8 +1282,8 @@ public: Item_func_acos(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "acos"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_acos>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_acos>(thd, this); } }; class Item_func_asin :public Item_dec_func @@ -1292,8 +1292,8 @@ public: Item_func_asin(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "asin"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_asin>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_asin>(thd, this); } }; class Item_func_atan :public Item_dec_func @@ -1303,8 +1303,8 @@ public: Item_func_atan(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {} double val_real(); const char *func_name() const { return "atan"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_atan>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_atan>(thd, this); } }; class Item_func_cos :public Item_dec_func @@ -1313,8 +1313,8 @@ public: Item_func_cos(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "cos"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cos>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cos>(thd, this); } }; class Item_func_sin :public Item_dec_func @@ -1323,8 +1323,8 @@ public: Item_func_sin(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "sin"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sin>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sin>(thd, this); } }; class Item_func_tan :public Item_dec_func @@ -1333,8 +1333,8 @@ public: Item_func_tan(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "tan"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_tan>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_tan>(thd, this); } }; class Item_func_cot :public Item_dec_func @@ -1343,8 +1343,8 @@ public: Item_func_cot(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "cot"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cot>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cot>(thd, this); } }; @@ -1366,8 +1366,8 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ceiling>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ceiling>(thd, this); } }; @@ -1379,8 +1379,8 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_floor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_floor>(thd, this); } }; /* This handles round and truncate */ @@ -1404,8 +1404,8 @@ public: { args[0]->type_handler()->Item_func_round_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_round>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_round>(thd, this); } }; @@ -1429,8 +1429,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rand>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rand>(thd, this); } private: void seed_random (Item * val); }; @@ -1446,8 +1446,8 @@ public: uint decimal_precision() const { return 1; } void fix_length_and_dec() { fix_char_length(2); } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sign>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sign>(thd, this); } }; @@ -1465,8 +1465,8 @@ public: const char *func_name() const { return name; } void fix_length_and_dec() { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_units>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_units>(thd, this); } }; @@ -1560,8 +1560,8 @@ class Item_func_min :public Item_func_min_max public: Item_func_min(THD *thd, List<Item> &list): Item_func_min_max(thd, list, 1) {} const char *func_name() const { return "least"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_min>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_min>(thd, this); } }; class Item_func_max :public Item_func_min_max @@ -1569,8 +1569,8 @@ class Item_func_max :public Item_func_min_max public: Item_func_max(THD *thd, List<Item> &list): Item_func_min_max(thd, list, -1) {} const char *func_name() const { return "greatest"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_max>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_max>(thd, this); } }; @@ -1601,8 +1601,8 @@ public: /* The item could be a NULL constant. */ null_value= args[0]->is_null(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rollup_const>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rollup_const>(thd, this); } }; @@ -1623,8 +1623,8 @@ public: Item_func_octet_length(THD *thd, Item *a): Item_long_func_length(thd, a) {} longlong val_int(); const char *func_name() const { return "octet_length"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_octet_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_octet_length>(thd, this); } }; class Item_func_bit_length :public Item_longlong_func @@ -1638,8 +1638,8 @@ public: } longlong val_int(); const char *func_name() const { return "bit_length"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_length>(thd, this); } }; class Item_func_char_length :public Item_long_func_length @@ -1649,8 +1649,8 @@ public: Item_func_char_length(THD *thd, Item *a): Item_long_func_length(thd, a) {} longlong val_int(); const char *func_name() const { return "char_length"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_char_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_char_length>(thd, this); } }; class Item_func_coercibility :public Item_long_func @@ -1670,8 +1670,8 @@ public: Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { return this; } bool const_item() const { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_coercibility>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_coercibility>(thd, this); } }; @@ -1703,8 +1703,8 @@ public: agg_arg_charsets_for_comparison(cmp_collation, args, 2); } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_locate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_locate>(thd, this); } }; @@ -1718,8 +1718,8 @@ public: longlong val_int(); const char *func_name() const { return "field"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_field>(thd, this); } }; @@ -1733,8 +1733,8 @@ public: longlong val_int(); const char *func_name() const { return "ascii"; } void fix_length_and_dec() { max_length=3; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ascii>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ascii>(thd, this); } }; class Item_func_ord :public Item_long_func @@ -1747,8 +1747,8 @@ public: void fix_length_and_dec() { fix_char_length(7); } longlong val_int(); const char *func_name() const { return "ord"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ord>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ord>(thd, this); } }; class Item_func_find_in_set :public Item_long_func @@ -1765,8 +1765,8 @@ public: longlong val_int(); const char *func_name() const { return "find_in_set"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_find_in_set>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_find_in_set>(thd, this); } }; /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ @@ -1794,8 +1794,8 @@ public: longlong val_int(); const char *func_name() const { return "|"; } enum precedence precedence() const { return BITOR_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_or>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_or>(thd, this); } }; class Item_func_bit_and :public Item_func_bit @@ -1805,8 +1805,8 @@ public: longlong val_int(); const char *func_name() const { return "&"; } enum precedence precedence() const { return BITAND_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_and>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_and>(thd, this); } }; class Item_func_bit_count :public Item_long_func @@ -1818,8 +1818,8 @@ public: longlong val_int(); const char *func_name() const { return "bit_count"; } void fix_length_and_dec() { max_length=2; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_count>(thd, this); } }; class Item_func_shift_left :public Item_func_bit @@ -1829,8 +1829,8 @@ public: longlong val_int(); const char *func_name() const { return "<<"; } enum precedence precedence() const { return SHIFT_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_shift_left>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_shift_left>(thd, this); } }; class Item_func_shift_right :public Item_func_bit @@ -1840,8 +1840,8 @@ public: longlong val_int(); const char *func_name() const { return ">>"; } enum precedence precedence() const { return SHIFT_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_shift_right>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_shift_right>(thd, this); } }; class Item_func_bit_neg :public Item_func_bit @@ -1856,8 +1856,8 @@ public: str->append(func_name()); args[0]->print_parenthesised(str, query_type, precedence()); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_neg>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_neg>(thd, this); } }; @@ -1881,8 +1881,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_last_insert_id>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_last_insert_id>(thd, this); } }; @@ -1905,8 +1905,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_benchmark>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_benchmark>(thd, this); } }; @@ -1932,8 +1932,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sleep>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sleep>(thd, this); } }; @@ -2060,8 +2060,8 @@ class Item_func_udf_float :public Item_udf_func String *val_str(String *str); const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { fix_num_length_and_dec(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_float>(thd, this); } }; @@ -2078,8 +2078,8 @@ public: String *val_str(String *str); const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals= 0; max_length= 21; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_int>(thd, this); } }; @@ -2096,8 +2096,8 @@ public: String *val_str(String *str); const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec() { fix_num_length_and_dec(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_decimal>(thd, this); } }; @@ -2135,8 +2135,8 @@ public: } const Type_handler *type_handler() const { return string_type_handler(); } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_str>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_str>(thd, this); } }; #else /* Dummy functions to get sql_yacc.cc compiled */ @@ -2218,8 +2218,8 @@ class Item_func_get_lock :public Item_long_func { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_lock>(thd, this); } }; class Item_func_release_lock :public Item_long_func @@ -2242,8 +2242,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_release_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_release_lock>(thd, this); } }; /* replication functions */ @@ -2273,8 +2273,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_master_pos_wait>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_master_pos_wait>(thd, this); } }; @@ -2298,8 +2298,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_master_gtid_wait>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_master_gtid_wait>(thd, this); } }; @@ -2406,8 +2406,8 @@ public: bool register_field_in_bitmap(void *arg); bool set_entry(THD *thd, bool create_if_not_exists); void cleanup(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_set_user_var>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_set_user_var>(thd, this); } }; @@ -2434,8 +2434,8 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_user_var>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_user_var>(thd, this); } private: bool set_value(THD *thd, sp_rcontext *ctx, Item **it); @@ -2479,8 +2479,8 @@ public: void set_null_value(CHARSET_INFO* cs); void set_value(const char *str, uint length, CHARSET_INFO* cs); const Type_handler *type_handler() const { return &type_handler_double; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_user_var_as_out_param>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_user_var_as_out_param>(thd, this); } }; @@ -2534,8 +2534,8 @@ public: void cleanup(); bool check_vcol_func_processor(void *arg); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_system_var>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_system_var>(thd, this); } }; @@ -2584,14 +2584,14 @@ public: virtual void print(String *str, enum_query_type query_type); bool fix_index(); - void init_search(THD *thd, bool no_order); + bool init_search(THD *thd, bool no_order); bool check_vcol_func_processor(void *arg) { return mark_unsupported_function("match ... against()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_match>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_match>(thd, this); } + Item *build_clone(THD *thd) { return 0; } private: /** Check whether storage engine for given table, @@ -2636,8 +2636,8 @@ public: longlong val_int(); const char *func_name() const { return "^"; } enum precedence precedence() const { return BITXOR_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_xor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_xor>(thd, this); } }; class Item_func_is_free_lock :public Item_long_func @@ -2654,8 +2654,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_free_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_free_lock>(thd, this); } }; class Item_func_is_used_lock :public Item_long_func @@ -2672,8 +2672,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_used_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_used_lock>(thd, this); } }; @@ -2721,8 +2721,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_row_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_row_count>(thd, this); } }; @@ -2732,26 +2732,12 @@ public: * */ -class sp_head; -class sp_name; -struct st_sp_security_context; - -class Item_func_sp :public Item_func +class Item_func_sp :public Item_func, + public Item_sp { private: - Name_resolution_context *context; - sp_name *m_name; - mutable sp_head *m_sp; - TABLE *dummy_table; - uchar result_buf[64]; - /* - The result field of the concrete stored function. - */ - Field *sp_result_field; bool execute(); - bool execute_impl(THD *thd); - bool init_result_field(THD *thd, sp_head *sp); protected: bool is_expensive_processor(void *arg) @@ -2843,7 +2829,6 @@ public: virtual bool change_context_processor(void *cntx) { context= (Name_resolution_context *)cntx; return FALSE; } - bool sp_check_access(THD * thd); virtual enum Functype functype() const { return FUNC_SP; } bool fix_fields(THD *thd, Item **ref); @@ -2864,11 +2849,11 @@ public: { return TRUE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sp>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sp>(thd, this); } + Item *build_clone(THD *thd) { - Item_func_sp *clone= (Item_func_sp *) Item_func::build_clone(thd, mem_root); + Item_func_sp *clone= (Item_func_sp *) Item_func::build_clone(thd); if (clone) clone->sp_result_field= NULL; return clone; @@ -2892,8 +2877,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_found_rows>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_found_rows>(thd, this); } }; @@ -2911,8 +2896,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_oracle_sql_rowcount>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_oracle_sql_rowcount>(thd, this); } }; @@ -2935,8 +2920,8 @@ public: maybe_null= null_value= false; max_length= 11; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sqlcode>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sqlcode>(thd, this); } }; @@ -2955,8 +2940,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uuid_short>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uuid_short>(thd, this); } }; @@ -2985,8 +2970,8 @@ public: Item_func::update_used_tables(); maybe_null= last_value->maybe_null; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_last_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_last_value>(thd, this); } }; @@ -2996,9 +2981,10 @@ class Item_func_nextval :public Item_longlong_func { protected: TABLE_LIST *table_list; + TABLE *table; public: - Item_func_nextval(THD *thd, TABLE_LIST *table): - Item_longlong_func(thd), table_list(table) {} + Item_func_nextval(THD *thd, TABLE_LIST *table_list_arg): + Item_longlong_func(thd), table_list(table_list_arg) {} longlong val_int(); const char *func_name() const { return "nextval"; } void fix_length_and_dec() @@ -3007,14 +2993,31 @@ public: max_length= MAX_BIGINT_WIDTH; maybe_null= 1; /* In case of errors */ } + /* + update_table() function must be called during the value function + as in case of DEFAULT the sequence table may not yet be open + while fix_fields() are called + */ + void update_table() + { + if (!(table= table_list->table)) + { + /* + If nextval was used in DEFAULT then next_local points to + the table_list used by to open the sequence table + */ + table= table_list->next_local->table; + } + } bool const_item() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nextval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nextval>(thd, this); } void print(String *str, enum_query_type query_type); bool check_vcol_func_processor(void *arg) { return mark_unsupported_function(func_name(), "()", arg, - VCOL_NON_DETERMINISTIC); + (VCOL_NON_DETERMINISTIC | + VCOL_NOT_VIRTUAL)); } }; @@ -3024,12 +3027,12 @@ public: class Item_func_lastval :public Item_func_nextval { public: - Item_func_lastval(THD *thd, TABLE_LIST *table): - Item_func_nextval(thd, table) {} + Item_func_lastval(THD *thd, TABLE_LIST *table_list_arg): + Item_func_nextval(thd, table_list_arg) {} longlong val_int(); const char *func_name() const { return "lastval"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lastval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lastval>(thd, this); } }; @@ -3041,21 +3044,21 @@ class Item_func_setval :public Item_func_nextval ulonglong round; bool is_used; public: - Item_func_setval(THD *thd, TABLE_LIST *table, longlong nextval_arg, + Item_func_setval(THD *thd, TABLE_LIST *table_list_arg, longlong nextval_arg, ulonglong round_arg, bool is_used_arg) - : Item_func_nextval(thd, table), + : Item_func_nextval(thd, table_list_arg), nextval(nextval_arg), round(round_arg), is_used(is_used_arg) {} longlong val_int(); const char *func_name() const { return "setval"; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_setval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_setval>(thd, this); } }; -Item *get_system_var(THD *thd, enum_var_type var_type, LEX_CSTRING name, - LEX_CSTRING component); +Item *get_system_var(THD *thd, enum_var_type var_type, + const LEX_CSTRING *name, const LEX_CSTRING *component); extern bool check_reserved_words(const LEX_CSTRING *name); Item *find_date_time_item(Item **args, uint nargs, uint col); double my_double_round(double value, longlong dec, bool dec_unsigned, diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 9fd246bbe4c..aee44a7a01f 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index d332d067c37..8101433abb5 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -202,8 +202,8 @@ public: Item_geometry_func(thd, a, srid) {} const char *func_name() const { return "st_geometryfromtext"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_from_text>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_from_text>(thd, this); } }; class Item_func_geometry_from_wkb: public Item_geometry_func @@ -219,8 +219,8 @@ public: Item_geometry_func(thd, a, srid) {} const char *func_name() const { return "st_geometryfromwkb"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_from_wkb>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_from_wkb>(thd, this); } }; @@ -241,8 +241,8 @@ public: Item_geometry_func(thd, js, opt, srid) {} const char *func_name() const { return "st_geomfromgeojson"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_from_json>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_from_json>(thd, this); } }; @@ -254,8 +254,8 @@ public: const char *func_name() const { return "st_astext"; } String *val_str_ascii(String *); void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_as_wkt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_as_wkt>(thd, this); } }; class Item_func_as_wkb: public Item_binary_func_args_geometry @@ -273,8 +273,8 @@ public: max_length= (uint32) UINT_MAX32; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_as_wkb>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_as_wkb>(thd, this); } }; @@ -296,8 +296,8 @@ public: const char *func_name() const { return "st_asgeojson"; } void fix_length_and_dec(); String *val_str_ascii(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_as_geojson>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_as_geojson>(thd, this); } }; @@ -314,8 +314,8 @@ public: fix_length_and_charset(20, default_charset()); maybe_null= 1; }; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_type>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_type>(thd, this); } }; @@ -349,8 +349,8 @@ public: {} const char *func_name() const { return "st_convexhull"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_convexhull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_convexhull>(thd, this); } }; @@ -362,8 +362,8 @@ public: const char *func_name() const { return "st_centroid"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_centroid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_centroid>(thd, this); } }; class Item_func_envelope: public Item_geometry_func_args_geometry @@ -374,8 +374,8 @@ public: const char *func_name() const { return "st_envelope"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_envelope>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_envelope>(thd, this); } }; @@ -408,8 +408,8 @@ public: :Item_geometry_func_args_geometry(thd, a) {} const char *func_name() const { return "st_boundary"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_boundary>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_boundary>(thd, this); } }; @@ -424,8 +424,8 @@ public: const char *func_name() const { return "point"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_point>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_point>(thd, this); } }; class Item_func_spatial_decomp: public Item_geometry_func_args_geometry @@ -450,8 +450,8 @@ public: } } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_decomp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_decomp>(thd, this); } }; class Item_func_spatial_decomp_n: public Item_geometry_func_args_geometry @@ -483,8 +483,8 @@ public: } } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_decomp_n>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_decomp_n>(thd, this); } }; class Item_func_spatial_collection: public Item_geometry_func @@ -521,8 +521,8 @@ public: } const char *func_name() const { return "geometrycollection"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_collection>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_collection>(thd, this); } }; @@ -571,7 +571,7 @@ public: usable_tables, sargables, false); } bool need_parentheses_in_default() { return false; } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *build_clone(THD *thd) { return 0; } }; @@ -583,8 +583,8 @@ public: { } longlong val_int(); const char *func_name() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_mbr_rel>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_mbr_rel>(thd, this); } }; @@ -599,8 +599,8 @@ public: { } longlong val_int(); const char *func_name() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_precise_rel>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_precise_rel>(thd, this); } }; @@ -622,8 +622,8 @@ public: longlong val_int(); const char *func_name() const { return "st_relate"; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_relate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_relate>(thd, this); } }; @@ -658,8 +658,8 @@ public: { Item_func::print(str, query_type); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_operation>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_operation>(thd, this); } }; @@ -715,8 +715,8 @@ public: :Item_geometry_func_args_geometry(thd, obj, distance) {} const char *func_name() const { return "st_buffer"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_buffer>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_buffer>(thd, this); } }; @@ -729,8 +729,8 @@ public: const char *func_name() const { return "st_isempty"; } void fix_length_and_dec() { maybe_null= 1; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isempty>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isempty>(thd, this); } }; class Item_func_issimple: public Item_long_func_args_geometry @@ -746,8 +746,8 @@ public: const char *func_name() const { return "st_issimple"; } void fix_length_and_dec() { decimals=0; max_length=2; } uint decimal_precision() const { return 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_issimple>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_issimple>(thd, this); } }; class Item_func_isclosed: public Item_long_func_args_geometry @@ -759,8 +759,8 @@ public: const char *func_name() const { return "st_isclosed"; } void fix_length_and_dec() { decimals=0; max_length=2; } uint decimal_precision() const { return 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isclosed>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isclosed>(thd, this); } }; class Item_func_isring: public Item_func_issimple @@ -769,8 +769,8 @@ public: Item_func_isring(THD *thd, Item *a): Item_func_issimple(thd, a) {} longlong val_int(); const char *func_name() const { return "st_isring"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isring>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isring>(thd, this); } }; class Item_func_dimension: public Item_long_func_args_geometry @@ -781,8 +781,8 @@ public: longlong val_int(); const char *func_name() const { return "st_dimension"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dimension>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dimension>(thd, this); } }; @@ -797,8 +797,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_x>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_x>(thd, this); } }; @@ -813,8 +813,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_y>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_y>(thd, this); } }; @@ -826,8 +826,8 @@ public: longlong val_int(); const char *func_name() const { return "st_numgeometries"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_numgeometries>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_numgeometries>(thd, this); } }; @@ -839,8 +839,8 @@ public: longlong val_int(); const char *func_name() const { return "st_numinteriorrings"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_numinteriorring>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_numinteriorring>(thd, this); } }; @@ -852,8 +852,8 @@ public: longlong val_int(); const char *func_name() const { return "st_numpoints"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_numpoints>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_numpoints>(thd, this); } }; @@ -868,8 +868,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_area>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_area>(thd, this); } }; @@ -886,8 +886,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_glength>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_glength>(thd, this); } }; @@ -899,8 +899,8 @@ public: longlong val_int(); const char *func_name() const { return "srid"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_srid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_srid>(thd, this); } }; @@ -916,8 +916,8 @@ public: :Item_real_func_args_geometry_geometry(thd, a, b) {} double val_real(); const char *func_name() const { return "st_distance"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_distance>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_distance>(thd, this); } }; @@ -933,8 +933,8 @@ public: const char *func_name() const { return "st_pointonsurface"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_pointonsurface>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_pointonsurface>(thd, this); } }; @@ -951,8 +951,8 @@ class Item_func_gis_debug: public Item_long_func { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_gis_debug>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_gis_debug>(thd, this); } }; #endif diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc index ff671536002..d4788a39d5e 100644 --- a/sql/item_inetfunc.cc +++ b/sql/item_inetfunc.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "item_inetfunc.h" #include "my_net.h" diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h index bd0a95b5270..d934cef43dd 100644 --- a/sql/item_inetfunc.h +++ b/sql/item_inetfunc.h @@ -39,8 +39,8 @@ public: maybe_null= 1; unsigned_flag= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet_aton>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet_aton>(thd, this); } }; @@ -61,8 +61,8 @@ public: fix_length_and_charset(3 * 8 + 7, default_charset()); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet_ntoa>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet_ntoa>(thd, this); } }; @@ -130,8 +130,8 @@ public: fix_length_and_charset(16, &my_charset_bin); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet6_aton>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet6_aton>(thd, this); } protected: virtual bool calc_value(const String *arg, String *buffer); @@ -164,8 +164,8 @@ public: maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet6_ntoa>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet6_ntoa>(thd, this); } protected: virtual bool calc_value(const String *arg, String *buffer); @@ -186,8 +186,8 @@ public: public: virtual const char *func_name() const { return "is_ipv4"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv4>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv4>(thd, this); } protected: virtual bool calc_value(const String *arg); @@ -208,8 +208,8 @@ public: public: virtual const char *func_name() const { return "is_ipv6"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv6>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv6>(thd, this); } protected: virtual bool calc_value(const String *arg); @@ -230,8 +230,8 @@ public: public: virtual const char *func_name() const { return "is_ipv4_compat"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv4_compat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv4_compat>(thd, this); } protected: virtual bool calc_value(const String *arg); @@ -252,8 +252,8 @@ public: public: virtual const char *func_name() const { return "is_ipv4_mapped"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv4_mapped>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); } protected: virtual bool calc_value(const String *arg); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 069bdf2e079..4b2de7cf28b 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -14,7 +14,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" #include "item.h" diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 77e7588be25..354de69eee4 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -54,8 +54,8 @@ public: Item_bool_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_valid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_valid>(thd, this); } }; @@ -70,8 +70,8 @@ public: Item_bool_func(thd, js, i_path) {} const char *func_name() const { return "json_exists"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_exists>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_exists>(thd, this); } longlong val_int(); }; @@ -89,8 +89,8 @@ public: void fix_length_and_dec(); String *val_str(String *); virtual bool check_and_get_value(json_engine_t *je, String *res, int *error); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_value>(thd, this); } }; @@ -102,8 +102,8 @@ public: bool is_json_type() { return true; } const char *func_name() const { return "json_query"; } bool check_and_get_value(json_engine_t *je, String *res, int *error); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_query>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_query>(thd, this); } }; @@ -117,8 +117,8 @@ public: const char *func_name() const { return "json_quote"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_quote>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_quote>(thd, this); } }; @@ -132,8 +132,8 @@ public: const char *func_name() const { return "json_unquote"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_unquote>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_unquote>(thd, this); } }; @@ -168,8 +168,8 @@ public: longlong val_int(); double val_real(); uint get_n_paths() const { return arg_count - 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_extract>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_extract>(thd, this); } }; @@ -187,8 +187,8 @@ public: const char *func_name() const { return "json_contains"; } void fix_length_and_dec(); longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_contains>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_contains>(thd, this); } }; @@ -210,8 +210,8 @@ public: void fix_length_and_dec(); void cleanup(); longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_contains_path>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_contains_path>(thd, this); } }; @@ -229,8 +229,8 @@ public: bool is_json_type() { return true; } void fix_length_and_dec(); const char *func_name() const { return "json_array"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_array>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_array>(thd, this); } }; @@ -246,8 +246,8 @@ public: String *val_str(String *); uint get_n_paths() const { return arg_count/2; } const char *func_name() const { return "json_array_append"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_array_append>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_array_append>(thd, this); } }; @@ -258,8 +258,8 @@ public: Item_func_json_array_append(thd, list) {} String *val_str(String *); const char *func_name() const { return "json_array_insert"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_array_insert>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_array_insert>(thd, this); } }; @@ -273,8 +273,8 @@ public: String *val_str(String *); bool is_json_type() { return true; } const char *func_name() const { return "json_object"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_object>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_object>(thd, this); } }; @@ -288,8 +288,8 @@ public: String *val_str(String *); bool is_json_type() { return true; } const char *func_name() const { return "json_merge"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_merge>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_merge>(thd, this); } }; @@ -311,8 +311,8 @@ public: const char *func_name() const { return "json_length"; } void fix_length_and_dec(); longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_length>(thd, this); } }; @@ -327,8 +327,8 @@ public: const char *func_name() const { return "json_depth"; } void fix_length_and_dec() { max_length= 10; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_depth>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_depth>(thd, this); } }; @@ -341,8 +341,8 @@ public: const char *func_name() const { return "json_type"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_type>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_type>(thd, this); } }; @@ -364,8 +364,8 @@ public: return mode_insert ? (mode_replace ? "json_set" : "json_insert") : "json_update"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_insert>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_insert>(thd, this); } }; @@ -380,8 +380,8 @@ public: String *val_str(String *); uint get_n_paths() const { return arg_count - 1; } const char *func_name() const { return "json_remove"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_remove>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_remove>(thd, this); } }; @@ -397,8 +397,8 @@ public: const char *func_name() const { return "json_keys"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_keys>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_keys>(thd, this); } }; @@ -422,8 +422,8 @@ public: void fix_length_and_dec(); String *val_str(String *); uint get_n_paths() const { return arg_count > 4 ? arg_count - 4 : 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_search>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_search>(thd, this); } }; @@ -451,8 +451,8 @@ public: String *val_str(String *str); String *val_json(String *str); bool is_json_type() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_format>(thd, this); } }; diff --git a/sql/item_row.cc b/sql/item_row.cc index c7e0f164af9..64794093bec 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -163,15 +163,15 @@ void Item_row::bring_value() } -Item* Item_row::build_clone(THD *thd, MEM_ROOT *mem_root) +Item* Item_row::build_clone(THD *thd) { - Item_row *copy= (Item_row *) get_copy(thd, mem_root); + Item_row *copy= (Item_row *) get_copy(thd); if (!copy) return 0; - copy->args= (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count); + copy->args= (Item**) alloc_root(thd->mem_root, sizeof(Item*) * arg_count); for (uint i= 0; i < arg_count; i++) { - Item *arg_clone= args[i]->build_clone(thd, mem_root); + Item *arg_clone= args[i]->build_clone(thd); if (!arg_clone) return 0; copy->args[i]= arg_clone; diff --git a/sql/item_row.h b/sql/item_row.h index 206a8ed83c5..064c1f267b4 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -130,9 +130,9 @@ public: } bool check_vcol_func_processor(void *arg) {return FALSE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_row>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root); + Item *get_copy(THD *thd) + { return get_item_copy<Item_row>(thd, this); } + Item *build_clone(THD *thd); }; #endif /* ITEM_ROW_INCLUDED */ diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b39fcb78fe9..483a6468c2e 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -31,7 +31,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> // HAVE_* +#include "mariadb.h" // HAVE_* #include "sql_priv.h" /* @@ -4513,6 +4513,9 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg) case MYSQL_TYPE_GEOMETRY: type= DYN_COL_STRING; break; + case MYSQL_TYPE_VARCHAR_COMPRESSED: + case MYSQL_TYPE_BLOB_COMPRESSED: + DBUG_ASSERT(0); } } if (type == DYN_COL_STRING && diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 49f82f7028c..faad324403a 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -147,8 +147,8 @@ public: fix_length_and_charset(32, default_charset()); } const char *func_name() const { return "md5"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_md5>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_md5>(thd, this); } }; @@ -159,8 +159,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "sha"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sha>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sha>(thd, this); } }; class Item_func_sha2 :public Item_str_ascii_checksum_func @@ -171,8 +171,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "sha2"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sha2>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sha2>(thd, this); } }; class Item_func_to_base64 :public Item_str_ascii_checksum_func @@ -184,8 +184,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "to_base64"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_to_base64>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_to_base64>(thd, this); } }; class Item_func_from_base64 :public Item_str_binary_checksum_func @@ -197,8 +197,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "from_base64"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_from_base64>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_from_base64>(thd, this); } }; #include <my_crypt.h> @@ -223,8 +223,8 @@ public: Item_aes_crypt(thd, a, b) {} void fix_length_and_dec(); const char *func_name() const { return "aes_encrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_aes_encrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_aes_encrypt>(thd, this); } }; class Item_func_aes_decrypt :public Item_aes_crypt @@ -234,8 +234,8 @@ public: Item_aes_crypt(thd, a, b) {} void fix_length_and_dec(); const char *func_name() const { return "aes_decrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_aes_decrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_aes_decrypt>(thd, this); } }; @@ -281,8 +281,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "concat"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_concat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_concat>(thd, this); } }; @@ -301,9 +301,9 @@ public: { } String *val_str(String *); const char *func_name() const { return "concat_operator_oracle"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) + Item *get_copy(THD *thd) { - return get_item_copy<Item_func_concat_operator_oracle>(thd, mem_root, this); + return get_item_copy<Item_func_concat_operator_oracle>(thd, this); } }; @@ -321,8 +321,8 @@ public: maybe_null= 1; } const char *func_name() const { return "decode_histogram"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_decode_histogram>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_decode_histogram>(thd, this); } }; class Item_func_concat_ws :public Item_str_func @@ -334,8 +334,8 @@ public: void fix_length_and_dec(); const char *func_name() const { return "concat_ws"; } table_map not_null_tables() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_concat_ws>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_concat_ws>(thd, this); } }; class Item_func_reverse :public Item_str_func @@ -346,8 +346,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "reverse"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_reverse>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_reverse>(thd, this); } }; @@ -361,8 +361,8 @@ public: void fix_length_and_dec(); String *val_str_internal(String *str, String *empty_string_for_null); const char *func_name() const { return "replace"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_replace>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_replace>(thd, this); } }; @@ -374,8 +374,8 @@ public: Item_func_replace(thd, org, find, replace) {} String *val_str(String *to) { return val_str_internal(to, &tmp_emtpystr); }; const char *func_name() const { return "replace_oracle"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_replace_oracle>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_replace_oracle>(thd, this); } }; @@ -400,7 +400,7 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); const char *func_name() const { return "regexp_replace"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0;} + Item *get_copy(THD *thd) { return 0;} }; @@ -422,7 +422,7 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); const char *func_name() const { return "regexp_substr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } }; @@ -436,8 +436,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "insert"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_insert>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_insert>(thd, this); } }; @@ -459,8 +459,8 @@ public: Item_func_lcase(THD *thd, Item *item): Item_str_conv(thd, item) {} const char *func_name() const { return "lcase"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lcase>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lcase>(thd, this); } }; class Item_func_ucase :public Item_str_conv @@ -469,8 +469,8 @@ public: Item_func_ucase(THD *thd, Item *item): Item_str_conv(thd, item) {} const char *func_name() const { return "ucase"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ucase>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ucase>(thd, this); } }; @@ -482,8 +482,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "left"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_left>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_left>(thd, this); } }; @@ -495,8 +495,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "right"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_right>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_right>(thd, this); } }; @@ -512,8 +512,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_substr>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_substr>(thd, this); } }; class Item_func_substr_oracle :public Item_func_substr @@ -527,8 +527,8 @@ public: Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c): Item_func_substr(thd, a, b, c) {} const char *func_name() const { return "substr_oracle"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_substr_oracle>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_substr_oracle>(thd, this); } }; class Item_func_substr_index :public Item_str_func @@ -540,8 +540,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substring_index"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_substr_index>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_substr_index>(thd, this); } }; @@ -574,8 +574,8 @@ public: const char *func_name() const { return "trim"; } void print(String *str, enum_query_type query_type); virtual const char *mode_name() const { return "both"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_trim>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_trim>(thd, this); } }; @@ -587,8 +587,8 @@ public: String *val_str(String *); const char *func_name() const { return "ltrim"; } const char *mode_name() const { return "leading"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ltrim>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ltrim>(thd, this); } }; @@ -600,8 +600,8 @@ public: String *val_str(String *); const char *func_name() const { return "rtrim"; } const char *mode_name() const { return "trailing"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rtrim>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rtrim>(thd, this); } }; @@ -639,8 +639,8 @@ public: "password" : "old_password"); } static char *alloc(THD *thd, const char *password, size_t pass_len, enum PW_Alg al); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_password>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_password>(thd, this); } }; @@ -661,8 +661,8 @@ public: max_length = args[0]->max_length + 9; } const char *func_name() const { return "des_encrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_des_encrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_des_encrypt>(thd, this); } }; class Item_func_des_decrypt :public Item_str_binary_checksum_func @@ -683,8 +683,8 @@ public: max_length-= 9U; } const char *func_name() const { return "des_decrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_des_decrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_des_decrypt>(thd, this); } }; @@ -719,8 +719,8 @@ public: { return FALSE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_encrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_encrypt>(thd, this); } }; #include "sql_crypt.h" @@ -739,8 +739,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "encode"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_encode>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_encode>(thd, this); } protected: virtual void crypto_transform(String *); private: @@ -754,8 +754,8 @@ class Item_func_decode :public Item_func_encode public: Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {} const char *func_name() const { return "decode"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_decode>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_decode>(thd, this); } protected: void crypto_transform(String *); }; @@ -794,8 +794,8 @@ public: } const char *func_name() const { return "database"; } const char *fully_qualified_func_name() const { return "database()"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_database>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_database>(thd, this); } }; @@ -815,8 +815,8 @@ public: max_length= 512 * system_charset_info->mbmaxlen; null_value= maybe_null= false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sqlerrm>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sqlerrm>(thd, this); } }; @@ -847,8 +847,8 @@ public: { return save_str_value_in_field(field, &str_value); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_user>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_user>(thd, this); } }; @@ -897,8 +897,8 @@ public: return mark_unsupported_function(fully_qualified_func_name(), arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_current_role>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_current_role>(thd, this); } }; @@ -910,8 +910,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "soundex"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_soundex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_soundex>(thd, this); } }; @@ -924,8 +924,8 @@ public: String *val_str(String *str); void fix_length_and_dec(); const char *func_name() const { return "elt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_elt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_elt>(thd, this); } }; @@ -938,8 +938,8 @@ public: String *val_str(String *str); void fix_length_and_dec(); const char *func_name() const { return "make_set"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_make_set>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_make_set>(thd, this); } }; @@ -955,8 +955,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "format"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_format>(thd, this); } }; @@ -979,8 +979,8 @@ public: } const char *func_name() const { return "char"; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_char>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_char>(thd, this); } }; class Item_func_chr :public Item_func_char @@ -994,8 +994,8 @@ public: max_length= 4; } const char *func_name() const { return "chr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_chr>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_chr>(thd, this); } }; class Item_func_repeat :public Item_str_func @@ -1007,8 +1007,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "repeat"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_repeat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_repeat>(thd, this); } }; @@ -1019,8 +1019,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "space"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_space>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_space>(thd, this); } }; @@ -1036,8 +1036,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_binlog_gtid_pos>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_binlog_gtid_pos>(thd, this); } }; @@ -1063,8 +1063,8 @@ public: Item_func_pad(thd, arg1, arg2) {} String *val_str(String *); const char *func_name() const { return "rpad"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rpad>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rpad>(thd, this); } }; @@ -1077,8 +1077,8 @@ public: Item_func_pad(thd, arg1, arg2) {} String *val_str(String *); const char *func_name() const { return "lpad"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lpad>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lpad>(thd, this); } }; @@ -1095,8 +1095,8 @@ public: max_length=64; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_conv>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_conv>(thd, this); } }; @@ -1130,8 +1130,8 @@ public: fix_char_length(args[0]->max_length * 2); m_arg0_type_handler= args[0]->type_handler(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_hex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_hex>(thd, this); } }; class Item_func_unhex :public Item_str_func @@ -1151,8 +1151,8 @@ public: decimals=0; max_length=(1+args[0]->max_length)/2; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_unhex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_unhex>(thd, this); } }; @@ -1183,8 +1183,8 @@ public: Item_func_like_range_min(THD *thd, Item *a, Item *b): Item_func_like_range(thd, a, b, true) { } const char *func_name() const { return "like_range_min"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_like_range_min>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_like_range_min>(thd, this); } }; @@ -1194,8 +1194,8 @@ public: Item_func_like_range_max(THD *thd, Item *a, Item *b): Item_func_like_range(thd, a, b, false) { } const char *func_name() const { return "like_range_max"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_like_range_max>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_like_range_max>(thd, this); } }; #endif @@ -1221,8 +1221,8 @@ public: void print(String *str, enum_query_type query_type); const char *func_name() const { return "cast_as_binary"; } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_binary>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_binary>(thd, this); } }; @@ -1243,8 +1243,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_load_file>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_load_file>(thd, this); } }; @@ -1260,8 +1260,8 @@ class Item_func_export_set: public Item_str_func String *val_str(String *str); void fix_length_and_dec(); const char *func_name() const { return "export_set"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_export_set>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_export_set>(thd, this); } }; @@ -1279,8 +1279,8 @@ public: 2 * collation.collation->mbmaxlen; max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_quote>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_quote>(thd, this); } }; class Item_func_conv_charset :public Item_str_func @@ -1363,8 +1363,8 @@ public: void fix_length_and_dec(); const char *func_name() const { return "convert"; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_conv_charset>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_conv_charset>(thd, this); } }; class Item_func_set_collation :public Item_str_func @@ -1385,8 +1385,8 @@ public: return args[0]->field_for_view_update(); } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_set_collation>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_set_collation>(thd, this); } }; @@ -1414,8 +1414,8 @@ public: :Item_func_expr_str_metadata(thd, a) { } String *val_str(String *); const char *func_name() const { return "charset"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_charset>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_charset>(thd, this); } }; @@ -1426,8 +1426,8 @@ public: :Item_func_expr_str_metadata(thd, a) {} String *val_str(String *); const char *func_name() const { return "collation"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_collation>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_collation>(thd, this); } }; @@ -1461,8 +1461,8 @@ public: Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { return this; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_weight_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_weight_string>(thd, this); } }; class Item_func_crc32 :public Item_long_func @@ -1476,8 +1476,8 @@ public: const char *func_name() const { return "crc32"; } void fix_length_and_dec() { max_length=10; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_crc32>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_crc32>(thd, this); } }; class Item_func_uncompressed_length : public Item_long_func_length @@ -1489,8 +1489,8 @@ public: const char *func_name() const{return "uncompressed_length";} void fix_length_and_dec() { max_length=10; maybe_null= true; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uncompressed_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uncompressed_length>(thd, this); } }; #ifdef HAVE_COMPRESS @@ -1508,8 +1508,8 @@ public: void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} const char *func_name() const{return "compress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_compress>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_compress>(thd, this); } }; class Item_func_uncompress: public Item_str_binary_checksum_func @@ -1521,8 +1521,8 @@ public: void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; } const char *func_name() const{return "uncompress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uncompress>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uncompress>(thd, this); } }; @@ -1542,8 +1542,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uuid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uuid>(thd, this); } }; @@ -1565,8 +1565,8 @@ public: String *val_str(String *); void print(String *str, enum_query_type query_type); enum Functype functype() const { return DYNCOL_FUNC; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_create>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_create>(thd, this); } }; @@ -1579,8 +1579,8 @@ public: const char *func_name() const{ return "column_add"; } String *val_str(String *); void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_add>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_add>(thd, this); } }; class Item_func_dyncol_json: public Item_str_func @@ -1596,8 +1596,8 @@ public: maybe_null= 1; decimals= 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_json>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_json>(thd, this); } }; /* @@ -1638,8 +1638,8 @@ public: bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_dyncol_get>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_dyncol_get>(thd, this); } }; @@ -1651,8 +1651,8 @@ public: void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; }; const char *func_name() const{ return "column_list"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_list>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_list>(thd, this); } }; #endif /* ITEM_STRFUNC_INCLUDED */ diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 8e4c5dcbbf1..e47fa17896d 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -29,7 +29,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -115,9 +115,10 @@ void Item_subselect::init(st_select_lex *select_lex, do not take into account expression inside aggregate functions because they can access original table fields */ - parsing_place= (outer_select->in_sum_expr ? NO_MATTER - : outer_select->parsing_place); - if (unit->is_unit_op()) + parsing_place= (outer_select->in_sum_expr ? + NO_MATTER : + outer_select->parsing_place); + if (unit->is_unit_op() && unit->first_select()->next_select()) engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); @@ -263,6 +264,13 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) return TRUE; + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + if (sl->tvc) + { + wrap_tvc_in_derived_table(thd, sl); + } + } if (!(res= engine->prepare(thd))) { @@ -950,7 +958,7 @@ void Item_subselect::print(String *str, enum_query_type query_type) { if (query_type & QT_ITEM_SUBSELECT_ID_ONLY) { - str->append("(subquery#"); + str->append(STRING_WITH_LEN("(subquery#")); if (unit && unit->first_select()) { char buf[64]; @@ -3838,8 +3846,8 @@ int subselect_single_select_engine::exec() { /* Change the access method to full table scan */ tab->save_read_first_record= tab->read_first_record; - tab->save_read_record= tab->read_record.read_record; - tab->read_record.read_record= rr_sequential; + tab->save_read_record= tab->read_record.read_record_func; + tab->read_record.read_record_func= rr_sequential; tab->read_first_record= read_first_record_seq; tab->read_record.record= tab->table->record[0]; tab->read_record.thd= join->thd; @@ -3861,8 +3869,8 @@ int subselect_single_select_engine::exec() JOIN_TAB *tab= *ptab; tab->read_record.record= 0; tab->read_record.ref_length= 0; - tab->read_first_record= tab->save_read_first_record; - tab->read_record.read_record= tab->save_read_record; + tab->read_first_record= tab->save_read_first_record; + tab->read_record.read_record_func= tab->save_read_record; } executed= 1; if (!(uncacheable() & ~UNCACHEABLE_EXPLAIN) && @@ -4370,7 +4378,6 @@ void subselect_union_engine::print(String *str, enum_query_type query_type) void subselect_uniquesubquery_engine::print(String *str, enum_query_type query_type) { - const char *table_name= tab->table->s->table_name.str; str->append(STRING_WITH_LEN("<primary_index_lookup>(")); tab->ref.items[0]->print(str, query_type); str->append(STRING_WITH_LEN(" in ")); @@ -4383,10 +4390,10 @@ void subselect_uniquesubquery_engine::print(String *str, str->append(STRING_WITH_LEN("<temporary table>")); } else - str->append(table_name, tab->table->s->table_name.length); + str->append(&tab->table->s->table_name); KEY *key_info= tab->table->key_info+ tab->ref.key; str->append(STRING_WITH_LEN(" on ")); - str->append(key_info->name); + str->append(&key_info->name); if (cond) { str->append(STRING_WITH_LEN(" where ")); @@ -4407,9 +4414,9 @@ void subselect_uniquesubquery_engine::print(String *str) for (uint i= 0; i < key_info->user_defined_key_parts; i++) tab->ref.items[i]->print(str); str->append(STRING_WITH_LEN(" in ")); - str->append(tab->table->s->table_name.str, tab->table->s->table_name.length); + str->append(&tab->table->s->table_name); str->append(STRING_WITH_LEN(" on ")); - str->append(key_info->name); + str->append(&key_info->name); if (cond) { str->append(STRING_WITH_LEN(" where ")); @@ -4428,7 +4435,7 @@ void subselect_indexsubquery_engine::print(String *str, str->append(tab->table->s->table_name.str, tab->table->s->table_name.length); KEY *key_info= tab->table->key_info+ tab->ref.key; str->append(STRING_WITH_LEN(" on ")); - str->append(key_info->name); + str->append(&key_info->name); if (check_null) str->append(STRING_WITH_LEN(" checking NULL")); if (cond) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 812352e4c4f..55c9d759ddf 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -264,9 +264,10 @@ public: void register_as_with_rec_ref(With_element *with_elem); void init_expr_cache_tracker(THD *thd); - Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* build_clone(THD *thd) { return 0; } + Item* get_copy(THD *thd) { return 0; } + bool wrap_tvc_in_derived_table(THD *thd, st_select_lex *tvc_sl); friend class select_result_interceptor; friend class Item_in_optimizer; @@ -879,6 +880,7 @@ public: virtual enum_engine_type engine_type() { return SINGLE_SELECT_ENGINE; } int get_identifier(); void force_reexecution(); + void change_select(st_select_lex *new_select) { select_lex= new_select; } friend class subselect_hash_sj_engine; friend class Item_in_subselect; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 23e63f709cd..945224c2623 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -26,10 +26,14 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "uniques.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "sql_parse.h" +#include "sp_head.h" /** Calculate the affordable RAM limit for structures like TREE or Unique @@ -1146,17 +1150,19 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) bool Item_sum_hybrid::fix_fields(THD *thd, Item **ref) { + DBUG_ENTER("Item_sum_hybrid::fix_fields"); DBUG_ASSERT(fixed == 0); Item *item= args[0]; if (init_sum_func_check(thd)) - return TRUE; + DBUG_RETURN(TRUE); // 'item' can be changed during fix_fields if ((!item->fixed && item->fix_fields(thd, args)) || (item= args[0])->check_cols(1)) - return TRUE; + DBUG_RETURN(TRUE); + m_with_subquery= args[0]->with_subquery(); with_window_func|= args[0]->with_window_func; @@ -1166,11 +1172,11 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) result_field=0; if (check_sum_func(thd, ref)) - return TRUE; + DBUG_RETURN(TRUE); orig_args[0]= args[0]; fixed= 1; - return FALSE; + DBUG_RETURN(FALSE); } @@ -1201,15 +1207,16 @@ void Item_sum_hybrid::fix_length_and_dec() void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) { + DBUG_ENTER("Item_sum_hybrid::setup_hybrid"); if (!(value= item->get_cache(thd))) - return; + DBUG_VOID_RETURN; value->setup(thd, item); value->store(value_arg); /* Don't cache value, as it will change */ if (!item->const_item()) value->set_used_tables(RAND_TABLE_BIT); if (!(arg_cache= item->get_cache(thd))) - return; + DBUG_VOID_RETURN; arg_cache->setup(thd, item); /* Don't cache value, as it will change */ if (!item->const_item()) @@ -1217,23 +1224,178 @@ void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) cmp= new Arg_comparator(); if (cmp) cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); + DBUG_VOID_RETURN; } Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) { + DBUG_ENTER("Item_sum_hybrid::create_tmp_field"); + if (args[0]->type() == Item::FIELD_ITEM) { Field *field= ((Item_field*) args[0])->field; if ((field= create_tmp_field_from_field(table->in_use, field, &name, table, NULL))) field->flags&= ~NOT_NULL_FLAG; - return field; + DBUG_RETURN(field); } - return tmp_table_field_from_field_type(table); + DBUG_RETURN(tmp_table_field_from_field_type(table)); +} + +/*********************************************************************** +** Item_sum_sp class +***********************************************************************/ + +Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg, sp_head *sp, List<Item> &list) + :Item_sum(thd, list), Item_sp(thd, context_arg, name_arg) +{ + maybe_null= 1; + quick_group= 0; + m_sp= sp; +} + +Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg, sp_head *sp) + :Item_sum(thd), Item_sp(thd, context_arg, name_arg) +{ + maybe_null= 1; + quick_group= 0; + m_sp= sp; } +bool +Item_sum_sp::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + if (init_sum_func_check(thd)) + return TRUE; + decimals= 0; + + m_sp= m_sp ? m_sp : sp_handler_function.sp_find_routine(thd, m_name, true); + + if (!m_sp) + { + my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); + context->process_error(thd); + return TRUE; + } + + if (init_result_field(thd, max_length, maybe_null, &null_value, &name)) + return TRUE; + + for (uint i= 0 ; i < arg_count ; i++) + { + if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) + return TRUE; + set_if_bigger(decimals, args[i]->decimals); + m_with_subquery|= args[i]->with_subquery(); + with_window_func|= args[i]->with_window_func; + } + result_field= NULL; + max_length= float_length(decimals); + null_value= 1; + fix_length_and_dec(); + + if (check_sum_func(thd, ref)) + return TRUE; + + memcpy(orig_args, args, sizeof(Item *) * arg_count); + fixed= 1; + return FALSE; +} + +/** + Execute function to store value in result field. + This is called when we need the value to be returned for the function. + Here we send a signal in form of the server status that all rows have been + fetched and now we have to exit from the function with the return value. + @return Function returns error status. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ + +bool +Item_sum_sp::execute() +{ + THD *thd= current_thd; + bool res; + uint old_server_status= thd->server_status; + + /* We set server status so we can send a signal to exit from the + function with the return value. */ + + thd->server_status= SERVER_STATUS_LAST_ROW_SENT; + res= Item_sp::execute(thd, &null_value, args, arg_count); + thd->server_status= old_server_status; + return res; +} + +/** + Handles the aggregation of the values. + @note: See class description for more details on how and why this is done. + @return The error state. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ + +bool +Item_sum_sp::add() +{ + return execute_impl(current_thd, args, arg_count); +} + + +void +Item_sum_sp::clear() +{ + delete func_ctx; + func_ctx= NULL; + sp_query_arena->free_items(); + free_root(&sp_mem_root, MYF(0)); +} + +const Type_handler *Item_sum_sp::type_handler() const +{ + DBUG_ENTER("Item_sum_sp::type_handler"); + DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp)); + DBUG_ASSERT(sp_result_field); + // This converts ENUM/SET to STRING + const Type_handler *handler= sp_result_field->type_handler(); + DBUG_RETURN(handler->type_handler_for_item_field()); +} + +void +Item_sum_sp::cleanup() +{ + Item_sp::cleanup(); + Item_sum::cleanup(); +} + +/** + Initialize local members with values from the Field interface. + @note called from Item::fix_fields. +*/ + +void +Item_sum_sp::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_sp::fix_length_and_dec"); + DBUG_ASSERT(sp_result_field); + Type_std_attributes::set(sp_result_field); + Item_sum::fix_length_and_dec(); + DBUG_VOID_RETURN; +} + +const char * +Item_sum_sp::func_name() const +{ + THD *thd= current_thd; + return Item_sp::func_name(thd); +} + /*********************************************************************** ** reset and add of sum_func ***********************************************************************/ @@ -1245,6 +1407,7 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item) :Item_sum_num(thd, item), Type_handler_hybrid_field_type(item), + direct_added(FALSE), direct_reseted_field(FALSE), curr_dec_buff(item->curr_dec_buff), count(item->count) { @@ -1264,6 +1427,15 @@ Item *Item_sum_sum::copy_or_same(THD* thd) } +void Item_sum_sum::cleanup() +{ + DBUG_ENTER("Item_sum_sum::cleanup"); + direct_added= direct_reseted_field= FALSE; + Item_sum_num::cleanup(); + DBUG_VOID_RETURN; +} + + void Item_sum_sum::clear() { DBUG_ENTER("Item_sum_sum::clear"); @@ -1313,6 +1485,38 @@ void Item_sum_sum::fix_length_and_dec() } +void Item_sum_sum::direct_add(my_decimal *add_sum_decimal) +{ + DBUG_ENTER("Item_sum_sum::direct_add"); + DBUG_PRINT("info", ("add_sum_decimal: %p", add_sum_decimal)); + direct_added= TRUE; + direct_reseted_field= FALSE; + if (add_sum_decimal) + { + direct_sum_is_null= FALSE; + direct_sum_decimal= *add_sum_decimal; + } + else + { + direct_sum_is_null= TRUE; + direct_sum_decimal= decimal_zero; + } + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::direct_add(double add_sum_real, bool add_sum_is_null) +{ + DBUG_ENTER("Item_sum_sum::direct_add"); + DBUG_PRINT("info", ("add_sum_real: %f", add_sum_real)); + direct_added= TRUE; + direct_reseted_field= FALSE; + direct_sum_is_null= add_sum_is_null; + direct_sum_real= add_sum_real; + DBUG_VOID_RETURN; +} + + bool Item_sum_sum::add() { DBUG_ENTER("Item_sum_sum::add"); @@ -1326,50 +1530,84 @@ void Item_sum_sum::add_helper(bool perform_removal) if (result_type() == DECIMAL_RESULT) { - my_decimal value; - const my_decimal *val= aggr->arg_val_decimal(&value); - if (!aggr->arg_is_null(true)) + if (unlikely(direct_added)) { - if (perform_removal) + /* Add value stored by Item_sum_sum::direct_add */ + DBUG_ASSERT(!perform_removal); + + direct_added= FALSE; + if (likely(!direct_sum_is_null)) { - if (count > 0) + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), + &direct_sum_decimal, dec_buffs + curr_dec_buff); + curr_dec_buff^= 1; + null_value= 0; + } + } + else + { + direct_reseted_field= FALSE; + my_decimal value; + const my_decimal *val= aggr->arg_val_decimal(&value); + if (!aggr->arg_is_null(true)) + { + if (perform_removal) { - my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), - dec_buffs + curr_dec_buff, val); - count--; + if (count > 0) + { + my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), + dec_buffs + curr_dec_buff, val); + count--; + } + else + DBUG_VOID_RETURN; } else - DBUG_VOID_RETURN; - } - else - { - count++; - my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), - val, dec_buffs + curr_dec_buff); + { + count++; + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), + val, dec_buffs + curr_dec_buff); + } + curr_dec_buff^= 1; + null_value= (count > 0) ? 0 : 1; } - curr_dec_buff^= 1; - null_value= (count > 0) ? 0 : 1; } } else { - if (perform_removal && count > 0) - sum-= aggr->arg_val_real(); + if (unlikely(direct_added)) + { + /* Add value stored by Item_sum_sum::direct_add */ + DBUG_ASSERT(!perform_removal); + + direct_added= FALSE; + if (!direct_sum_is_null) + { + sum+= direct_sum_real; + null_value= 0; + } + } else - sum+= aggr->arg_val_real(); - if (!aggr->arg_is_null(true)) { - if (perform_removal) + direct_reseted_field= FALSE; + if (perform_removal && count > 0) + sum-= aggr->arg_val_real(); + else + sum+= aggr->arg_val_real(); + if (!aggr->arg_is_null(true)) { - if (count > 0) + if (perform_removal) { - count--; + if (count > 0) + { + count--; + } } - } - else - count++; + else + count++; - null_value= (count > 0) ? 0 : 1; + null_value= (count > 0) ? 0 : 1; + } } } DBUG_VOID_RETURN; @@ -1556,22 +1794,46 @@ bool Aggregator_distinct::arg_is_null(bool use_null_value) Item *Item_sum_count::copy_or_same(THD* thd) { - return new (thd->mem_root) Item_sum_count(thd, this); + DBUG_ENTER("Item_sum_count::copy_or_same"); + DBUG_RETURN(new (thd->mem_root) Item_sum_count(thd, this)); +} + + +void Item_sum_count::direct_add(longlong add_count) +{ + DBUG_ENTER("Item_sum_count::direct_add"); + DBUG_PRINT("info", ("add_count: %lld", add_count)); + direct_counted= TRUE; + direct_reseted_field= FALSE; + direct_count= add_count; + DBUG_VOID_RETURN; } void Item_sum_count::clear() { + DBUG_ENTER("Item_sum_count::clear"); count= 0; + DBUG_VOID_RETURN; } bool Item_sum_count::add() { - if (aggr->arg_is_null(false)) - return 0; - count++; - return 0; + DBUG_ENTER("Item_sum_count::add"); + if (direct_counted) + { + direct_counted= FALSE; + count+= direct_count; + } + else + { + direct_reseted_field= FALSE; + if (aggr->arg_is_null(false)) + DBUG_RETURN(0); + count++; + } + DBUG_RETURN(0); } @@ -1590,10 +1852,11 @@ void Item_sum_count::remove() longlong Item_sum_count::val_int() { + DBUG_ENTER("Item_sum_count::val_int"); DBUG_ASSERT(fixed == 1); if (aggr) aggr->endup(); - return (longlong) count; + DBUG_RETURN((longlong)count); } @@ -1601,6 +1864,8 @@ void Item_sum_count::cleanup() { DBUG_ENTER("Item_sum_count::cleanup"); count= 0; + direct_counted= FALSE; + direct_reseted_field= FALSE; Item_sum_int::cleanup(); DBUG_VOID_RETURN; } @@ -2015,10 +2280,13 @@ Item *Item_sum_variance::result_item(THD *thd, Field *field) void Item_sum_hybrid::clear() { + DBUG_ENTER("Item_sum_hybrid::clear"); value->clear(); null_value= 1; + DBUG_VOID_RETURN; } + bool Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { @@ -2031,51 +2299,66 @@ Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) return retval; } + +void Item_sum_hybrid::direct_add(Item *item) +{ + DBUG_ENTER("Item_sum_hybrid::direct_add"); + DBUG_PRINT("info", ("item: %p", item)); + direct_added= TRUE; + direct_item= item; + DBUG_VOID_RETURN; +} + + double Item_sum_hybrid::val_real() { + DBUG_ENTER("Item_sum_hybrid::val_real"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0.0; + DBUG_RETURN(0.0); double retval= value->val_real(); if ((null_value= value->null_value)) DBUG_ASSERT(retval == 0.0); - return retval; + DBUG_RETURN(retval); } longlong Item_sum_hybrid::val_int() { + DBUG_ENTER("Item_sum_hybrid::val_int"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0; + DBUG_RETURN(0); longlong retval= value->val_int(); if ((null_value= value->null_value)) DBUG_ASSERT(retval == 0); - return retval; + DBUG_RETURN(retval); } my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val) { + DBUG_ENTER("Item_sum_hybrid::val_decimal"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0; + DBUG_RETURN(0); my_decimal *retval= value->val_decimal(val); if ((null_value= value->null_value)) DBUG_ASSERT(retval == NULL); - return retval; + DBUG_RETURN(retval); } String * Item_sum_hybrid::val_str(String *str) { + DBUG_ENTER("Item_sum_hybrid::val_str"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0; + DBUG_RETURN(0); String *retval= value->val_str(str); if ((null_value= value->null_value)) DBUG_ASSERT(retval == NULL); - return retval; + DBUG_RETURN(retval); } @@ -2099,6 +2382,7 @@ void Item_sum_hybrid::cleanup() void Item_sum_hybrid::no_rows_in_result() { + DBUG_ENTER("Item_sum_hybrid::no_rows_in_result"); /* We may be called here twice in case of ref field in function */ if (was_values) { @@ -2106,6 +2390,7 @@ void Item_sum_hybrid::no_rows_in_result() was_null_value= value->null_value; clear(); } + DBUG_VOID_RETURN; } void Item_sum_hybrid::restore_to_before_no_rows_in_result() @@ -2120,14 +2405,26 @@ void Item_sum_hybrid::restore_to_before_no_rows_in_result() Item *Item_sum_min::copy_or_same(THD* thd) { + DBUG_ENTER("Item_sum_min::copy_or_same"); Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this); item->setup_hybrid(thd, args[0], value); - return item; + DBUG_RETURN(item); } bool Item_sum_min::add() { + Item *tmp_item; + DBUG_ENTER("Item_sum_min::add"); + DBUG_PRINT("enter", ("this: %p", this)); + + if (unlikely(direct_added)) + { + /* Change to use direct_item */ + tmp_item= arg_cache->get_item(); + arg_cache->store(direct_item); + } + DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE")); /* args[0] < value */ arg_cache->cache_value(); if (!arg_cache->null_value && @@ -2137,7 +2434,13 @@ bool Item_sum_min::add() value->cache_value(); null_value= 0; } - return 0; + if (unlikely(direct_added)) + { + /* Restore original item */ + direct_added= FALSE; + arg_cache->store(tmp_item); + } + DBUG_RETURN(0); } @@ -2151,8 +2454,19 @@ Item *Item_sum_max::copy_or_same(THD* thd) bool Item_sum_max::add() { + Item *tmp_item; + DBUG_ENTER("Item_sum_max::add"); + DBUG_PRINT("enter", ("this: %p", this)); + + if (unlikely(direct_added)) + { + /* Change to use direct_item */ + tmp_item= arg_cache->get_item(); + arg_cache->store(direct_item); + } /* args[0] > value */ arg_cache->cache_value(); + DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE")); if (!arg_cache->null_value && (null_value || cmp->compare() > 0)) { @@ -2160,7 +2474,13 @@ bool Item_sum_max::add() value->cache_value(); null_value= 0; } - return 0; + if (unlikely(direct_added)) + { + /* Restore original item */ + direct_added= FALSE; + arg_cache->store(tmp_item); + } + DBUG_RETURN(0); } @@ -2339,14 +2659,26 @@ void Item_sum_num::reset_field() void Item_sum_hybrid::reset_field() { + Item *tmp_item, *arg0; + DBUG_ENTER("Item_sum_hybrid::reset_field"); + + arg0= args[0]; + if (unlikely(direct_added)) + { + /* Switch to use direct item */ + tmp_item= value->get_item(); + value->store(direct_item); + arg0= direct_item; + } + switch(result_type()) { case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),result_field->charset()),*res; - res=args[0]->val_str(&tmp); - if (args[0]->null_value) + res= arg0->val_str(&tmp); + if (arg0->null_value) { result_field->set_null(); result_field->reset(); @@ -2360,11 +2692,11 @@ void Item_sum_hybrid::reset_field() } case INT_RESULT: { - longlong nr=args[0]->val_int(); + longlong nr= arg0->val_int(); if (maybe_null) { - if (args[0]->null_value) + if (arg0->null_value) { nr=0; result_field->set_null(); @@ -2372,16 +2704,17 @@ void Item_sum_hybrid::reset_field() else result_field->set_notnull(); } + DBUG_PRINT("info", ("nr: %lld", nr)); result_field->store(nr, unsigned_flag); break; } case REAL_RESULT: { - double nr= args[0]->val_real(); + double nr= arg0->val_real(); if (maybe_null) { - if (args[0]->null_value) + if (arg0->null_value) { nr=0.0; result_field->set_null(); @@ -2394,11 +2727,11 @@ void Item_sum_hybrid::reset_field() } case DECIMAL_RESULT: { - my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff); + my_decimal value_buff, *arg_dec= arg0->val_decimal(&value_buff); if (maybe_null) { - if (args[0]->null_value) + if (arg0->null_value) result_field->set_null(); else result_field->set_notnull(); @@ -2416,26 +2749,49 @@ void Item_sum_hybrid::reset_field() case TIME_RESULT: DBUG_ASSERT(0); } + + if (unlikely(direct_added)) + { + direct_added= FALSE; + value->store(tmp_item); + } + DBUG_VOID_RETURN; } void Item_sum_sum::reset_field() { + my_bool null_flag; DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (result_type() == DECIMAL_RESULT) { - my_decimal value, *arg_val= args[0]->val_decimal(&value); - if (!arg_val) // Null - arg_val= &decimal_zero; + my_decimal value, *arg_val; + if (unlikely(direct_added)) + arg_val= &direct_sum_decimal; + else + { + if (!(arg_val= args[0]->val_decimal(&value))) + arg_val= &decimal_zero; // Null + } result_field->store_decimal(arg_val); } else { DBUG_ASSERT(result_type() == REAL_RESULT); - double nr= args[0]->val_real(); // Nulls also return 0 + double nr= likely(!direct_added) ? args[0]->val_real() : direct_sum_real; float8store(result_field->ptr, nr); } - if (args[0]->null_value) + + if (unlikely(direct_added)) + { + direct_added= FALSE; + direct_reseted_field= TRUE; + null_flag= direct_sum_is_null; + } + else + null_flag= args[0]->null_value; + + if (null_flag) result_field->set_null(); else result_field->set_notnull(); @@ -2444,13 +2800,22 @@ void Item_sum_sum::reset_field() void Item_sum_count::reset_field() { + DBUG_ENTER("Item_sum_count::reset_field"); uchar *res=result_field->ptr; longlong nr=0; DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); - if (!args[0]->maybe_null || !args[0]->is_null()) - nr=1; + if (unlikely(direct_counted)) + { + nr= direct_count; + direct_counted= FALSE; + direct_reseted_field= TRUE; + } + else if (!args[0]->maybe_null || !args[0]->is_null()) + nr= 1; + DBUG_PRINT("info", ("nr: %lld", nr)); int8store(res,nr); + DBUG_VOID_RETURN; } @@ -2518,13 +2883,26 @@ void Item_sum_sum::update_field() DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (result_type() == DECIMAL_RESULT) { - my_decimal value, *arg_val= args[0]->val_decimal(&value); - if (!args[0]->null_value) + my_decimal value, *arg_val; + my_bool null_flag; + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= direct_reseted_field= FALSE; + arg_val= &direct_sum_decimal; + null_flag= direct_sum_is_null; + } + else + { + arg_val= args[0]->val_decimal(&value); + null_flag= args[0]->null_value; + } + + if (!null_flag) { if (!result_field->is_null()) { - my_decimal field_value, - *field_val= result_field->val_decimal(&field_value); + my_decimal field_value; + my_decimal *field_val= result_field->val_decimal(&field_value); my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val); result_field->store_decimal(dec_buffs); } @@ -2538,11 +2916,22 @@ void Item_sum_sum::update_field() else { double old_nr,nr; - uchar *res=result_field->ptr; + uchar *res= result_field->ptr; + my_bool null_flag; float8get(old_nr,res); - nr= args[0]->val_real(); - if (!args[0]->null_value) + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= direct_reseted_field= FALSE; + null_flag= direct_sum_is_null; + nr= direct_sum_real; + } + else + { + nr= args[0]->val_real(); + null_flag= args[0]->null_value; + } + if (!null_flag) { old_nr+=nr; result_field->set_notnull(); @@ -2554,13 +2943,21 @@ void Item_sum_sum::update_field() void Item_sum_count::update_field() { + DBUG_ENTER("Item_sum_count::update_field"); longlong nr; uchar *res=result_field->ptr; nr=sint8korr(res); - if (!args[0]->maybe_null || !args[0]->is_null()) + if (unlikely(direct_counted || direct_reseted_field)) + { + direct_counted= direct_reseted_field= FALSE; + nr+= direct_count; + } + else if (!args[0]->maybe_null || !args[0]->is_null()) nr++; + DBUG_PRINT("info", ("nr: %lld", nr)); int8store(res,nr); + DBUG_VOID_RETURN; } @@ -2618,6 +3015,13 @@ Item *Item_sum_avg::result_item(THD *thd, Field *field) void Item_sum_hybrid::update_field() { + DBUG_ENTER("Item_sum_hybrid::update_field"); + Item *tmp_item; + if (unlikely(direct_added)) + { + tmp_item= args[0]; + args[0]= direct_item; + } switch (result_type()) { case STRING_RESULT: min_max_update_str_field(); @@ -2631,12 +3035,19 @@ void Item_sum_hybrid::update_field() default: min_max_update_real_field(); } + if (unlikely(direct_added)) + { + direct_added= FALSE; + args[0]= tmp_item; + } + DBUG_VOID_RETURN; } void Item_sum_hybrid::min_max_update_str_field() { + DBUG_ENTER("Item_sum_hybrid::min_max_update_str_field"); DBUG_ASSERT(cmp); String *res_str=args[0]->val_str(&cmp->value1); @@ -2649,6 +3060,7 @@ Item_sum_hybrid::min_max_update_str_field() result_field->store(res_str->ptr(),res_str->length(),res_str->charset()); result_field->set_notnull(); } + DBUG_VOID_RETURN; } @@ -2657,6 +3069,7 @@ Item_sum_hybrid::min_max_update_real_field() { double nr,old_nr; + DBUG_ENTER("Item_sum_hybrid::min_max_update_real_field"); old_nr=result_field->val_real(); nr= args[0]->val_real(); if (!args[0]->null_value) @@ -2669,6 +3082,7 @@ Item_sum_hybrid::min_max_update_real_field() else if (result_field->is_null(0)) result_field->set_null(); result_field->store(old_nr); + DBUG_VOID_RETURN; } @@ -2677,6 +3091,7 @@ Item_sum_hybrid::min_max_update_int_field() { longlong nr,old_nr; + DBUG_ENTER("Item_sum_hybrid::min_max_update_int_field"); old_nr=result_field->val_int(); nr=args[0]->val_int(); if (!args[0]->null_value) @@ -2696,7 +3111,9 @@ Item_sum_hybrid::min_max_update_int_field() } else if (result_field->is_null(0)) result_field->set_null(); + DBUG_PRINT("info", ("nr: %lld", old_nr)); result_field->store(old_nr, unsigned_flag); + DBUG_VOID_RETURN; } @@ -2707,6 +3124,7 @@ Item_sum_hybrid::min_max_update_int_field() void Item_sum_hybrid::min_max_update_decimal_field() { + DBUG_ENTER("Item_sum_hybrid::min_max_update_decimal_field"); my_decimal old_val, nr_val; const my_decimal *old_nr; const my_decimal *nr= args[0]->val_decimal(&nr_val); @@ -2727,6 +3145,7 @@ Item_sum_hybrid::min_max_update_decimal_field() } else if (result_field->is_null(0)) result_field->set_null(); + DBUG_VOID_RETURN; } @@ -2850,7 +3269,7 @@ double Item_sum_udf_float::val_real() double res; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_float::val"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", + DBUG_PRINT("enter",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); res= udf.val(&tmp_null_value); null_value= tmp_null_value; @@ -2894,7 +3313,7 @@ my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf) my_bool tmp_null_value; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_decimal::val_decimal"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", + DBUG_PRINT("enter",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); res= udf.val_decimal(&tmp_null_value, dec_buf); @@ -2920,7 +3339,7 @@ longlong Item_sum_udf_int::val_int() longlong res; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_int::val_int"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", + DBUG_PRINT("enter",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); res= udf.val_int(&tmp_null_value); null_value= tmp_null_value; @@ -3114,6 +3533,11 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), Item **arg= item->args, **arg_end= item->args + item->arg_count_field; uint old_length= result->length(); + ulonglong *offset_limit= &item->copy_offset_limit; + ulonglong *row_limit = &item->copy_row_limit; + if (item->limit_clause && !(*row_limit)) + return 1; + if (item->no_appended) item->no_appended= FALSE; else @@ -3121,6 +3545,14 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), tmp.length(0); + if (item->limit_clause && (*offset_limit)) + { + item->row_count++; + item->no_appended= TRUE; + (*offset_limit)--; + return 0; + } + for (; arg < arg_end; arg++) { String *res; @@ -3150,6 +3582,8 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), result->append(*res); } + if (item->limit_clause) + (*row_limit)--; item->row_count++; /* stop if length of result more than max_length */ @@ -3198,7 +3632,8 @@ Item_func_group_concat:: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool distinct_arg, List<Item> *select_list, const SQL_I_List<ORDER> &order_list, - String *separator_arg) + String *separator_arg, bool limit_clause, + Item *row_limit_arg, Item *offset_limit_arg) :Item_sum(thd), tmp_table_param(0), separator(separator_arg), tree(0), unique_filter(NULL), table(0), order(0), context(context_arg), @@ -3207,7 +3642,9 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, row_count(0), distinct(distinct_arg), warning_for_row(FALSE), - force_copy_fields(0), original(0) + force_copy_fields(0), row_limit(NULL), + offset_limit(NULL), limit_clause(limit_clause), + copy_offset_limit(0), copy_row_limit(0), original(0) { Item *item_select; Item **arg_ptr; @@ -3249,6 +3686,11 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, /* orig_args is only used for print() */ orig_args= (Item**) (order + arg_count_order); memcpy(orig_args, args, sizeof(Item*) * arg_count); + if (limit_clause) + { + row_limit= row_limit_arg; + offset_limit= offset_limit_arg; + } } @@ -3268,7 +3710,9 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, warning_for_row(item->warning_for_row), always_null(item->always_null), force_copy_fields(item->force_copy_fields), - original(item) + row_limit(item->row_limit), offset_limit(item->offset_limit), + limit_clause(item->limit_clause),copy_offset_limit(item->copy_offset_limit), + copy_row_limit(item->copy_row_limit), original(item) { quick_group= item->quick_group; result.set_charset(collation.collation); @@ -3322,7 +3766,7 @@ void Item_func_group_concat::cleanup() table= 0; if (tree) { - delete_tree(tree); + delete_tree(tree, 0); tree= 0; } if (unique_filter) @@ -3363,6 +3807,10 @@ void Item_func_group_concat::clear() null_value= TRUE; warning_for_row= FALSE; no_appended= TRUE; + if (offset_limit) + copy_offset_limit= offset_limit->val_int(); + if (row_limit) + copy_row_limit= row_limit->val_int(); if (tree) reset_tree(tree); if (unique_filter) @@ -3617,6 +4065,12 @@ bool Item_func_group_concat::setup(THD *thd) (void*)this, tree_key_length, ram_limitation(thd)); + if ((row_limit && row_limit->cmp_type() != INT_RESULT) || + (offset_limit && offset_limit->cmp_type() != INT_RESULT)) + { + my_error(ER_INVALID_VALUE_TO_LIMIT, MYF(0)); + DBUG_RETURN(TRUE); + } DBUG_RETURN(FALSE); } diff --git a/sql/item_sum.h b/sql/item_sum.h index 0cb5be391a2..cbf245f3cd3 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -354,7 +354,8 @@ public: VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC, ROW_NUMBER_FUNC, RANK_FUNC, DENSE_RANK_FUNC, PERCENT_RANK_FUNC, CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC, - NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC + NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC, PERCENTILE_CONT_FUNC, + PERCENTILE_DISC_FUNC, SP_AGGREGATE_FUNC }; Item **ref_by; /* pointer to a ref to the object used to register it */ @@ -520,6 +521,7 @@ public: Item *get_arg(uint i) const { return args[i]; } Item *set_arg(uint i, THD *thd, Item *new_val); uint get_arg_count() const { return arg_count; } + virtual Item **get_args() { return fixed ? orig_args : args; } /* Initialization of distinct related members */ void init_aggregator() @@ -756,14 +758,20 @@ class Item_sum_sum :public Item_sum_num, public Type_handler_hybrid_field_type { protected: + bool direct_added; + bool direct_reseted_field; + bool direct_sum_is_null; + double direct_sum_real; double sum; + my_decimal direct_sum_decimal; my_decimal dec_buffs[2]; uint curr_dec_buff; void fix_length_and_dec(); public: Item_sum_sum(THD *thd, Item *item_par, bool distinct): - Item_sum_num(thd, item_par) + Item_sum_num(thd, item_par), direct_added(FALSE), + direct_reseted_field(FALSE) { set_distinct(distinct); } @@ -772,6 +780,9 @@ public: { return has_with_distinct() ? SUM_DISTINCT_FUNC : SUM_FUNC; } + void cleanup(); + void direct_add(my_decimal *add_sum_decimal); + void direct_add(double add_sum_real, bool add_sum_is_null); void clear(); bool add(); double val_real(); @@ -791,8 +802,8 @@ public: } Item *copy_or_same(THD* thd); void remove(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_sum>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_sum>(thd, this); } bool supports_removal() const { @@ -807,6 +818,9 @@ private: class Item_sum_count :public Item_sum_int { + bool direct_counted; + bool direct_reseted_field; + longlong direct_count; longlong count; friend class Aggregator_distinct; @@ -816,9 +830,10 @@ class Item_sum_count :public Item_sum_int void cleanup(); void remove(); - public: +public: Item_sum_count(THD *thd, Item *item_par): - Item_sum_int(thd, item_par), count(0) + Item_sum_int(thd, item_par), direct_counted(FALSE), + direct_reseted_field(FALSE), count(0) {} /** @@ -830,12 +845,14 @@ class Item_sum_count :public Item_sum_int */ Item_sum_count(THD *thd, List<Item> &list): - Item_sum_int(thd, list), count(0) + Item_sum_int(thd, list), direct_counted(FALSE), + direct_reseted_field(FALSE), count(0) { set_distinct(TRUE); } Item_sum_count(THD *thd, Item_sum_count *item): - Item_sum_int(thd, item), count(item->count) + Item_sum_int(thd, item), direct_counted(FALSE), + direct_reseted_field(FALSE), count(item->count) {} enum Sumfunctype sum_func () const { @@ -850,13 +867,14 @@ class Item_sum_count :public Item_sum_int longlong val_int(); void reset_field(); void update_field(); + void direct_add(longlong add_count); const char *func_name() const { return has_with_distinct() ? "count(distinct " : "count("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_count>(thd, this); } bool supports_removal() const { @@ -911,8 +929,8 @@ public: count= 0; Item_sum_sum::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_avg>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_avg>(thd, this); } bool supports_removal() const { @@ -977,8 +995,8 @@ public: count= 0; Item_sum_num::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_variance>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_variance>(thd, this); } }; /* @@ -998,8 +1016,8 @@ class Item_sum_std :public Item_sum_variance Item *result_item(THD *thd, Field *field); const char *func_name() const { return "std("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_std>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_std>(thd, this); } }; // This class is a string or number function depending on num_func @@ -1008,6 +1026,8 @@ class Item_cache; class Item_sum_hybrid :public Item_sum, public Type_handler_hybrid_field_type { protected: + bool direct_added; + Item *direct_item; Item_cache *value, *arg_cache; Arg_comparator *cmp; int cmp_sign; @@ -1018,19 +1038,20 @@ protected: Item_sum_hybrid(THD *thd, Item *item_par,int sign): Item_sum(thd, item_par), Type_handler_hybrid_field_type(&type_handler_longlong), - value(0), arg_cache(0), cmp(0), + direct_added(FALSE), value(0), arg_cache(0), cmp(0), cmp_sign(sign), was_values(TRUE) { collation.set(&my_charset_bin); } Item_sum_hybrid(THD *thd, Item_sum_hybrid *item) :Item_sum(thd, item), Type_handler_hybrid_field_type(item), - value(item->value), arg_cache(0), + direct_added(FALSE), value(item->value), arg_cache(0), cmp_sign(item->cmp_sign), was_values(item->was_values) { } bool fix_fields(THD *, Item **); void fix_length_and_dec(); void setup_hybrid(THD *thd, Item *item, Item *value_arg); void clear(); + void direct_add(Item *item); double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); @@ -1068,8 +1089,8 @@ public: bool add(); const char *func_name() const { return "min("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_min>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_min>(thd, this); } }; @@ -1083,8 +1104,8 @@ public: bool add(); const char *func_name() const { return "max("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_max>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_max>(thd, this); } }; @@ -1164,8 +1185,8 @@ public: bool add(); const char *func_name() const { return "bit_or("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_or>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_or>(thd, this); } private: void set_bits_from_counters(); @@ -1181,8 +1202,8 @@ public: bool add(); const char *func_name() const { return "bit_and("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_and>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_and>(thd, this); } private: void set_bits_from_counters(); @@ -1196,13 +1217,139 @@ public: bool add(); const char *func_name() const { return "bit_xor("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_xor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_xor>(thd, this); } private: void set_bits_from_counters(); }; +class sp_head; +class sp_name; +class Query_arena; +struct st_sp_security_context; + +/* + Item_sum_sp handles STORED AGGREGATE FUNCTIONS + + Each Item_sum_sp represents a custom aggregate function. Inside the + function's body, we require at least one occurence of FETCH GROUP NEXT ROW + instruction. This cursor is what makes custom stored aggregates possible. + + During computation the function's add method is called. This in turn performs + an execution of the function. The function will execute from the current + function context (and instruction), if one exists, or from the start if not. + See Item_sp for more details. + + Upon encounter of FETCH GROUP NEXT ROW instruction, the function will pause + execution. We assume that the user has performed the necessary additions for + a row, between two encounters of FETCH GROUP NEXT ROW. + + Example: + create aggregate function f1(x INT) returns int + begin + declare continue handler for not found return s; + declare s int default 0 + loop + fetch group next row; + set s = s + x; + end loop; + end + + The function will always stop after an encounter of FETCH GROUP NEXT ROW, + except (!) on first encounter, as the value for the first row in the + group is already set in the argument x. This behaviour is done so when + a user writes a function, he should "logically" include FETCH GROUP NEXT ROW + before any "add" instructions in the stored function. This means however that + internally, the first occurence doesn't stop the function. See the + implementation of FETCH GROUP NEXT ROW for details as to how it happens. + + Either way, one should assume that after calling "Item_sum_sp::add()" that + the values for that particular row have been added to the aggregation. + + To produce values for val_xxx methods we need an extra syntactic construct. + We require a continue handler when "no more rows are available". val_xxx + methods force a function return by executing the function again, while + setting a server flag that no more rows have been found. This implies + that val_xxx methods should only be called once per group however. + + Example: + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ret_val; +*/ +class Item_sum_sp :public Item_sum, + public Item_sp +{ + private: + bool execute(); + +public: + Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name, + sp_head *sp); + + Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name, + sp_head *sp, List<Item> &list); + + enum Sumfunctype sum_func () const + { + return SP_AGGREGATE_FUNC; + } + void fix_length_and_dec(); + bool fix_fields(THD *thd, Item **ref); + const char *func_name() const; + const Type_handler *type_handler() const; + bool add(); + + /* val_xx functions */ + longlong val_int() + { + if(execute()) + return 0; + return sp_result_field->val_int(); + } + + double val_real() + { + if(execute()) + return 0.0; + return sp_result_field->val_real(); + } + + my_decimal *val_decimal(my_decimal *dec_buf) + { + if(execute()) + return NULL; + return sp_result_field->val_decimal(dec_buf); + } + + String *val_str(String *str) + { + String buf; + char buff[20]; + buf.set(buff, 20, str->charset()); + buf.length(0); + if (execute()) + return NULL; + /* + result_field will set buf pointing to internal buffer + of the resul_field. Due to this it will change any time + when SP is executed. In order to prevent occasional + corruption of returned value, we make here a copy. + */ + sp_result_field->val_str(&buf); + str->copy(buf); + return str; + } + void reset_field(){DBUG_ASSERT(0);} + void update_field(){DBUG_ASSERT(0);} + void clear(); + void cleanup(); + inline Field *get_sp_result_field() + { + return sp_result_field; + } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_sp>(thd, this); } +}; /* Items to get the value of a stored sum function */ @@ -1254,8 +1401,8 @@ public: my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_real(dec); } String *val_str(String *str) { return val_string_from_real(str); } double val_real(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_avg_field_double>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_avg_field_double>(thd, this); } }; @@ -1274,8 +1421,8 @@ public: longlong val_int() { return val_int_from_decimal(); } String *val_str(String *str) { return val_string_from_decimal(str); } my_decimal *val_decimal(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_avg_field_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_avg_field_decimal>(thd, this); } }; @@ -1295,8 +1442,8 @@ public: { return val_decimal_from_real(dec_buf); } bool is_null() { update_null_value(); return null_value; } const Type_handler *type_handler() const { return &type_handler_double; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_variance_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_variance_field>(thd, this); } }; @@ -1308,8 +1455,8 @@ public: { } enum Type type() const { return FIELD_STD_ITEM; } double val_real(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_std_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_std_field>(thd, this); } }; @@ -1395,8 +1542,8 @@ class Item_sum_udf_float :public Item_udf_sum const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_float>(thd, this); } }; @@ -1417,8 +1564,8 @@ public: const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals=0; max_length=21; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_int>(thd, this); } }; @@ -1458,8 +1605,8 @@ public: const Type_handler *type_handler() const { return string_type_handler(); } void fix_length_and_dec(); Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_str>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_str>(thd, this); } }; @@ -1479,8 +1626,8 @@ public: const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_decimal>(thd, this); } }; #else /* Dummy functions to get sql_yacc.cc compiled */ @@ -1600,6 +1747,16 @@ class Item_func_group_concat : public Item_sum bool always_null; bool force_copy_fields; bool no_appended; + /** Limits the rows in the result */ + Item *row_limit; + /** Skips a particular number of rows in from the result*/ + Item *offset_limit; + bool limit_clause; + /* copy of the offset limit */ + ulonglong copy_offset_limit; + /*copy of the row limit */ + ulonglong copy_row_limit; + /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) @@ -1616,7 +1773,8 @@ class Item_func_group_concat : public Item_sum public: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool is_distinct, List<Item> *is_select, - const SQL_I_List<ORDER> &is_order, String *is_separator); + const SQL_I_List<ORDER> &is_order, String *is_separator, + bool limit_clause, Item *row_limit, Item *offset_limit); Item_func_group_concat(THD *thd, Item_func_group_concat *item); ~Item_func_group_concat(); @@ -1667,8 +1825,8 @@ public: virtual void print(String *str, enum_query_type query_type); virtual bool change_context_processor(void *cntx) { context= (Name_resolution_context *)cntx; return FALSE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_group_concat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_group_concat>(thd, this); } }; #endif /* ITEM_SUM_INCLUDED */ diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index a0fdd4bb8fc..fd170a707c9 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -30,7 +30,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index f94a0d385f7..adc7b2535a9 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -66,8 +66,8 @@ public: { max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_period_add>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_period_add>(thd, this); } }; @@ -84,8 +84,8 @@ public: decimals=0; max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_period_diff>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_period_diff>(thd, this); } }; @@ -109,8 +109,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_to_days>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_to_days>(thd, this); } }; @@ -137,8 +137,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_to_seconds>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_to_seconds>(thd, this); } }; @@ -160,8 +160,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dayofmonth>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dayofmonth>(thd, this); } }; @@ -195,8 +195,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_month>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_month>(thd, this); } }; @@ -217,8 +217,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_monthname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_monthname>(thd, this); } }; @@ -240,8 +240,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dayofyear>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dayofyear>(thd, this); } }; @@ -263,8 +263,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_hour>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_hour>(thd, this); } }; @@ -286,8 +286,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_minute>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_minute>(thd, this); } }; @@ -309,8 +309,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_quarter>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_quarter>(thd, this); } }; @@ -332,8 +332,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_second>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_second>(thd, this); } }; @@ -365,8 +365,8 @@ public: { return arg_count == 2; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_week>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_week>(thd, this); } }; class Item_func_yearweek :public Item_long_func @@ -393,8 +393,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_yearweek>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_yearweek>(thd, this); } }; @@ -418,8 +418,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_year>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_year>(thd, this); } }; @@ -454,8 +454,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_weekday>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_weekday>(thd, this); } }; class Item_func_dayname :public Item_func_weekday @@ -533,8 +533,8 @@ public: } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_unix_timestamp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_unix_timestamp>(thd, this); } }; @@ -556,8 +556,8 @@ public: } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_time_to_sec>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_time_to_sec>(thd, this); } }; @@ -684,8 +684,8 @@ public: Item_func_curtime_local(THD *thd, uint dec): Item_func_curtime(thd, dec) {} const char *func_name() const { return "curtime"; } virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curtime_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curtime_local>(thd, this); } }; @@ -695,8 +695,8 @@ public: Item_func_curtime_utc(THD *thd, uint dec): Item_func_curtime(thd, dec) {} const char *func_name() const { return "utc_time"; } virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curtime_utc>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curtime_utc>(thd, this); } }; @@ -723,8 +723,8 @@ public: Item_func_curdate_local(THD *thd): Item_func_curdate(thd) {} const char *func_name() const { return "curdate"; } void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curdate_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curdate_local>(thd, this); } }; @@ -734,8 +734,8 @@ public: Item_func_curdate_utc(THD *thd): Item_func_curdate(thd) {} const char *func_name() const { return "utc_date"; } void store_now_in_TIME(THD* thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curdate_utc>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curdate_utc>(thd, this); } }; @@ -772,8 +772,8 @@ public: int save_in_field(Field *field, bool no_conversions); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); virtual enum Functype functype() const { return NOW_FUNC; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_now_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_now_local>(thd, this); } }; @@ -789,8 +789,8 @@ public: return mark_unsupported_function(func_name(), "()", arg, VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_now_utc>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_now_utc>(thd, this); } }; @@ -813,8 +813,8 @@ public: VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC); } virtual enum Functype functype() const { return SYSDATE_FUNC; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sysdate_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sysdate_local>(thd, this); } }; @@ -832,8 +832,8 @@ public: { return has_date_args() || has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_from_days>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_from_days>(thd, this); } }; @@ -860,8 +860,8 @@ public: return false; return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_date_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_date_format>(thd, this); } }; class Item_func_time_format: public Item_func_date_format @@ -871,8 +871,8 @@ public: Item_func_date_format(thd, a, b) { is_time_format= true; } const char *func_name() const { return "time_format"; } bool check_vcol_func_processor(void *arg) { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_time_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_time_format>(thd, this); } }; @@ -886,8 +886,8 @@ class Item_func_from_unixtime :public Item_datetimefunc const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_from_unixtime>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_from_unixtime>(thd, this); } }; @@ -931,8 +931,8 @@ class Item_func_convert_tz :public Item_datetimefunc } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); void cleanup(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_convert_tz>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_convert_tz>(thd, this); } }; @@ -949,8 +949,8 @@ public: maybe_null= true; } const char *func_name() const { return "sec_to_time"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sec_to_time>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sec_to_time>(thd, this); } }; @@ -970,8 +970,8 @@ public: void print(String *str, enum_query_type query_type); enum precedence precedence() const { return ADDINTERVAL_PRECEDENCE; } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_date_add_interval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_date_add_interval>(thd, this); } }; @@ -1078,8 +1078,8 @@ class Item_extract :public Item_int_func } return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_extract>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_extract>(thd, this); } }; @@ -1118,8 +1118,8 @@ public: } void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_char_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_char_typecast>(thd, this); } }; @@ -1143,8 +1143,8 @@ public: { args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_date_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_date_typecast>(thd, this); } }; @@ -1161,8 +1161,8 @@ public: { args[0]->type_handler()->Item_time_typecast_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_time_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_time_typecast>(thd, this); } }; @@ -1179,8 +1179,8 @@ public: { args[0]->type_handler()->Item_datetime_typecast_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_datetime_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_datetime_typecast>(thd, this); } }; @@ -1193,8 +1193,8 @@ public: Item_datefunc(thd, a, b) {} const char *func_name() const { return "makedate"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_makedate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_makedate>(thd, this); } }; @@ -1211,8 +1211,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); void print(String *str, enum_query_type query_type); const char *func_name() const { return "add_time"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_add_time>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_add_time>(thd, this); } }; class Item_func_timediff :public Item_timefunc @@ -1229,8 +1229,8 @@ public: maybe_null= true; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_timediff>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_timediff>(thd, this); } }; class Item_func_maketime :public Item_timefunc @@ -1251,8 +1251,8 @@ public: } const char *func_name() const { return "maketime"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_maketime>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_maketime>(thd, this); } }; @@ -1274,8 +1274,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_microsecond>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_microsecond>(thd, this); } }; @@ -1295,8 +1295,8 @@ public: maybe_null=1; } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_timestamp_diff>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_timestamp_diff>(thd, this); } }; @@ -1321,8 +1321,8 @@ public: fix_length_and_charset(17, default_charset()); } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_format>(thd, this); } }; @@ -1341,8 +1341,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); const char *func_name() const { return "str_to_date"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_str_to_date>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_str_to_date>(thd, this); } }; @@ -1354,8 +1354,8 @@ public: Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "last_day"; } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_last_day>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_last_day>(thd, this); } }; #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc index f993276dab3..cd0fb5058a6 100644 --- a/sql/item_windowfunc.cc +++ b/sql/item_windowfunc.cc @@ -1,6 +1,21 @@ +/* + Copyright (c) 2016,2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mariadb.h" #include "item_windowfunc.h" -#include "my_dbug.h" -#include "my_global.h" #include "sql_select.h" // test if group changed @@ -93,6 +108,7 @@ Item_window_func::fix_fields(THD *thd, Item **ref) my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func()->func_name()); return true; } + /* TODO: why the last parameter is 'ref' in this call? What if window_func decides to substitute itself for something else and does *ref=.... ? @@ -153,10 +169,25 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, window_func()->setup_caches(thd); } +bool Item_window_func::check_result_type_of_order_item() +{ + if (only_single_element_order_list()) + { + Item_result rtype= window_spec->order_list->first->item[0]->cmp_type(); + // TODO (varun) : support date type in percentile_cont function + if (rtype != REAL_RESULT && rtype != INT_RESULT && + rtype != DECIMAL_RESULT && rtype != TIME_RESULT) + { + my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name()); + return TRUE; + } + setting_handler_for_percentile_functions(rtype); + } + return FALSE; +} /* This must be called before attempting to compute the window function values. - @detail If we attempt to do it in fix_fields(), partition_fields will refer to the original window function arguments. @@ -179,6 +210,71 @@ void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec) clear(); } +void Item_sum_percentile_disc::setup_window_func(THD *thd, Window_spec *window_spec) +{ + order_item= window_spec->order_list->first->item[0]; + if (!(value= order_item->get_cache(thd))) + return; + value->setup(thd, order_item); + value->store(order_item); +} + +void Item_sum_percentile_cont::setup_window_func(THD *thd, Window_spec *window_spec) +{ + order_item= window_spec->order_list->first->item[0]; + /* TODO(varun): need to discuss and finalise what type should we + return for percentile cont functions + */ + if (!(ceil_value= order_item->get_cache(thd))) + return; + ceil_value->setup(thd, order_item); + ceil_value->store(order_item); + + if (!(floor_value= order_item->get_cache(thd))) + return; + floor_value->setup(thd, order_item); + floor_value->store(order_item); +} +bool Item_sum_percentile_cont::fix_fields(THD *thd, Item **ref) +{ + bool res; + res= Item_sum_num::fix_fields(thd, ref); + if (res) + return res; + + switch(args[0]->cmp_type()) + { + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + break; + default: + my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name()); + return TRUE; + } + return res; +} +bool Item_sum_percentile_disc::fix_fields(THD *thd, Item **ref) +{ + bool res; + res= Item_sum_num::fix_fields(thd, ref); + if (res) + return res; + + switch(args[0]->cmp_type()) + { + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + break; + default: + my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name()); + return TRUE; + } + return res; + +} + bool Item_sum_dense_rank::add() { if (peer_tracker->check_if_next_group() || first_add) diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h index 9fe95ed6cee..0a0a02c86b1 100644 --- a/sql/item_windowfunc.h +++ b/sql/item_windowfunc.h @@ -1,7 +1,22 @@ +/* + Copyright (c) 2016,2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + #ifndef ITEM_WINDOWFUNC_INCLUDED #define ITEM_WINDOWFUNC_INCLUDED -#include "my_global.h" #include "item.h" class Window_spec; @@ -9,6 +24,7 @@ class Window_spec; int test_if_group_changed(List<Cached_item> &list); + /* A wrapper around test_if_group_changed */ class Group_bound_tracker { @@ -16,10 +32,10 @@ public: Group_bound_tracker(THD *thd, SQL_I_List<ORDER> *list) { - for (ORDER *curr = list->first; curr; curr=curr->next) + for (ORDER *curr = list->first; curr; curr=curr->next) { - Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE); - group_fields.push_back(tmp); + Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE); + group_fields.push_back(tmp); } } @@ -130,8 +146,8 @@ public: return "row_number"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_row_number>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_row_number>(thd, this); } }; @@ -205,8 +221,8 @@ public: } Item_sum_int::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_rank>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_rank>(thd, this); } }; @@ -275,8 +291,8 @@ class Item_sum_dense_rank: public Item_sum_int } Item_sum_int::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_dense_rank>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_dense_rank>(thd, this); } }; class Item_sum_hybrid_simple : public Item_sum, @@ -338,8 +354,8 @@ class Item_sum_first_value : public Item_sum_hybrid_simple return "first_value"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_first_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_first_value>(thd, this); } }; /* @@ -364,8 +380,8 @@ class Item_sum_last_value : public Item_sum_hybrid_simple return "last_value"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_last_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_last_value>(thd, this); } }; class Item_sum_nth_value : public Item_sum_hybrid_simple @@ -384,8 +400,8 @@ class Item_sum_nth_value : public Item_sum_hybrid_simple return "nth_value"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_nth_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_nth_value>(thd, this); } }; class Item_sum_lead : public Item_sum_hybrid_simple @@ -404,8 +420,8 @@ class Item_sum_lead : public Item_sum_hybrid_simple return "lead"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_lead>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_lead>(thd, this); } }; class Item_sum_lag : public Item_sum_hybrid_simple @@ -424,8 +440,8 @@ class Item_sum_lag : public Item_sum_hybrid_simple return "lag"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_lag>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_lag>(thd, this); } }; /* @@ -516,8 +532,8 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count } void setup_window_func(THD *thd, Window_spec *window_spec); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_percent_rank>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_percent_rank>(thd, this); } private: longlong cur_rank; // Current rank of the current row. @@ -557,6 +573,9 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count Item_sum_cume_dist(THD *thd) : Item_sum_window_with_row_count(thd), current_row_count_(0) {} + Item_sum_cume_dist(THD *thd, Item *arg) : Item_sum_window_with_row_count(thd,arg), + current_row_count_(0) {} + double val_real() { if (get_row_count() == 0) @@ -600,8 +619,13 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count // requires. } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_cume_dist>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_cume_dist>(thd, this); } + + ulonglong get_row_number() + { + return current_row_count_ ; + } private: ulonglong current_row_count_; @@ -670,14 +694,285 @@ class Item_sum_ntile : public Item_sum_window_with_row_count const Type_handler *type_handler() const { return &type_handler_longlong; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_ntile>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_ntile>(thd, this); } private: longlong get_num_quantiles() { return args[0]->val_int(); } ulong current_row_count_; }; +class Item_sum_percentile_disc : public Item_sum_cume_dist, + public Type_handler_hybrid_field_type +{ +public: + Item_sum_percentile_disc(THD *thd, Item* arg) : Item_sum_cume_dist(thd, arg), + Type_handler_hybrid_field_type(&type_handler_longlong), + value(NULL), val_calculated(FALSE), first_call(TRUE), + prev_value(0), order_item(NULL){} + + double val_real() + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_real(); + } + + longlong val_int() + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_int(); + } + + my_decimal* val_decimal(my_decimal* dec) + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_decimal(dec); + } + + String* val_str(String *str) + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_str(str); + } + + bool add() + { + Item *arg= get_arg(0); + if (arg->is_null()) + return false; + + if (first_call) + { + prev_value= arg->val_real(); + if (prev_value > 1 || prev_value < 0) + { + my_error(ER_ARGUMENT_OUT_OF_RANGE, MYF(0), func_name()); + return true; + } + first_call= false; + } + + double arg_val= arg->val_real(); + + if (prev_value != arg_val) + { + my_error(ER_ARGUMENT_NOT_CONSTANT, MYF(0), func_name()); + return true; + } + + if (val_calculated) + return false; + + value->store(order_item); + value->cache_value(); + if (value->null_value) + return false; + + Item_sum_cume_dist::add(); + double val= Item_sum_cume_dist::val_real(); + + if (val >= prev_value && !val_calculated) + val_calculated= true; + return false; + } + + enum Sumfunctype sum_func() const + { + return PERCENTILE_DISC_FUNC; + } + + void clear() + { + val_calculated= false; + first_call= true; + value->clear(); + Item_sum_cume_dist::clear(); + } + + const char*func_name() const + { + return "percentile_disc"; + } + + void update_field() {} + void set_type_handler(Window_spec *window_spec); + const Type_handler *type_handler() const + {return Type_handler_hybrid_field_type::type_handler();} + + void fix_length_and_dec() + { + decimals = 10; // TODO-cvicentiu find out how many decimals the standard + // requires. + } + + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_percentile_disc>(thd, this); } + void setup_window_func(THD *thd, Window_spec *window_spec); + void setup_hybrid(THD *thd, Item *item); + bool fix_fields(THD *thd, Item **ref); + +private: + Item_cache *value; + bool val_calculated; + bool first_call; + double prev_value; + Item *order_item; +}; + +class Item_sum_percentile_cont : public Item_sum_cume_dist, + public Type_handler_hybrid_field_type +{ +public: + Item_sum_percentile_cont(THD *thd, Item* arg) : Item_sum_cume_dist(thd, arg), + Type_handler_hybrid_field_type(&type_handler_double), + floor_value(NULL), ceil_value(NULL), first_call(TRUE),prev_value(0), + ceil_val_calculated(FALSE), floor_val_calculated(FALSE), order_item(NULL){} + + double val_real() + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + double val= 1 + prev_value * (get_row_count()-1); + + /* + Applying the formula to get the value + If (CRN = FRN = RN) then the result is (value of expression from row at RN) + Otherwise the result is + (CRN - RN) * (value of expression for row at FRN) + + (RN - FRN) * (value of expression for row at CRN) + */ + + if(ceil(val) == floor(val)) + return floor_value->val_real(); + + double ret_val= ((val - floor(val)) * ceil_value->val_real()) + + ((ceil(val) - val) * floor_value->val_real()); + + return ret_val; + } + + bool add() + { + Item *arg= get_arg(0); + if (arg->is_null()) + return false; + + if (first_call) + { + first_call= false; + prev_value= arg->val_real(); + if (prev_value > 1 || prev_value < 0) + { + my_error(ER_ARGUMENT_OUT_OF_RANGE, MYF(0), func_name()); + return true; + } + } + + double arg_val= arg->val_real(); + if (prev_value != arg_val) + { + my_error(ER_ARGUMENT_NOT_CONSTANT, MYF(0), func_name()); + return true; + } + + if (!floor_val_calculated) + { + floor_value->store(order_item); + floor_value->cache_value(); + if (floor_value->null_value) + return false; + } + if (floor_val_calculated && !ceil_val_calculated) + { + ceil_value->store(order_item); + ceil_value->cache_value(); + if (ceil_value->null_value) + return false; + } + + Item_sum_cume_dist::add(); + double val= 1 + prev_value * (get_row_count()-1); + + if (!floor_val_calculated && get_row_number() == floor(val)) + floor_val_calculated= true; + + if (!ceil_val_calculated && get_row_number() == ceil(val)) + ceil_val_calculated= true; + return false; + } + + enum Sumfunctype sum_func() const + { + return PERCENTILE_CONT_FUNC; + } + + void clear() + { + first_call= true; + floor_value->clear(); + ceil_value->clear(); + floor_val_calculated= false; + ceil_val_calculated= false; + Item_sum_cume_dist::clear(); + } + + const char*func_name() const + { + return "percentile_cont"; + } + void update_field() {} + void set_type_handler(Window_spec *window_spec); + const Type_handler *type_handler() const + {return Type_handler_hybrid_field_type::type_handler();} + + void fix_length_and_dec() + { + decimals = 10; // TODO-cvicentiu find out how many decimals the standard + // requires. + } + + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_percentile_cont>(thd, this); } + void setup_window_func(THD *thd, Window_spec *window_spec); + void setup_hybrid(THD *thd, Item *item); + bool fix_fields(THD *thd, Item **ref); + +private: + Item_cache *floor_value; + Item_cache *ceil_value; + bool first_call; + double prev_value; + bool ceil_val_calculated; + bool floor_val_calculated; + Item *order_item; +}; + + + class Item_window_func : public Item_func_or_sum { @@ -732,6 +1027,8 @@ public: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: case Item_sum::NTILE_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return true; default: return false; @@ -758,6 +1055,8 @@ public: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: case Item_sum::NTILE_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return true; default: return false; @@ -781,12 +1080,40 @@ public: case Item_sum::DENSE_RANK_FUNC: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return true; default: return false; } } + bool only_single_element_order_list() const + { + switch (window_func()->sum_func()){ + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: + return true; + default: + return false; + } + } + + void setting_handler_for_percentile_functions(Item_result rtype) const + { + switch (window_func()->sum_func()){ + case Item_sum::PERCENTILE_DISC_FUNC: + ((Item_sum_percentile_disc* ) window_func())->set_handler_by_cmp_type(rtype); + break; + default: + return; + } + } + + bool check_result_type_of_order_item(); + + + /* Computation functions. TODO: consoder merging these with class Group_bound_tracker. @@ -954,7 +1281,7 @@ public: void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } }; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index a69d48e8328..83a25b7865c 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -247,8 +247,8 @@ public: Item_nodeset_func(thd, pxml) {} const char *func_name() const { return "xpath_rootelement"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_rootelement>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_rootelement>(thd, this); } }; @@ -260,8 +260,8 @@ public: Item_nodeset_func(thd, a, b, pxml) {} const char *func_name() const { return "xpath_union"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_union>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_union>(thd, this); } }; @@ -294,8 +294,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_selfbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_selfbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); } }; @@ -308,8 +308,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_childbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_childbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_childbyname>(thd, this); } }; @@ -324,8 +324,8 @@ public: need_self(need_self_arg) {} const char *func_name() const { return "xpath_descendantbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); } }; @@ -340,8 +340,8 @@ public: need_self(need_self_arg) {} const char *func_name() const { return "xpath_ancestorbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); } }; @@ -354,8 +354,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_parentbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_parentbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); } }; @@ -368,8 +368,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_attributebyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_attributebyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); } }; @@ -385,8 +385,8 @@ public: Item_nodeset_func(thd, a, b, pxml) {} const char *func_name() const { return "xpath_predicate"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_predicate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_predicate>(thd, this); } }; @@ -398,8 +398,8 @@ public: Item_nodeset_func(thd, a, b, pxml) { } const char *func_name() const { return "xpath_elementbyindex"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); } }; @@ -426,8 +426,8 @@ public: } return args[0]->val_real() ? 1 : 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_xpath_cast_bool>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_xpath_cast_bool>(thd, this); } }; @@ -440,8 +440,8 @@ public: Item_xpath_cast_number(THD *thd, Item *a): Item_real_func(thd, a) {} const char *func_name() const { return "xpath_cast_number"; } virtual double val_real() { return args[0]->val_real(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_xpath_cast_number>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_xpath_cast_number>(thd, this); } }; @@ -457,8 +457,8 @@ public: String *val_nodeset(String *res) { return string_cache; } void fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_context_cache>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_context_cache>(thd, this); } }; @@ -478,8 +478,8 @@ public: return ((MY_XPATH_FLT*)flt->ptr())->pos + 1; return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xpath_position>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xpath_position>(thd, this); } }; @@ -501,8 +501,8 @@ public: return predicate_supplied_context_size; return res->length() / sizeof(MY_XPATH_FLT); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xpath_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xpath_count>(thd, this); } }; @@ -546,8 +546,8 @@ public: } return sum; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xpath_sum>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xpath_sum>(thd, this); } }; @@ -624,8 +624,8 @@ public: } return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_to_const_comparator>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_to_const_comparator>(thd, this); } }; @@ -2638,7 +2638,7 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) { Item_splocal *splocal= new (thd->mem_root) Item_splocal(thd, &name, spv->offset, spv->type_handler(), 0); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (splocal) splocal->m_sp= lex->sphead; #endif diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 3c071b897e2..425b2b8ec1c 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -96,8 +96,8 @@ public: Item_xml_str_func(thd, a, b) {} const char *func_name() const { return "extractvalue"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xml_extractvalue>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xml_extractvalue>(thd, this); } }; @@ -112,8 +112,8 @@ public: Item_xml_str_func(thd, a, b, c) {} const char *func_name() const { return "updatexml"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xml_update>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xml_update>(thd, this); } }; #endif /* ITEM_XMLFUNC_INCLUDED */ diff --git a/sql/key.cc b/sql/key.cc index db44e4780c4..3ee083e560f 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -16,7 +16,7 @@ /* Functions to handle keys and fields in forms */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "key.h" // key_rec_cmp #include "field.h" // Field diff --git a/sql/key.h b/sql/key.h index 7b83d74c901..523d0b7f10b 100644 --- a/sql/key.h +++ b/sql/key.h @@ -16,8 +16,6 @@ #ifndef KEY_INCLUDED #define KEY_INCLUDED -#include "my_global.h" /* uchar */ - class Field; class String; struct TABLE; diff --git a/sql/keycaches.cc b/sql/keycaches.cc index 336f2611a7e..6de775d3f2d 100644 --- a/sql/keycaches.cc +++ b/sql/keycaches.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "keycaches.h" /**************************************************************************** diff --git a/sql/lex.h b/sql/lex.h index ec2ca7de564..da985ad26a6 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -273,6 +273,7 @@ static SYMBOL symbols[] = { { "HAVING", SYM(HAVING)}, { "HELP", SYM(HELP_SYM)}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY)}, + { "INVISIBLE", SYM(INVISIBLE_SYM)}, { "HOST", SYM(HOST_SYM)}, { "HOSTS", SYM(HOSTS_SYM)}, { "HOUR", SYM(HOUR_SYM)}, @@ -424,6 +425,7 @@ static SYMBOL symbols[] = { { "NOCACHE", SYM(NOCACHE_SYM)}, { "NOCYCLE", SYM(NOCYCLE_SYM)}, { "NO_WAIT", SYM(NO_WAIT_SYM)}, + { "NOWAIT", SYM(NOWAIT_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, @@ -701,6 +703,7 @@ static SYMBOL symbols[] = { { "WHILE", SYM(WHILE_SYM)}, { "WINDOW", SYM(WINDOW_SYM)}, { "WITH", SYM(WITH)}, + { "WITHIN", SYM(WITHIN)}, { "WORK", SYM(WORK_SYM)}, { "WRAPPER", SYM(WRAPPER_SYM)}, { "WRITE", SYM(WRITE_SYM)}, @@ -736,6 +739,7 @@ static SYMBOL sql_functions[] = { { "LAG", SYM(LAG_SYM)}, { "LEAD", SYM(LEAD_SYM)}, { "MAX", SYM(MAX_SYM)}, + { "MEDIAN", SYM(MEDIAN_SYM)}, { "MID", SYM(SUBSTRING)}, /* unireg function */ { "MIN", SYM(MIN_SYM)}, { "NOW", SYM(NOW_SYM)}, @@ -743,6 +747,8 @@ static SYMBOL sql_functions[] = { { "NTILE", SYM(NTILE_SYM)}, { "POSITION", SYM(POSITION_SYM)}, { "PERCENT_RANK", SYM(PERCENT_RANK_SYM)}, + { "PERCENTILE_CONT", SYM(PERCENTILE_CONT_SYM)}, + { "PERCENTILE_DISC", SYM(PERCENTILE_DISC_SYM)}, { "RANK", SYM(RANK_SYM)}, { "ROW_NUMBER", SYM(ROW_NUMBER_SYM)}, { "SESSION_USER", SYM(USER_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index c6dd3724eba..11cadb528d2 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -70,7 +70,7 @@ in case external_lock() fails. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "debug_sync.h" #include "lock.h" @@ -294,7 +294,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) if (lock_tables_check(thd, tables, count, flags)) DBUG_RETURN(NULL); - if (!(thd->variables.option_bits & OPTION_TABLE_LOCK)) + if (!(thd->variables.option_bits & OPTION_TABLE_LOCK) && + !(flags & MYSQL_LOCK_USE_MALLOC)) gld_flags|= GET_LOCK_ON_THD; if (! (sql_lock= get_lock_data(thd, tables, count, gld_flags))) @@ -415,7 +416,8 @@ static int lock_external(THD *thd, TABLE **tables, uint count) void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock) { mysql_unlock_tables(thd, sql_lock, - thd->variables.option_bits & OPTION_TABLE_LOCK); + (thd->variables.option_bits & OPTION_TABLE_LOCK) || + !(sql_lock->flags & GET_LOCK_ON_THD)); } @@ -433,7 +435,10 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock) if (sql_lock->lock_count) thr_multi_unlock(sql_lock->locks, sql_lock->lock_count, 0); if (free_lock) + { + DBUG_ASSERT(!(sql_lock->flags & GET_LOCK_ON_THD)); my_free(sql_lock); + } if (!errors) thd->clear_error(); THD_STAGE_INFO(thd, org_stage); @@ -668,6 +673,7 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) sql_lock->table_count=a->table_count+b->table_count; sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1); sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2); + sql_lock->flags= 0; memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks)); memcpy(sql_lock->locks+a->lock_count,b->locks, b->lock_count*sizeof(*b->locks)); @@ -782,6 +788,7 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1); to= table_buf= sql_lock->table= (TABLE**) (locks + lock_count * 2); sql_lock->table_count= table_count; + sql_lock->flags= flags; for (i=0 ; i < count ; i++) { @@ -915,7 +922,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type, MDL_request schema_request; MDL_request mdl_request; - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); if (thd->locked_tables_mode) { diff --git a/sql/log.cc b/sql/log.cc index e632fe629f5..6923a6241cd 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -25,7 +25,7 @@ Abort logging when we get an error in reading or writing log files */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "log.h" #include "sql_base.h" // open_log_table @@ -39,11 +39,9 @@ #include "rpl_filter.h" #include "rpl_rli.h" #include "sql_audit.h" -#include "log_slow.h" #include "mysqld.h" #include <my_dir.h> -#include <stdarg.h> #include <m_ctype.h> // For test_if_number #ifdef _WIN32 @@ -51,11 +49,13 @@ #endif #include "sql_plugin.h" -#include "rpl_handler.h" #include "debug_sync.h" #include "sql_show.h" #include "my_pthread.h" +#include "semisync_master.h" #include "wsrep_mysqld.h" +#include "sp_rcontext.h" +#include "sp_head.h" /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 @@ -2391,7 +2391,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg) *errmsg = "Could not open log file"; goto err; } - if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0, + if (init_io_cache(log, file, (size_t)binlog_file_cache_size, READ_CACHE, 0, 0, MYF(MY_WME|MY_DONT_CHECK_FILESIZE))) { sql_print_error("Failed to create a cache on log (file '%s')", @@ -2686,7 +2686,7 @@ bool MYSQL_LOG::open( mysqld_port, mysqld_unix_port #endif ); - end= strnmov(buff + len, "Time Id Command Argument\n", + end= strnmov(buff + len, "Time\t\t Id Command\tArgument\n", sizeof(buff) - len); if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) || flush_io_cache(&log_file)) @@ -2898,27 +2898,27 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;); /* Note that my_b_write() assumes it knows the length for this */ - if (event_time != last_time) - { - last_time= event_time; + if (event_time != last_time) + { + last_time= event_time; - localtime_r(&event_time, &start); + localtime_r(&event_time, &start); - time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE, - "%02d%02d%02d %2d:%02d:%02d\t", - start.tm_year % 100, start.tm_mon + 1, - start.tm_mday, start.tm_hour, - start.tm_min, start.tm_sec); + time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE, + "%02d%02d%02d %2d:%02d:%02d\t", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); - if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len)) - goto err; - } - else - if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0) - goto err; + if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len)) + goto err; + } + else + if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0) + goto err; - /* command_type, thread_id */ - size_t length= my_snprintf(buff, 32, "%5llu ", thread_id_arg); + /* command_type, thread_id */ + size_t length= my_snprintf(buff, 32, "%6llu ", thread_id_arg); if (my_b_write(&log_file, (uchar*) buff, length)) goto err; @@ -2987,6 +2987,7 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, const char *sql_text, uint sql_text_len) { bool error= 0; + char llbuff[22]; DBUG_ENTER("MYSQL_QUERY_LOG::write"); mysql_mutex_lock(&LOCK_log); @@ -3025,22 +3026,38 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0); sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0); if (my_b_printf(&log_file, - "# Thread_id: %lu Schema: %s QC_hit: %s\n" \ - "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n" \ - "# Rows_affected: %lu\n", + "# Thread_id: %lu Schema: %s QC_hit: %s\n" + "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n" + "# Rows_affected: %lu Bytes_sent: %lu\n", (ulong) thd->thread_id, (thd->db ? thd->db : ""), ((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"), query_time_buff, lock_time_buff, (ulong) thd->get_sent_row_count(), (ulong) thd->get_examined_row_count(), - thd->get_stmt_da()->is_ok() ? - (ulong) thd->get_stmt_da()->affected_rows() : - 0)) + (ulong) thd->get_affected_rows(), + (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old))) + goto err; + + if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) + && thd->tmp_tables_used && + my_b_printf(&log_file, + "# Tmp_tables: %lu Tmp_disk_tables: %lu " + "Tmp_table_sizes: %s\n", + (ulong) thd->tmp_tables_used, + (ulong) thd->tmp_tables_disk_used, + llstr(thd->tmp_tables_size, llbuff))) goto err; - if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) && + + if (thd->spcont && + my_b_printf(&log_file, "# Stored_routine: %s\n", + ErrConvDQName(thd->spcont->m_sp).ptr())) + goto err; + + if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) && (thd->query_plan_flags & (QPLAN_FULL_SCAN | QPLAN_FULL_JOIN | QPLAN_TMP_TABLE | - QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK)) && + QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK | + QPLAN_FILESORT_PRIORITY_QUEUE)) && my_b_printf(&log_file, "# Full_scan: %s Full_join: %s " "Tmp_table: %s Tmp_table_on_disk: %s\n" @@ -3048,8 +3065,8 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, "Priority_queue: %s\n", ((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"), ((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"), - ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"), - ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"), + (thd->tmp_tables_used ? "Yes" : "No"), + (thd->tmp_tables_disk_used ? "Yes" : "No"), ((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"), ((thd->query_plan_flags & QPLAN_FILESORT_DISK) ? "Yes" : "No"), @@ -3193,7 +3210,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period) group_commit_trigger_lock_wait(0), sync_period_ptr(sync_period), sync_counter(0), state_file_deleted(false), binlog_state_recover_done(false), - is_relay_log(0), signal_cnt(0), + is_relay_log(0), relay_signal_cnt(0), checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF), relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF), description_event_for_exec(0), description_event_for_queue(0), @@ -3263,7 +3280,8 @@ void MYSQL_BIN_LOG::cleanup() mysql_mutex_destroy(&LOCK_xid_list); mysql_mutex_destroy(&LOCK_binlog_background_thread); mysql_mutex_destroy(&LOCK_binlog_end_pos); - mysql_cond_destroy(&update_cond); + mysql_cond_destroy(&COND_relay_log_updated); + mysql_cond_destroy(&COND_bin_log_updated); mysql_cond_destroy(&COND_queue_busy); mysql_cond_destroy(&COND_xid_list); mysql_cond_destroy(&COND_binlog_background_thread); @@ -3298,7 +3316,8 @@ void MYSQL_BIN_LOG::init_pthread_objects() mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION); mysql_mutex_init(key_BINLOG_LOCK_xid_list, &LOCK_xid_list, MY_MUTEX_INIT_FAST); - mysql_cond_init(m_key_update_cond, &update_cond, 0); + mysql_cond_init(m_key_relay_log_update, &COND_relay_log_updated, 0); + mysql_cond_init(m_key_bin_log_update, &COND_bin_log_updated, 0); mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0); mysql_cond_init(key_BINLOG_COND_xid_list, &COND_xid_list, 0); @@ -3785,6 +3804,11 @@ bool MYSQL_BIN_LOG::open(const char *log_name, close_purge_index_file(); #endif + /* Notify the io thread that binlog is rotated to a new file */ + if (is_relay_log) + signal_relay_log_update(); + else + update_binlog_end_pos(); DBUG_RETURN(0); err: @@ -4154,14 +4178,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log, mysql_mutex_unlock(&LOCK_xid_list); } - /* - The following mutex is needed to ensure that no threads call - 'delete thd' as we would then risk missing a 'rollback' from this - thread. If the transaction involved MyISAM tables, it should go - into binlog even on rollback. - */ - mysql_mutex_lock(&LOCK_thread_count); - /* Save variables so that we can reopen the log */ save_name=name; name=0; // Protect against free @@ -4268,7 +4284,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log, err: if (error == 1) name= const_cast<char*>(save_name); - mysql_mutex_unlock(&LOCK_thread_count); if (!is_relay_log) { @@ -5152,7 +5167,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) new file name in the current binary log file. */ if ((error= generate_new_name(new_name, name, 0))) + { +#ifdef ENABLE_AND_FIX_HANG + close_on_error= TRUE; +#endif goto end; + } new_name_ptr=new_name; if (log_type == LOG_BIN) @@ -5183,13 +5203,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) } bytes_written += r.data_written; } - /* - Update needs to be signalled even if there is no rotate event - log rotation should give the waiting thread a signal to - discover EOF and move on to the next log. - */ - signal_update(); } + + /* + Update needs to be signalled even if there is no rotate event + log rotation should give the waiting thread a signal to + discover EOF and move on to the next log. + */ + if ((error= flush_io_cache(&log_file))) + { + close_on_error= TRUE; + goto end; + } + update_binlog_end_pos(); + old_name=name; name=0; // Don't free name close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX; @@ -5320,7 +5347,7 @@ bool MYSQL_BIN_LOG::append_no_lock(Log_event* ev) if (my_b_append_tell(&log_file) > max_size) error= new_file_without_locking(); err: - signal_update(); // Safe as we don't call close + update_binlog_end_pos(); DBUG_RETURN(error); } @@ -5381,7 +5408,7 @@ bool MYSQL_BIN_LOG::write_event_buffer(uchar* buf, uint len) err: my_safe_afree(ebuf, len); if (!error) - signal_update(); + update_binlog_end_pos(); DBUG_RETURN(error); } @@ -5486,13 +5513,14 @@ stmt_has_updated_trans_table(const THD *thd) */ bool use_trans_cache(const THD* thd, bool is_transactional) { + if (is_transactional) + return 1; binlog_cache_mngr *const cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton); - return - ((thd->is_current_stmt_binlog_format_row() || - thd->variables.binlog_direct_non_trans_update) ? is_transactional : - (is_transactional || !cache_mngr->trx_cache.empty())); + return ((thd->is_current_stmt_binlog_format_row() || + thd->variables.binlog_direct_non_trans_update) ? 0 : + !cache_mngr->trx_cache.empty()); } /** @@ -5952,6 +5980,8 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, local_server_id= thd->variables.server_id; seq_no= thd->variables.gtid_seq_no; + DBUG_ASSERT(local_server_id != 0); + if (thd->variables.option_bits & OPTION_GTID_BEGIN) { DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. " @@ -6413,6 +6443,7 @@ err: { my_off_t offset= my_b_tell(file); bool check_purge= false; + DBUG_ASSERT(!is_relay_log); if (!error) { @@ -6427,26 +6458,23 @@ err: mysql_mutex_assert_owner(&LOCK_log); mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true; - bool last= true; - if ((error= RUN_HOOK(binlog_storage, after_flush, - (thd, log_file_name, file->pos_in_file, - synced, first, last)))) +#ifdef HAVE_REPLICATION + if (repl_semisync_master.report_binlog_update(thd, log_file_name, + file->pos_in_file)) { sql_print_error("Failed to run 'after_flush' hooks"); error= 1; } else +#endif { - /* update binlog_end_pos so it can be read by dump thread - * - * note: must be _after_ the RUN_HOOK(after_flush) or else - * semi-sync-plugin might not have put the transaction into - * it's list before dump-thread tries to send it - */ + /* + update binlog_end_pos so it can be read by dump thread + note: must be _after_ the RUN_HOOK(after_flush) or else + semi-sync might not have put the transaction into + it's list before dump-thread tries to send it + */ update_binlog_end_pos(offset); - - signal_update(); if ((error= rotate(false, &check_purge))) check_purge= false; } @@ -6463,15 +6491,14 @@ err: mysql_mutex_assert_not_owner(&LOCK_log); mysql_mutex_assert_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true; - bool last= true; - if (RUN_HOOK(binlog_storage, after_sync, - (thd, log_file_name, file->pos_in_file, - first, last))) +#ifdef HAVE_REPLICATION + if (repl_semisync_master.wait_after_sync(log_file_name, + file->pos_in_file)) { error=1; /* error is already printed inside hook */ } +#endif /* Take mutex to protect against a reader seeing partial writes of 64-bit @@ -6513,31 +6540,29 @@ bool slow_log_print(THD *thd, const char *query, uint query_length, } +/** + Decide if we should log the command to general log + + @retval + FALSE No logging + TRUE Ok to log +*/ + bool LOGGER::log_command(THD *thd, enum enum_server_command command) { -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *sctx= thd->security_ctx; -#endif /* Log command if we have at least one log event handler enabled and want to log this king of commands */ - if (*general_log_handler_list && (what_to_log & (1L << (uint) command))) - { - if ((thd->variables.option_bits & OPTION_LOG_OFF) -#ifndef NO_EMBEDDED_ACCESS_CHECKS - && (sctx->master_access & SUPER_ACL) -#endif - ) - { - /* No logging */ - return FALSE; - } - - return TRUE; - } + if (!(*general_log_handler_list && (what_to_log & (1L << (uint) command)))) + return FALSE; - return FALSE; + /* + If LOG_SLOW_DISABLE_SLAVE is set when slave thread starts, then + OPTION_LOG_OFF is set. + Only the super user can set this bit. + */ + return !(thd->variables.option_bits & OPTION_LOG_OFF); } @@ -6547,7 +6572,7 @@ bool general_log_print(THD *thd, enum enum_server_command command, va_list args; uint error= 0; - /* Print the message to the buffer if we want to log this king of commands */ + /* Print the message to the buffer if we want to log this kind of commands */ if (! logger.log_command(thd, command)) return FALSE; @@ -7174,7 +7199,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd) if (!(error= write_incident_already_locked(thd)) && !(error= flush_and_sync(0))) { - signal_update(); + update_binlog_end_pos(); if ((error= rotate(false, &check_purge))) check_purge= false; } @@ -7215,7 +7240,7 @@ MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name_arg */ if (!write_event(&ev) && !flush_and_sync(0)) { - signal_update(); + update_binlog_end_pos(); } else { @@ -7650,7 +7675,11 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) else if (is_leader) trx_group_commit_leader(entry); else if (!entry->queued_by_other) + { + DEBUG_SYNC(entry->thd, "after_semisync_queue"); + entry->thd->wait_for_wakeup_ready(); + } else { /* @@ -7890,45 +7919,40 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) else { bool any_error= false; - bool all_error= true; mysql_mutex_assert_not_owner(&LOCK_prepare_ordered); mysql_mutex_assert_owner(&LOCK_log); mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true, last; + for (current= queue; current != NULL; current= current->next) { - last= current->next == NULL; +#ifdef HAVE_REPLICATION if (!current->error && - RUN_HOOK(binlog_storage, after_flush, - (current->thd, - current->cache_mngr->last_commit_pos_file, - current->cache_mngr->last_commit_pos_offset, synced, - first, last))) + repl_semisync_master. + report_binlog_update(current->thd, + current->cache_mngr->last_commit_pos_file, + current->cache_mngr-> + last_commit_pos_offset)) { current->error= ER_ERROR_ON_WRITE; current->commit_errno= -1; current->error_cache= NULL; any_error= true; } - else - all_error= false; - first= false; +#endif } - /* update binlog_end_pos so it can be read by dump thread - * - * note: must be _after_ the RUN_HOOK(after_flush) or else - * semi-sync-plugin might not have put the transaction into - * it's list before dump-thread tries to send it - */ + /* + update binlog_end_pos so it can be read by dump thread + Note: must be _after_ the RUN_HOOK(after_flush) or else + semi-sync might not have put the transaction into + it's list before dump-thread tries to send it + */ update_binlog_end_pos(commit_offset); if (any_error) sql_print_error("Failed to run 'after_flush' hooks"); - if (!all_error) - signal_update(); } /* @@ -7986,18 +8010,19 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) mysql_mutex_assert_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true, last; + bool first __attribute__((unused))= true; + bool last __attribute__((unused)); for (current= queue; current != NULL; current= current->next) { last= current->next == NULL; - if (!current->error && - RUN_HOOK(binlog_storage, after_sync, - (current->thd, current->cache_mngr->last_commit_pos_file, - current->cache_mngr->last_commit_pos_offset, - first, last))) - { - /* error is already printed inside hook */ - } +#ifdef HAVE_REPLICATION + if (!current->error) + current->error= + repl_semisync_master.wait_after_sync(current->cache_mngr-> + last_commit_pos_file, + current->cache_mngr-> + last_commit_pos_offset); +#endif first= false; } } @@ -8308,10 +8333,10 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd) DBUG_ENTER("wait_for_update_relay_log"); mysql_mutex_assert_owner(&LOCK_log); - thd->ENTER_COND(&update_cond, &LOCK_log, + thd->ENTER_COND(&COND_relay_log_updated, &LOCK_log, &stage_slave_has_read_all_relay_log, &old_stage); - mysql_cond_wait(&update_cond, &LOCK_log); + mysql_cond_wait(&COND_relay_log_updated, &LOCK_log); thd->EXIT_COND(&old_stage); DBUG_VOID_RETURN; } @@ -8332,23 +8357,6 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd) LOCK_log is released by the caller. */ -int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, - const struct timespec *timeout) -{ - int ret= 0; - DBUG_ENTER("wait_for_update_bin_log"); - - thd_wait_begin(thd, THD_WAIT_BINLOG); - mysql_mutex_assert_owner(&LOCK_log); - if (!timeout) - mysql_cond_wait(&update_cond, &LOCK_log); - else - ret= mysql_cond_timedwait(&update_cond, &LOCK_log, - const_cast<struct timespec *>(timeout)); - thd_wait_end(thd); - DBUG_RETURN(ret); -} - int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd, struct timespec *timeout) { @@ -8358,9 +8366,9 @@ int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd, thd_wait_begin(thd, THD_WAIT_BINLOG); mysql_mutex_assert_owner(get_binlog_end_pos_lock()); if (!timeout) - mysql_cond_wait(&update_cond, get_binlog_end_pos_lock()); + mysql_cond_wait(&COND_bin_log_updated, get_binlog_end_pos_lock()); else - ret= mysql_cond_timedwait(&update_cond, get_binlog_end_pos_lock(), + ret= mysql_cond_timedwait(&COND_bin_log_updated, get_binlog_end_pos_lock(), timeout); thd_wait_end(thd); DBUG_RETURN(ret); @@ -8405,7 +8413,8 @@ void MYSQL_BIN_LOG::close(uint exiting) relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF); write_event(&s); bytes_written+= s.data_written; - signal_update(); + flush_io_cache(&log_file); + update_binlog_end_pos(); /* When we shut down server, write out the binlog state to a separate @@ -8624,14 +8633,6 @@ bool flush_error_log() return result; } -void MYSQL_BIN_LOG::signal_update() -{ - DBUG_ENTER("MYSQL_BIN_LOG::signal_update"); - signal_cnt++; - mysql_cond_broadcast(&update_cond); - DBUG_VOID_RETURN; -} - #ifdef _WIN32 static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, size_t length, size_t buffLen) @@ -8675,7 +8676,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, time_t skr; struct tm tm_tmp; struct tm *start; - THD *thd; + THD *thd= 0; int tag_length= 0; char tag[NAME_LEN]; DBUG_ENTER("print_buffer_to_file"); @@ -8709,7 +8710,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, start->tm_hour, start->tm_min, start->tm_sec, - (unsigned long) pthread_self(), + (unsigned long) (thd ? thd->thread_id : 0), (level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ? "Warning" : "Note"), tag_length, tag, @@ -10029,7 +10030,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, for (;;) { while ((ev= Log_event::read_log_event(first_round ? first_log : &log, - 0, fdle, opt_master_verify_checksum)) + fdle, opt_master_verify_checksum)) && ev->is_valid()) { enum Log_event_type typ= ev->get_type_code(); @@ -10270,7 +10271,7 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery) return 1; } - if ((ev= Log_event::read_log_event(&log, 0, &fdle, + if ((ev= Log_event::read_log_event(&log, &fdle, opt_master_verify_checksum)) && ev->get_type_code() == FORMAT_DESCRIPTION_EVENT) { @@ -10511,7 +10512,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) *out_gtid_list= NULL; - if (!(ev= Log_event::read_log_event(cache, 0, &init_fdle, + if (!(ev= Log_event::read_log_event(cache, &init_fdle, opt_master_verify_checksum)) || ev->get_type_code() != FORMAT_DESCRIPTION_EVENT) { @@ -10527,7 +10528,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) { Log_event_type typ; - ev= Log_event::read_log_event(cache, 0, fdle, opt_master_verify_checksum); + ev= Log_event::read_log_event(cache, fdle, opt_master_verify_checksum); if (!ev) { errormsg= "Could not read GTID list event while looking for GTID " @@ -10557,6 +10558,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) return errormsg; } + struct st_mysql_storage_engine binlog_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; diff --git a/sql/log.h b/sql/log.h index a5548ef4e60..6305dd97355 100644 --- a/sql/log.h +++ b/sql/log.h @@ -349,6 +349,11 @@ public: /* for documentation of mutexes held in various places in code */ }; +/* Tell the io thread if we can delay the master info sync. */ +#define SEMI_SYNC_SLAVE_DELAY_SYNC 1 +/* Tell the io thread if the current event needs a ack. */ +#define SEMI_SYNC_NEED_ACK 2 + class MYSQL_QUERY_LOG: public MYSQL_LOG { public: @@ -425,14 +430,18 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG #ifdef HAVE_PSI_INTERFACE /** The instrumentation key to use for @ LOCK_index. */ PSI_mutex_key m_key_LOCK_index; - /** The instrumentation key to use for @ update_cond. */ - PSI_cond_key m_key_update_cond; + /** The instrumentation key to use for @ COND_relay_log_updated */ + PSI_cond_key m_key_relay_log_update; + /** The instrumentation key to use for @ COND_bin_log_updated */ + PSI_cond_key m_key_bin_log_update; /** The instrumentation key to use for opening the log file. */ PSI_file_key m_key_file_log; /** The instrumentation key to use for opening the log index file. */ PSI_file_key m_key_file_log_index; PSI_file_key m_key_COND_queue_busy; + /** The instrumentation key to use for LOCK_binlog_end_pos. */ + PSI_mutex_key m_key_LOCK_binlog_end_pos; #endif struct group_commit_entry @@ -488,7 +497,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG mysql_mutex_t LOCK_binlog_end_pos; mysql_mutex_t LOCK_xid_list; mysql_cond_t COND_xid_list; - mysql_cond_t update_cond; + mysql_cond_t COND_relay_log_updated, COND_bin_log_updated; ulonglong bytes_written; IO_CACHE index_file; char index_file_name[FN_REFLEN]; @@ -605,7 +614,7 @@ public: /* This is relay log */ bool is_relay_log; - ulong signal_cnt; // update of the counter is checked by heartbeat + ulong relay_signal_cnt; // update of the counter is checked by heartbeat enum enum_binlog_checksum_alg checksum_alg_reset; // to contain a new value when binlog is rotated /* Holds the last seen in Relay-Log FD's checksum alg value. @@ -668,16 +677,20 @@ public: #ifdef HAVE_PSI_INTERFACE void set_psi_keys(PSI_mutex_key key_LOCK_index, - PSI_cond_key key_update_cond, + PSI_cond_key key_relay_log_update, + PSI_cond_key key_bin_log_update, PSI_file_key key_file_log, PSI_file_key key_file_log_index, - PSI_file_key key_COND_queue_busy) + PSI_file_key key_COND_queue_busy, + PSI_mutex_key key_LOCK_binlog_end_pos) { m_key_LOCK_index= key_LOCK_index; - m_key_update_cond= key_update_cond; + m_key_relay_log_update= key_relay_log_update; + m_key_bin_log_update= key_bin_log_update; m_key_file_log= key_file_log; m_key_file_log_index= key_file_log_index; m_key_COND_queue_busy= key_COND_queue_busy; + m_key_LOCK_binlog_end_pos= key_LOCK_binlog_end_pos; } #endif @@ -714,11 +727,56 @@ public: DBUG_VOID_RETURN; } void set_max_size(ulong max_size_arg); - void signal_update(); + + /* Handle signaling that relay has been updated */ + void signal_relay_log_update() + { + mysql_mutex_assert_owner(&LOCK_log); + DBUG_ASSERT(is_relay_log); + DBUG_ENTER("MYSQL_BIN_LOG::signal_relay_log_update"); + relay_signal_cnt++; + mysql_cond_broadcast(&COND_relay_log_updated); + DBUG_VOID_RETURN; + } + void signal_bin_log_update() + { + mysql_mutex_assert_owner(&LOCK_binlog_end_pos); + DBUG_ASSERT(!is_relay_log); + DBUG_ENTER("MYSQL_BIN_LOG::signal_bin_log_update"); + mysql_cond_broadcast(&COND_bin_log_updated); + DBUG_VOID_RETURN; + } + void update_binlog_end_pos() + { + if (is_relay_log) + signal_relay_log_update(); + else + { + lock_binlog_end_pos(); + binlog_end_pos= my_b_safe_tell(&log_file); + signal_bin_log_update(); + unlock_binlog_end_pos(); + } + } + void update_binlog_end_pos(my_off_t pos) + { + mysql_mutex_assert_owner(&LOCK_log); + mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos); + lock_binlog_end_pos(); + /* + Note: it would make more sense to assert(pos > binlog_end_pos) + but there are two places triggered by mtr that has pos == binlog_end_pos + i didn't investigate but accepted as it should do no harm + */ + DBUG_ASSERT(pos >= binlog_end_pos); + binlog_end_pos= pos; + signal_bin_log_update(); + unlock_binlog_end_pos(); + } + void wait_for_sufficient_commits(); void binlog_trigger_immediate_group_commit(); void wait_for_update_relay_log(THD* thd); - int wait_for_update_bin_log(THD* thd, const struct timespec * timeout); void init(ulong max_size); void init_pthread_objects(); void cleanup(); @@ -815,7 +873,7 @@ public: inline char* get_log_fname() { return log_file_name; } inline char* get_name() { return name; } inline mysql_mutex_t* get_log_lock() { return &LOCK_log; } - inline mysql_cond_t* get_log_cond() { return &update_cond; } + inline mysql_cond_t* get_bin_log_cond() { return &COND_bin_log_updated; } inline IO_CACHE* get_log_file() { return &log_file; } inline void lock_index() { mysql_mutex_lock(&LOCK_index);} @@ -839,23 +897,6 @@ public: bool check_strict_gtid_sequence(uint32 domain_id, uint32 server_id, uint64 seq_no); - - void update_binlog_end_pos(my_off_t pos) - { - mysql_mutex_assert_owner(&LOCK_log); - mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos); - lock_binlog_end_pos(); - /** - * note: it would make more sense to assert(pos > binlog_end_pos) - * but there are two places triggered by mtr that has pos == binlog_end_pos - * i didn't investigate but accepted as it should do no harm - */ - DBUG_ASSERT(pos >= binlog_end_pos); - binlog_end_pos= pos; - signal_update(); - unlock_binlog_end_pos(); - } - /** * used when opening new file, and binlog_end_pos moves backwards */ @@ -866,7 +907,7 @@ public: lock_binlog_end_pos(); binlog_end_pos= pos; strcpy(binlog_end_pos_file, file_name); - signal_update(); + signal_bin_log_update(); unlock_binlog_end_pos(); } @@ -1103,6 +1144,7 @@ void make_default_log_name(char **out, const char* log_ext, bool once); void binlog_reset_cache(THD *thd); extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log; +extern handlerton *binlog_hton; extern LOGGER logger; extern const char *log_bin_index; diff --git a/sql/log_event.cc b/sql/log_event.cc index 1449befc748..0a46bf44d7b 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifndef MYSQL_CLIENT @@ -323,7 +323,7 @@ public: bool res; if (copy_event_cache_to_string_and_reinit(m_cache, &tmp_str)) return 1; - res= m_ev->output_buf.append(tmp_str.str, tmp_str.length) != 0; + res= m_ev->output_buf.append(&tmp_str) != 0; my_free(tmp_str.str); return res ? res : 0; } @@ -1876,7 +1876,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, DBUG_RETURN(0); } -Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock, +Log_event* Log_event::read_log_event(IO_CACHE* file, const Format_description_log_event *fdle, my_bool crc_check) { @@ -1886,9 +1886,6 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock, const char *error= 0; Log_event *res= 0; - if (log_lock) - mysql_mutex_lock(log_lock); - switch (read_log_event(file, &event, fdle, BINLOG_CHECKSUM_ALG_OFF)) { case 0: @@ -1925,8 +1922,6 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock, res->register_temp_buf(event.release(), true); err: - if (log_lock) - mysql_mutex_unlock(log_lock); if (error) { DBUG_ASSERT(!res); @@ -5324,6 +5319,7 @@ bool test_if_equal_repl_errors(int expected_error, int actual_error) return 1; switch (expected_error) { case ER_DUP_ENTRY: + case ER_DUP_ENTRY_WITH_KEY_NAME: case ER_AUTOINC_READ_FAILED: return (actual_error == ER_AUTOINC_READ_FAILED || actual_error == HA_ERR_AUTOINC_ERANGE); @@ -5360,6 +5356,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, int expected_error,actual_error= 0; Schema_specification_st db_options; uint64 sub_id= 0; + void *hton= NULL; rpl_gtid gtid; Relay_log_info const *rli= rgi->rli; Rpl_filter *rpl_filter= rli->mi->rpl_filter; @@ -5530,7 +5527,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, gtid= rgi->current_gtid; if (rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, - true, false)) + true, false, &hton)) { int errcode= thd->get_stmt_da()->sql_errno(); if (!is_parallel_retry_error(rgi, errcode)) @@ -5583,13 +5580,13 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, if (thd->slave_thread) { /* - The opt_log_slow_slave_statements variable can be changed - dynamically, so we have to set the sql_log_slow respectively. + To be compatible with previous releases, the slave thread uses the global + log_slow_disabled_statements value, wich can be changed dynamically, so we + have to set the sql_log_slow respectively. */ - thd->variables.sql_log_slow= opt_log_slow_slave_statements; + thd->variables.sql_log_slow= !MY_TEST(global_system_variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE); } - thd->enable_slow_log= thd->variables.sql_log_slow; mysql_parse(thd, thd->query(), thd->query_length(), &parser_state, FALSE, FALSE); /* Finalize server status flags after executing a statement. */ @@ -5751,7 +5748,7 @@ compare_errors: end: if (sub_id && !thd->is_slave_error) - rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, rgi); + rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi); /* Probably we have set thd->query, thd->db, thd->catalog to point to places @@ -6354,7 +6351,7 @@ bool Format_description_log_event::write() FD_queue checksum_alg value. */ compile_time_assert(BINLOG_CHECKSUM_ALG_DESC_LEN == 1); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS data_written= 0; // to prepare for need_checksum assert #endif uint8 checksum_byte= (uint8) @@ -8294,15 +8291,17 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) int ret; if (gl_flags & FLAG_IGN_GTIDS) { + void *hton= NULL; uint32 i; + for (i= 0; i < count; ++i) { if ((ret= rpl_global_gtid_slave_state->record_gtid(thd, &list[i], - sub_id_list[i], - false, false))) + sub_id_list[i], + false, false, &hton))) return ret; rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i], - NULL); + hton, NULL); } } ret= Log_event::do_apply_event(rgi); @@ -8812,6 +8811,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) rpl_gtid gtid; uint64 sub_id= 0; Relay_log_info const *rli= rgi->rli; + void *hton= NULL; /* XID_EVENT works like a COMMIT statement. And it also updates the @@ -8836,7 +8836,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) gtid= rgi->current_gtid; err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true, - false); + false, &hton); if (err) { int ec= thd->get_stmt_da()->sql_errno(); @@ -8869,7 +8869,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) thd->mdl_context.release_transactional_locks(); if (!res && sub_id) - rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, rgi); + rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi); /* Increment the global status commit count variable @@ -10205,7 +10205,6 @@ int Execute_load_log_event::do_apply_event(rpl_group_info *rgi) } if (!(lev= (Load_log_event*) Log_event::read_log_event(&file, - (mysql_mutex_t*)0, rli->relay_log.description_event_for_exec, opt_slave_sql_verify_checksum)) || lev->get_type_code() != NEW_LOAD_EVENT) @@ -13415,7 +13414,7 @@ int Rows_log_event::find_key() */ last_part= key->user_defined_key_parts - 1; DBUG_PRINT("info", ("Index %s rec_per_key[%u]= %lu", - key->name, last_part, key->rec_per_key[last_part])); + key->name.str, last_part, key->rec_per_key[last_part])); if (!(m_table->file->index_flags(i, last_part, 1) & HA_READ_NEXT)) continue; @@ -13466,13 +13465,13 @@ void issue_long_find_row_warning(Log_event_type type, if ((global_system_variables.log_warnings > 1 && !rgi->is_long_find_row_note_printed())) { - time_t now= my_time(0); - time_t stmt_ts= rgi->get_row_stmt_start_timestamp(); + ulonglong now= microsecond_interval_timer(); + ulonglong stmt_ts= rgi->get_row_stmt_start_timestamp(); DBUG_EXECUTE_IF("inject_long_find_row_note", - stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2);); + stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2*HRTIME_RESOLUTION);); - long delta= (long) (now - stmt_ts); + longlong delta= (now - stmt_ts)/HRTIME_RESOLUTION; if (delta > LONG_FIND_ROW_THRESHOLD) { @@ -13602,10 +13601,10 @@ int Rows_log_event::find_row(rpl_group_info *rgi) if (m_key_info) { DBUG_PRINT("info",("locating record using key #%u [%s] (index_read)", - m_key_nr, m_key_info->name)); + m_key_nr, m_key_info->name.str)); /* We use this to test that the correct key is used in test cases. */ DBUG_EXECUTE_IF("slave_crash_if_wrong_index", - if(0 != strcmp(m_key_info->name,"expected_key")) abort();); + if(0 != strcmp(m_key_info->name.str,"expected_key")) abort();); /* The key is active: search the table using the index */ if (!table->file->inited && @@ -13878,7 +13877,6 @@ int Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, int error) { - /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/ m_table->file->ha_index_or_rnd_end(); my_free(m_key); m_key= NULL; @@ -14577,43 +14575,6 @@ Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len, #endif #if defined(MYSQL_SERVER) -/* - Access to the current replication position. - - There is a dummy replacement for this in the embedded library that returns - FALSE; this is used by XtraDB to allow it to access replication stuff while - still being able to use the same plugin in both stand-alone and embedded. - - In this function it's ok to use active_mi, as this is only called for - the main replication server. -*/ -bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos, - const char **group_relay_log_name, - ulonglong *relay_log_pos) -{ -#if defined(EMBEDDED_LIBRARY) || !defined(HAVE_REPLICATION) - return FALSE; -#else - const Relay_log_info *rli= &(active_mi->rli); - if (!rli->mi->using_parallel()) - { - *log_file_name= rli->group_master_log_name; - *log_pos= rli->group_master_log_pos + - (rli->future_event_relay_log_pos - rli->group_relay_log_pos); - *group_relay_log_name= rli->group_relay_log_name; - *relay_log_pos= rli->future_event_relay_log_pos; - } - else - { - *log_file_name= ""; - *log_pos= 0; - *group_relay_log_name= ""; - *relay_log_pos= 0; - } - return TRUE; -#endif -} - /** Check if we should write event to the relay log diff --git a/sql/log_event.h b/sql/log_event.h index 6b43dcaa763..1feaee86ec7 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1309,7 +1309,6 @@ public: constructor and pass description_event as an argument. */ static Log_event* read_log_event(IO_CACHE* file, - mysql_mutex_t* log_lock, const Format_description_log_event *description_event, my_bool crc_check); @@ -5189,10 +5188,6 @@ inline int Log_event_writer::write(Log_event *ev) bool slave_execute_deferred_events(THD *thd); #endif -bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos, - const char **group_relay_log_name, - ulonglong *relay_log_pos); - bool event_that_should_be_ignored(const char *buf); bool event_checksum_test(uchar *buf, ulong event_len, enum_binlog_checksum_alg alg); enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len); diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index bda9a345790..20986050203 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifndef MYSQL_CLIENT #include "unireg.h" diff --git a/sql/log_slow.h b/sql/log_slow.h index c52722f0cd7..069c35173b9 100644 --- a/sql/log_slow.h +++ b/sql/log_slow.h @@ -15,6 +15,9 @@ /* Defining what to log to slow log */ +#ifndef LOG_SLOW_INCLUDED +#define LOG_SLOW_INCLUDED + #define LOG_SLOW_VERBOSITY_INIT 0 #define LOG_SLOW_VERBOSITY_INNODB (1U << 0) #define LOG_SLOW_VERBOSITY_QUERY_PLAN (1U << 1) @@ -25,13 +28,26 @@ #define QPLAN_ADMIN (1U << 0) #define QPLAN_FILESORT (1U << 1) #define QPLAN_FILESORT_DISK (1U << 2) -#define QPLAN_FULL_JOIN (1U << 3) -#define QPLAN_FULL_SCAN (1U << 4) -#define QPLAN_QC (1U << 5) -#define QPLAN_QC_NO (1U << 6) -#define QPLAN_TMP_DISK (1U << 7) -#define QPLAN_TMP_TABLE (1U << 8) -#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 9) +#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 3) +#define QPLAN_FULL_JOIN (1U << 4) +#define QPLAN_FULL_SCAN (1U << 5) +#define QPLAN_NOT_USING_INDEX (1U << 6) +#define QPLAN_QC (1U << 7) +#define QPLAN_QC_NO (1U << 8) +#define QPLAN_TMP_TABLE (1U << 9) +#define QPLAN_TMP_DISK (1U << 10) /* ... */ #define QPLAN_MAX (1UL << 31) /* reserved as placeholder */ + +/* Bits for log_slow_disabled_statements */ +#define LOG_SLOW_DISABLE_ADMIN (1 << 0) +#define LOG_SLOW_DISABLE_CALL (1 << 1) +#define LOG_SLOW_DISABLE_SLAVE (1 << 2) +#define LOG_SLOW_DISABLE_SP (1 << 3) + +/* Bits for log_disabled_statements */ +#define LOG_DISABLE_SLAVE (1 << 0) +#define LOG_DISABLE_SP (1 << 1) + +#endif /* LOG_SLOW_INCLUDED */ diff --git a/sql/mariadb.h b/sql/mariadb.h new file mode 100644 index 00000000000..00cf2ed1d9c --- /dev/null +++ b/sql/mariadb.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2010, 2017, MariaDB Corporation. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + Include file that should always be included first in all file in the sql + directory. Used to ensure that some files, like my_global.h and my_config.h + are always included first. + It can also be used to speed up compilation by using precompiled headers. + + This file should include a minum set of header files used by all files + and header files that are very seldom changed. + It can also include some defines that all files should be aware of. +*/ + +#ifndef MARIADB_INCLUDED +#define MARIADB_INCLUDED +#include <my_global.h> +#endif /* MARIADB_INCLUDED */ diff --git a/sql/mdl.cc b/sql/mdl.cc index 8ce4e291706..bf708c945f4 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -14,6 +14,7 @@ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ +#include "mariadb.h" #include "sql_class.h" #include "debug_sync.h" #include "sql_array.h" @@ -2086,6 +2087,14 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) */ lock= ticket->m_lock; + if (lock_wait_timeout == 0) + { + mysql_prlock_unlock(&lock->m_rwlock); + MDL_ticket::destroy(ticket); + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); + DBUG_RETURN(TRUE); + } + lock->m_waiting.add_ticket(ticket); /* @@ -2110,11 +2119,11 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) the parallel replication scheduler should never schedule a DDL while DML's are still running. */ - DBUG_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE && - mdl_request->type != MDL_EXCLUSIVE) || - !(get_thd()->rgi_slave && - get_thd()->rgi_slave->is_parallel_exec && - lock->check_if_conflicting_replication_locks(this))); + DBUG_SLOW_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE && + mdl_request->type != MDL_EXCLUSIVE) || + !(get_thd()->rgi_slave && + get_thd()->rgi_slave->is_parallel_exec && + lock->check_if_conflicting_replication_locks(this))); mysql_prlock_unlock(&lock->m_rwlock); @@ -2635,7 +2644,7 @@ void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket) void MDL_context::release_lock(MDL_ticket *ticket) { - DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT); + DBUG_SLOW_ASSERT(ticket->m_duration == MDL_EXPLICIT); release_lock(MDL_EXPLICIT, ticket); } @@ -2895,8 +2904,8 @@ bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint, void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration) { - DBUG_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION && - duration != MDL_TRANSACTION); + DBUG_SLOW_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION && + duration != MDL_TRANSACTION); m_tickets[MDL_TRANSACTION].remove(mdl_ticket); m_tickets[duration].push_front(mdl_ticket); diff --git a/sql/mdl.h b/sql/mdl.h index 97216b8e7b1..f30c976ac94 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -349,7 +349,7 @@ public: NAME_LEN) - m_ptr + 1); m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1, m_length - 1); - DBUG_ASSERT(mdl_namespace_arg == USER_LOCK || ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(mdl_namespace_arg == USER_LOCK || ok_for_lower_case_names(db)); } void mdl_key_init(const MDL_key *rhs) { diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index 6535f16445b..3d3b4da11db 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -32,7 +32,7 @@ flush_io_cache(). */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // THD #ifdef HAVE_REPLICATION diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 50918d8dcf2..c532a489684 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "sql_parse.h" #include <my_bit.h> #include "sql_select.h" diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h index b8234998f74..56761e3623f 100644 --- a/sql/multi_range_read.h +++ b/sql/multi_range_read.h @@ -57,7 +57,7 @@ Storage engine internals - Currently DS-MRR is used by MyISAM, InnoDB/XtraDB and Maria storage engines. + Currently DS-MRR is used by MyISAM, InnoDB and Maria storage engines. Potentially it can be used with any table handler that has disk-based data storage and has better performance when reading data in rowid order. */ diff --git a/sql/my_apc.cc b/sql/my_apc.cc index 2699ac9c60b..9a0310234a2 100644 --- a/sql/my_apc.cc +++ b/sql/my_apc.cc @@ -17,6 +17,7 @@ #ifndef MY_APC_STANDALONE +#include "mariadb.h" #include "sql_class.h" #endif diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index be732d4a927..6d1c746fca8 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include <time.h> diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 265b370a154..918213568fc 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -132,8 +132,8 @@ public: void sanity_check() { - DBUG_ASSERT(foo1 == test_value); - DBUG_ASSERT(foo2 == test_value); + DBUG_SLOW_ASSERT(foo1 == test_value); + DBUG_SLOW_ASSERT(foo2 == test_value); } void fix_buffer_pointer() { buf= buffer; } diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index 390123fbba9..1d61986034a 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_string.h" diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index 5960346c60e..30f2d4f6526 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -18,9 +18,8 @@ on Windows. */ #define DONT_DEFINE_VOID -#include <my_global.h> +#include "mariadb.h" #include <my_getopt.h> -#include <my_sys.h> #include <m_string.h> #include <windows.h> diff --git a/sql/mysql_upgrade_service.cc b/sql/mysql_upgrade_service.cc index 36de05e54e4..2f242bebb0d 100644 --- a/sql/mysql_upgrade_service.cc +++ b/sql/mysql_upgrade_service.cc @@ -20,8 +20,8 @@ */ #define DONT_DEFINE_VOID +#include "mariadb.h" #include <process.h> -#include <my_global.h> #include <my_getopt.h> #include <my_sys.h> #include <m_string.h> @@ -519,4 +519,4 @@ int main(int argc, char **argv) CloseHandle(logfile_handle); my_end(0); exit(0); -}
\ No newline at end of file +} diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 82ca7860195..c2f0bbf2f10 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" // Includes mariadb.h #include "sql_priv.h" #include "unireg.h" #include <signal.h> @@ -75,6 +75,7 @@ #include "wsrep_var.h" #include "wsrep_thd.h" #include "wsrep_sst.h" +#include "proxy_protocol.h" #include "sql_callback.h" #include "threadpool.h" @@ -96,8 +97,8 @@ #include "set_var.h" #include "rpl_injector.h" - -#include "rpl_handler.h" +#include "semisync_master.h" +#include "semisync_slave.h" #ifdef HAVE_SYS_PRCTL_H #include <sys/prctl.h> @@ -373,6 +374,8 @@ char *my_bind_addr_str; static char *default_collation_name; char *default_storage_engine, *default_tmp_storage_engine; char *enforced_storage_engine=NULL; +char *gtid_pos_auto_engines; +plugin_ref *opt_gtid_pos_auto_plugins; static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME; static I_List<CONNECT> thread_cache; static bool binlog_format_used= false; @@ -394,7 +397,6 @@ my_bool disable_log_notes, opt_support_flashback= 0; static my_bool opt_abort; ulonglong log_output_options; my_bool opt_userstat_running; -my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; bool opt_skip_name_resolve=0; @@ -442,6 +444,7 @@ my_bool opt_replicate_annotate_row_events= 0; my_bool opt_mysql56_temporal_format=0, strict_password_validation= 1; my_bool opt_explicit_defaults_for_timestamp= 0; char *opt_slave_skip_errors; +char *opt_slave_transaction_retry_errors; /* Legacy global handlerton. These will be removed (please do not add more). @@ -456,8 +459,6 @@ my_bool relay_log_recovery; my_bool opt_sync_frm, opt_allow_suspicious_udfs; my_bool opt_secure_auth= 0; char* opt_secure_file_priv; -my_bool opt_log_slow_admin_statements= 0; -my_bool opt_log_slow_slave_statements= 0; my_bool lower_case_file_system= 0; my_bool opt_large_pages= 0; my_bool opt_super_large_pages= 0; @@ -491,7 +492,6 @@ uint protocol_version; uint lower_case_table_names; ulong tc_heuristic_recover= 0; int32 thread_count, service_thread_count; -int32 thread_running; int32 slave_open_temp_tables; ulong thread_created; ulong back_log, connect_timeout, concurrency, server_id; @@ -499,6 +499,7 @@ ulong what_to_log; ulong slow_launch_time; ulong open_files_limit, max_binlog_size; ulong slave_trans_retries; +ulong slave_trans_retry_interval; uint slave_net_timeout; ulong slave_exec_mode_options; ulong slave_run_triggers_for_rbr= 0; @@ -506,6 +507,7 @@ ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT; ulonglong slave_type_conversions_options; ulong thread_cache_size=0; ulonglong binlog_cache_size=0; +ulonglong binlog_file_cache_size=0; ulonglong max_binlog_cache_size=0; ulong slave_max_allowed_packet= 0; ulonglong binlog_stmt_cache_size=0; @@ -525,6 +527,9 @@ ulong max_connections, max_connect_errors; ulong extra_max_connections; uint max_digest_length= 0; ulong slave_retried_transactions; +ulong transactions_multi_engine; +ulong rpl_transactions_multi_engine; +ulong transactions_gtid_foreign_engine; ulonglong slave_skipped_errors; ulong feature_files_opened_with_delayed_keys= 0, feature_check_constraint= 0; ulonglong denied_connections; @@ -905,7 +910,7 @@ PSI_mutex_key key_LOCK_des_key_file; PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_BINLOG_LOCK_binlog_background_thread, - m_key_LOCK_binlog_end_pos, + key_LOCK_binlog_end_pos, key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, @@ -927,8 +932,10 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_thread_count, key_LOCK_thread_cache, key_PARTITION_LOCK_auto_inc; PSI_mutex_key key_RELAYLOG_LOCK_index; +PSI_mutex_key key_LOCK_relaylog_end_pos; PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; +PSI_mutex_key key_LOCK_binlog; PSI_mutex_key key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, @@ -940,6 +947,7 @@ PSI_mutex_key key_LOCK_after_binlog_sync; PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered, key_LOCK_slave_background; PSI_mutex_key key_TABLE_SHARE_LOCK_share; +PSI_mutex_key key_LOCK_ack_receiver; static PSI_mutex_info all_server_mutexes[]= { @@ -958,8 +966,9 @@ static PSI_mutex_info all_server_mutexes[]= { &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0}, { &key_BINLOG_LOCK_xid_list, "MYSQL_BIN_LOG::LOCK_xid_list", 0}, { &key_BINLOG_LOCK_binlog_background_thread, "MYSQL_BIN_LOG::LOCK_binlog_background_thread", 0}, - { &m_key_LOCK_binlog_end_pos, "MYSQL_BIN_LOG::LOCK_binlog_end_pos", 0 }, + { &key_LOCK_binlog_end_pos, "MYSQL_BIN_LOG::LOCK_binlog_end_pos", 0 }, { &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0}, + { &key_LOCK_relaylog_end_pos, "MYSQL_RELAY_LOG::LOCK_binlog_end_pos", 0}, { &key_delayed_insert_mutex, "Delayed_insert::mutex", 0}, { &key_hash_filo_lock, "hash_filo::lock", 0}, { &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL}, @@ -1016,7 +1025,9 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_binlog_state, "LOCK_binlog_state", 0}, { &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0}, { &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0}, - { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0} + { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0}, + { &key_LOCK_ack_receiver, "Ack_receiver::mutex", 0}, + { &key_LOCK_binlog, "LOCK_binlog", 0} }; PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, @@ -1043,7 +1054,8 @@ static PSI_rwlock_info all_server_rwlocks[]= PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; #endif /* HAVE_MMAP */ -PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, +PSI_cond_key key_BINLOG_COND_xid_list, + key_BINLOG_COND_bin_log_updated, key_BINLOG_COND_relay_log_updated, key_BINLOG_COND_binlog_background_thread, key_BINLOG_COND_binlog_background_thread_end, key_COND_cache_status_changed, key_COND_manager, @@ -1057,9 +1069,10 @@ PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, key_rpl_group_info_sleep_cond, key_TABLE_SHARE_cond, key_user_level_lock_cond, key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache, - key_COND_start_thread, + key_COND_start_thread, key_COND_binlog_send, key_BINLOG_COND_queue_busy; -PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready, +PSI_cond_key key_RELAYLOG_COND_relay_log_updated, + key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready, key_COND_wait_commit; PSI_cond_key key_RELAYLOG_COND_queue_busy; PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy; @@ -1068,6 +1081,7 @@ PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread, key_COND_parallel_entry, key_COND_group_commit_orderer, key_COND_prepare_ordered, key_COND_slave_background; PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates; +PSI_cond_key key_COND_ack_receiver; static PSI_cond_info all_server_conds[]= { @@ -1080,12 +1094,13 @@ static PSI_cond_info all_server_conds[]= { &key_COND_pool, "TC_LOG_MMAP::COND_pool", 0}, { &key_TC_LOG_MMAP_COND_queue_busy, "TC_LOG_MMAP::COND_queue_busy", 0}, #endif /* HAVE_MMAP */ + { &key_BINLOG_COND_bin_log_updated, "MYSQL_BIN_LOG::COND_bin_log_updated", 0}, { &key_BINLOG_COND_relay_log_updated, "MYSQL_BIN_LOG::COND_relay_log_updated", 0}, { &key_BINLOG_COND_xid_list, "MYSQL_BIN_LOG::COND_xid_list", 0}, - { &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0}, { &key_BINLOG_COND_binlog_background_thread, "MYSQL_BIN_LOG::COND_binlog_background_thread", 0}, { &key_BINLOG_COND_binlog_background_thread_end, "MYSQL_BIN_LOG::COND_binlog_background_thread_end", 0}, { &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0}, - { &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0}, + { &key_RELAYLOG_COND_relay_log_updated, "MYSQL_RELAY_LOG::COND_relay_log_updated", 0}, + { &key_RELAYLOG_COND_bin_log_updated, "MYSQL_RELAY_LOG::COND_bin_log_updated", 0}, { &key_RELAYLOG_COND_queue_busy, "MYSQL_RELAY_LOG::COND_queue_busy", 0}, { &key_COND_wakeup_ready, "THD::COND_wakeup_ready", 0}, { &key_COND_wait_commit, "wait_for_commit::COND_wait_commit", 0}, @@ -1119,13 +1134,16 @@ static PSI_cond_info all_server_conds[]= { &key_COND_slave_background, "COND_slave_background", 0}, { &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL}, { &key_COND_wait_gtid, "COND_wait_gtid", 0}, - { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0} + { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}, + { &key_COND_ack_receiver, "Ack_receiver::cond", 0}, + { &key_COND_binlog_send, "COND_binlog_send", 0} }; PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, key_thread_handle_manager, key_thread_main, key_thread_one_connection, key_thread_signal_hand, key_thread_slave_background, key_rpl_parallel_thread; +PSI_thread_key key_thread_ack_receiver; static PSI_thread_info all_server_threads[]= { @@ -1152,6 +1170,7 @@ static PSI_thread_info all_server_threads[]= { &key_thread_one_connection, "one_connection", 0}, { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL}, { &key_thread_slave_background, "slave_background", PSI_FLAG_GLOBAL}, + { &key_thread_ack_receiver, "Ack_receiver", PSI_FLAG_GLOBAL}, { &key_rpl_parallel_thread, "rpl_parallel_thread", 0} }; @@ -1629,13 +1648,11 @@ static void close_connections(void) { if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET) { - (void) mysql_socket_shutdown(base_ip_sock, SHUT_RDWR); (void) mysql_socket_close(base_ip_sock); base_ip_sock= MYSQL_INVALID_SOCKET; } if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET) { - (void) mysql_socket_shutdown(extra_ip_sock, SHUT_RDWR); (void) mysql_socket_close(extra_ip_sock); extra_ip_sock= MYSQL_INVALID_SOCKET; } @@ -1667,7 +1684,6 @@ static void close_connections(void) #ifdef HAVE_SYS_UN_H if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET) { - (void) mysql_socket_shutdown(unix_sock, SHUT_RDWR); (void) mysql_socket_close(unix_sock); (void) unlink(mysqld_unix_port); unix_sock= MYSQL_INVALID_SOCKET; @@ -1734,6 +1750,7 @@ static void close_connections(void) Events::deinit(); slave_prepare_for_shutdown(); mysql_bin_log.stop_background_thread(); + ack_receiver.stop(); /* Give threads time to die. @@ -2163,7 +2180,10 @@ static void mysqld_exit(int exit_code) (long) global_status_var.global_memory_used); if (!opt_debugging && !my_disable_leak_check && exit_code == 0) { - DBUG_ASSERT(global_status_var.global_memory_used == 0); +#ifdef SAFEMALLOC + sf_report_leaked_memory(0); +#endif + DBUG_SLOW_ASSERT(global_status_var.global_memory_used == 0); } cleanup_tls(); DBUG_LEAVE; @@ -2212,7 +2232,9 @@ void clean_up(bool print_message) ha_end(); if (tc_log) tc_log->close(); - delegates_destroy(); +#ifdef HAVE_REPLICATION + semi_sync_master_deinit(); +#endif xid_cache_free(); tdc_deinit(); mdl_destroy(); @@ -2287,6 +2309,7 @@ void clean_up(bool print_message) my_free(const_cast<char*>(relay_log_index)); #endif free_list(opt_plugin_load_list_ptr); + cleanup_proxy_protocol_networks(); /* The following lines may never be executed as the main thread may have @@ -2683,6 +2706,9 @@ static void network_init(void) if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)) unireg_abort(1); /* purecov: inspected */ + if (set_proxy_protocol_networks(my_proxy_protocol_networks)) + unireg_abort(1); + set_ports(); if (report_port == 0) @@ -2893,16 +2919,17 @@ void unlink_thd(THD *thd) DBUG_ENTER("unlink_thd"); DBUG_PRINT("enter", ("thd: %p", thd)); + thd->cleanup(); + thd->add_status_to_global(); + unlink_not_visible_thd(thd); + /* Do not decrement when its wsrep system thread. wsrep_applier is set for applier as well as rollbacker threads. */ if (IF_WSREP(!thd->wsrep_applier, 1)) dec_connection_count(thd->scheduler); - thd->cleanup(); - thd->add_status_to_global(); - unlink_not_visible_thd(thd); thd->free_connection(); DBUG_VOID_RETURN; @@ -3318,6 +3345,20 @@ static size_t my_setstacksize(pthread_attr_t *attr, size_t stacksize) } #endif +#ifdef DBUG_ASSERT_AS_PRINTF +extern "C" void +mariadb_dbug_assert_failed(const char *assert_expr, const char *file, + unsigned long line) +{ + fprintf(stderr, "Warning: assertion failed: %s at %s line %lu\n", + assert_expr, file, line); + if (opt_stack_trace) + { + fprintf(stderr, "Attempting backtrace to find out the reason for the assert:\n"); + my_print_stacktrace(NULL, (ulong) my_thread_stack_size, 1); + } +} +#endif /* DBUG_ASSERT_AS_PRINT */ #if !defined(__WIN__) #ifndef SA_RESETHAND @@ -4122,7 +4163,10 @@ static int init_common_variables() #ifdef SAFEMALLOC sf_malloc_dbug_id= mariadb_dbug_id; -#endif +#endif /* SAFEMALLOC */ +#ifdef DBUG_ASSERT_AS_PRINTF + my_dbug_assert_failed= mariadb_dbug_assert_failed; +#endif /* DBUG_ASSERT_AS_PRINTF */ if (!(type_handler_data= new Type_handler_data) || type_handler_data->init()) @@ -4191,10 +4235,12 @@ static int init_common_variables() constructor (called before main()). */ mysql_bin_log.set_psi_keys(key_BINLOG_LOCK_index, - key_BINLOG_update_cond, + key_BINLOG_COND_relay_log_updated, + key_BINLOG_COND_bin_log_updated, key_file_binlog, key_file_binlog_index, - key_BINLOG_COND_queue_busy); + key_BINLOG_COND_queue_busy, + key_LOCK_binlog_end_pos); #endif /* @@ -4244,12 +4290,13 @@ static int init_common_variables() (except in the embedded server, where the default continues to be MyISAM) */ -#if defined(WITH_INNOBASE_STORAGE_ENGINE) || defined(WITH_XTRADB_STORAGE_ENGINE) +#if defined(WITH_INNOBASE_STORAGE_ENGINE) default_storage_engine= const_cast<char *>("InnoDB"); #else default_storage_engine= const_cast<char *>("MyISAM"); #endif default_tmp_storage_engine= NULL; + gtid_pos_auto_engines= const_cast<char *>(""); /* Add server status variables to the dynamic list of @@ -4665,6 +4712,8 @@ static int init_common_variables() return 1; } + global_system_variables.in_subquery_conversion_threshold= IN_SUBQUERY_CONVERSION_THRESHOLD; + return 0; } @@ -4941,6 +4990,34 @@ static int init_default_storage_engine_impl(const char *opt_name, return 0; } + +static int +init_gtid_pos_auto_engines(void) +{ + plugin_ref *plugins; + + /* + For the command-line option --gtid_pos_auto_engines, we allow (and ignore) + engines that are unknown. This is convenient, since it allows to set + default auto-create engines that might not be used by particular users. + The option sets a list of storage engines that will have gtid position + table auto-created for them if needed. And if the engine is not available, + then it will certainly not be needed. + */ + if (gtid_pos_auto_engines) + plugins= resolve_engine_list(NULL, gtid_pos_auto_engines, + strlen(gtid_pos_auto_engines), false, false); + else + plugins= resolve_engine_list(NULL, "", 0, false, false); + if (!plugins) + return 1; + mysql_mutex_lock(&LOCK_global_system_variables); + opt_gtid_pos_auto_plugins= plugins; + mysql_mutex_unlock(&LOCK_global_system_variables); + return 0; +} + + static int init_server_components() { DBUG_ENTER("init_server_components"); @@ -5058,13 +5135,6 @@ static int init_server_components() xid_cache_init(); - /* - initialize delegates for extension observers, errors have already - been reported in the function - */ - if (delegates_init()) - unireg_abort(1); - /* need to configure logging before initializing storage engines */ if (!opt_bin_log_used && !WSREP_ON) { @@ -5096,6 +5166,13 @@ static int init_server_components() "this server. However this will be ignored as the " "--log-bin option is not defined."); } + + if (repl_semisync_master.init_object() || + repl_semisync_slave.init_object()) + { + sql_print_error("Could not initialize semisync."); + unireg_abort(1); + } #endif if (opt_bin_log) @@ -5384,6 +5461,9 @@ static int init_server_components() if (init_default_storage_engine(enforced_storage_engine, enforced_table_plugin)) unireg_abort(1); + if (init_gtid_pos_auto_engines()) + unireg_abort(1); + #ifdef USE_ARIA_FOR_TMP_TABLES if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap) { @@ -6345,6 +6425,7 @@ static void bootstrap(MYSQL_FILE *file) sql_print_warning("Can't create thread to handle bootstrap (errno= %d)", error); bootstrap_error=-1; + delete thd; DBUG_VOID_RETURN; } /* Wait for thread to die */ @@ -7224,9 +7305,6 @@ struct my_option my_long_options[]= {"autocommit", 0, "Set default value for autocommit (0 or 1)", &opt_autocommit, &opt_autocommit, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, NULL}, - {"bind-address", 0, "IP address to bind to.", - &my_bind_addr_str, &my_bind_addr_str, 0, GET_STR, - REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, " "and exclude all others not explicitly mentioned.", @@ -7377,6 +7455,14 @@ struct my_option my_long_options[]= "Set up signals usable for debugging. Deprecated, use --debug-gdb instead.", &opt_debugging, &opt_debugging, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"gtid-pos-auto-engines", 0, + "List of engines for which to automatically create a " + "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine " + "is replicated. This can be used to avoid introducing cross-engine " + "transactions, if engines are used different from that used by table " + "mysql.gtid_slave_pos", + >id_pos_auto_engines, 0, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0 }, #ifdef HAVE_LARGE_PAGE_OPTION {"super-large-pages", 0, "Enable support for super large pages.", &opt_super_large_pages, &opt_super_large_pages, 0, @@ -7674,6 +7760,7 @@ struct my_option my_long_options[]= MYSQL_SUGGEST_ANALOG_OPTION("max-binlog-dump-events", "--debug-max-binlog-dump-events"), MYSQL_SUGGEST_ANALOG_OPTION("sporadic-binlog-dump-fail", "--debug-sporadic-binlog-dump-fail"), MYSQL_COMPATIBILITY_OPTION("new"), + MYSQL_COMPATIBILITY_OPTION("show_compatibility_56"), /* The following options were added after 5.6.10 */ MYSQL_TO_BE_IMPLEMENTED_OPTION("rpl-stop-slave-timeout"), @@ -7774,7 +7861,7 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) var->type= SHOW_LONGLONG; var->value= buff; - *((longlong *)buff)= any_slave_sql_running(); + *((longlong *)buff)= any_slave_sql_running(false); return 0; } @@ -8146,6 +8233,27 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff, return 0; } +#define SHOW_FNAME(name) \ + rpl_semi_sync_master_show_##name + +#define DEF_SHOW_FUNC(name, show_type) \ + static int SHOW_FNAME(name)(MYSQL_THD thd, SHOW_VAR *var, char *buff) \ + { \ + repl_semisync_master.set_export_stats(); \ + var->type= show_type; \ + var->value= (char *)&rpl_semi_sync_master_##name; \ + return 0; \ + } + +DEF_SHOW_FUNC(status, SHOW_BOOL) +DEF_SHOW_FUNC(clients, SHOW_LONG) +DEF_SHOW_FUNC(wait_sessions, SHOW_LONG) +DEF_SHOW_FUNC(trx_wait_time, SHOW_LONGLONG) +DEF_SHOW_FUNC(trx_wait_num, SHOW_LONGLONG) +DEF_SHOW_FUNC(net_wait_time, SHOW_LONGLONG) +DEF_SHOW_FUNC(net_wait_num, SHOW_LONGLONG) +DEF_SHOW_FUNC(avg_net_wait_time, SHOW_LONG) +DEF_SHOW_FUNC(avg_trx_wait_time, SHOW_LONG) #ifdef HAVE_YASSL @@ -8376,6 +8484,8 @@ SHOW_VAR status_vars[]= { {"Busy_time", (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS}, {"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS}, {"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS}, + {"Column_compressions", (char*) offsetof(STATUS_VAR, column_compressions), SHOW_LONG_STATUS}, + {"Column_decompressions", (char*) offsetof(STATUS_VAR, column_decompressions), SHOW_LONG_STATUS}, {"Com", (char*) com_status_vars, SHOW_ARRAY}, {"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC}, {"Connections", (char*) &global_thread_id, SHOW_LONG_NOFLUSH}, @@ -8404,6 +8514,7 @@ SHOW_VAR status_vars[]= { {"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS}, {"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS}, {"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS}, + {"Feature_invisible_columns", (char*) offsetof(STATUS_VAR, feature_invisible_columns), SHOW_LONG_STATUS}, {"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS}, {"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS}, {"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS}, @@ -8461,6 +8572,26 @@ SHOW_VAR status_vars[]= { {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS}, {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS}, {"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS}, +#ifdef HAVE_REPLICATION + {"Rpl_semi_sync_master_status", (char*) &SHOW_FNAME(status), SHOW_FUNC}, + {"Rpl_semi_sync_master_clients", (char*) &SHOW_FNAME(clients), SHOW_FUNC}, + {"Rpl_semi_sync_master_yes_tx", (char*) &rpl_semi_sync_master_yes_transactions, SHOW_LONG}, + {"Rpl_semi_sync_master_no_tx", (char*) &rpl_semi_sync_master_no_transactions, SHOW_LONG}, + {"Rpl_semi_sync_master_wait_sessions", (char*) &SHOW_FNAME(wait_sessions), SHOW_FUNC}, + {"Rpl_semi_sync_master_no_times", (char*) &rpl_semi_sync_master_off_times, SHOW_LONG}, + {"Rpl_semi_sync_master_timefunc_failures", (char*) &rpl_semi_sync_master_timefunc_fails, SHOW_LONG}, + {"Rpl_semi_sync_master_wait_pos_backtraverse", (char*) &rpl_semi_sync_master_wait_pos_backtraverse, SHOW_LONG}, + {"Rpl_semi_sync_master_tx_wait_time", (char*) &SHOW_FNAME(trx_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_tx_waits", (char*) &SHOW_FNAME(trx_wait_num), SHOW_FUNC}, + {"Rpl_semi_sync_master_tx_avg_wait_time", (char*) &SHOW_FNAME(avg_trx_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_net_wait_time", (char*) &SHOW_FNAME(net_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_net_waits", (char*) &SHOW_FNAME(net_wait_num), SHOW_FUNC}, + {"Rpl_semi_sync_master_net_avg_wait_time", (char*) &SHOW_FNAME(avg_net_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_request_ack", (char*) &rpl_semi_sync_master_request_ack, SHOW_LONGLONG}, + {"Rpl_semi_sync_master_get_ack", (char*)&rpl_semi_sync_master_get_ack, SHOW_LONGLONG}, + {"Rpl_semi_sync_slave_status", (char*) &rpl_semi_sync_slave_status, SHOW_BOOL}, + {"Rpl_semi_sync_slave_send_ack", (char*) &rpl_semi_sync_slave_send_ack, SHOW_LONGLONG}, +#endif /* HAVE_REPLICATION */ #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH}, @@ -8537,6 +8668,10 @@ SHOW_VAR status_vars[]= { {"Subquery_cache_miss", (char*) &subquery_cache_miss, SHOW_LONG}, {"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG}, {"Table_locks_waited", (char*) &locks_waited, SHOW_LONG}, + {"Table_open_cache_active_instances", (char*) &tc_active_instances, SHOW_UINT}, + {"Table_open_cache_hits", (char*) offsetof(STATUS_VAR, table_open_cache_hits), SHOW_LONGLONG_STATUS}, + {"Table_open_cache_misses", (char*) offsetof(STATUS_VAR, table_open_cache_misses), SHOW_LONGLONG_STATUS}, + {"Table_open_cache_overflows", (char*) offsetof(STATUS_VAR, table_open_cache_overflows), SHOW_LONGLONG_STATUS}, #ifdef HAVE_MMAP {"Tc_log_max_pages_used", (char*) &tc_log_max_pages_used, SHOW_LONG}, {"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG_NOFLUSH}, @@ -8549,7 +8684,10 @@ SHOW_VAR status_vars[]= { {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH}, {"Threads_connected", (char*) &connection_count, SHOW_INT}, {"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH}, - {"Threads_running", (char*) &thread_running, SHOW_INT}, + {"Threads_running", (char*) offsetof(STATUS_VAR, threads_running), SHOW_UINT32_STATUS}, + {"Transactions_multi_engine", (char*) &transactions_multi_engine, SHOW_LONG}, + {"Rpl_transactions_multi_engine", (char*) &rpl_transactions_multi_engine, SHOW_LONG}, + {"Transactions_gtid_foreign_engine", (char*) &transactions_gtid_foreign_engine, SHOW_LONG}, {"Update_scan", (char*) offsetof(STATUS_VAR, update_scan_count), SHOW_LONG_STATUS}, {"Uptime", (char*) &show_starttime, SHOW_SIMPLE_FUNC}, #ifdef ENABLED_PROFILING @@ -8723,7 +8861,7 @@ static int mysql_init_variables(void) kill_in_progress= 0; cleanup_done= 0; test_flags= select_errors= dropping_tables= ha_open_options=0; - thread_count= thread_running= kill_cached_threads= wake_thread= 0; + thread_count= kill_cached_threads= wake_thread= 0; service_thread_count= 0; slave_open_temp_tables= 0; cached_thread_count= 0; @@ -8793,6 +8931,9 @@ static int mysql_init_variables(void) report_user= report_password = report_host= 0; /* TO BE DELETED */ opt_relay_logname= opt_relaylog_index_name= 0; slave_retried_transactions= 0; + transactions_multi_engine= 0; + rpl_transactions_multi_engine= 0; + transactions_gtid_foreign_engine= 0; log_bin_basename= NULL; log_bin_index= NULL; @@ -8947,12 +9088,12 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument) opt->name); break; case OPT_MYSQL_COMPATIBILITY: - sql_print_warning("'%s' is MySQL 5.6 compatible option. Not used or needed " - "in MariaDB.", opt->name); + sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. Not used or " + "needed in MariaDB.", opt->name); break; case OPT_MYSQL_TO_BE_IMPLEMENTED: - sql_print_warning("'%s' is MySQL 5.6 compatible option. To be implemented " - "in later versions.", opt->name); + sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. To be " + "implemented in later versions.", opt->name); break; case 'a': SYSVAR_AUTOSIZE(global_system_variables.sql_mode, MODE_ANSI); @@ -9540,8 +9681,10 @@ static int get_options(int *argc_ptr, char ***argv_ptr) flush_time= 0; #ifdef HAVE_REPLICATION - if (opt_slave_skip_errors) - init_slave_skip_errors(opt_slave_skip_errors); + if (init_slave_skip_errors(opt_slave_skip_errors)) + return 1; + if (init_slave_transaction_retry_errors(opt_slave_transaction_retry_errors)) + return 1; #endif if (global_system_variables.max_join_size == HA_POS_ERROR) @@ -10186,6 +10329,10 @@ PSI_stage_info stage_waiting_for_insert= { 0, "Waiting for INSERT", 0}; PSI_stage_info stage_waiting_for_master_to_send_event= { 0, "Waiting for master to send event", 0}; PSI_stage_info stage_waiting_for_master_update= { 0, "Waiting for master update", 0}; PSI_stage_info stage_waiting_for_relay_log_space= { 0, "Waiting for the slave SQL thread to free enough relay log space", 0}; +PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave= +{ 0, "Waiting for semi-sync ACK from slave", 0}; +PSI_stage_info stage_waiting_for_semi_sync_slave={ 0, "Waiting for semi-sync slave connection", 0}; +PSI_stage_info stage_reading_semi_sync_ack={ 0, "Reading semi-sync ACK from slave", 0}; PSI_stage_info stage_waiting_for_slave_mutex_on_exit= { 0, "Waiting for slave mutex on exit", 0}; PSI_stage_info stage_waiting_for_slave_thread_to_start= { 0, "Waiting for slave thread to start", 0}; PSI_stage_info stage_waiting_for_table_flush= { 0, "Waiting for table flush", 0}; @@ -10346,6 +10493,9 @@ PSI_stage_info *all_server_stages[]= & stage_gtid_wait_other_connection, & stage_slave_background_process_request, & stage_slave_background_wait_request, + & stage_waiting_for_semi_sync_ack_from_slave, + & stage_waiting_for_semi_sync_slave, + & stage_reading_semi_sync_ack, & stage_waiting_for_deadlock_kill }; diff --git a/sql/mysqld.h b/sql/mysqld.h index ed6685828ba..9a2cde9b145 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -17,8 +17,8 @@ #ifndef MYSQLD_INCLUDED #define MYSQLD_INCLUDED -#include <my_global.h> /* MYSQL_PLUGIN_IMPORT, FN_REFLEN, FN_EXTLEN */ #include "sql_basic_types.h" /* query_id_t */ +#include "sql_plugin.h" #include "sql_bitmap.h" /* Bitmap */ #include "my_decimal.h" /* my_decimal */ #include "mysql_com.h" /* SERVER_VERSION_LENGTH */ @@ -116,7 +116,6 @@ extern my_bool opt_backup_progress_log; extern my_bool opt_support_flashback; extern ulonglong log_output_options; extern ulong log_backup_output_options; -extern my_bool opt_log_queries_not_using_indexes; extern bool opt_disable_networking, opt_skip_show_db; extern bool opt_skip_name_resolve; extern bool opt_ignore_builtin_innodb; @@ -130,6 +129,9 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap; extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options; extern ulong slave_retried_transactions; +extern ulong transactions_multi_engine; +extern ulong rpl_transactions_multi_engine; +extern ulong transactions_gtid_foreign_engine; extern ulong slave_run_triggers_for_rbr; extern ulonglong slave_type_conversions_options; extern my_bool read_only, opt_readonly; @@ -140,7 +142,6 @@ extern const char *current_dbug_option; extern char* opt_secure_file_priv; extern char* opt_secure_backup_file_priv; extern size_t opt_secure_backup_file_priv_len; -extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements; extern my_bool sp_automatic_privileges, opt_noacl; extern ulong use_stat_tables; extern my_bool opt_old_style_user_limits, trust_function_creators; @@ -151,8 +152,11 @@ extern my_bool opt_enable_shared_memory; extern ulong opt_replicate_events_marked_for_skip; extern char *default_tz_name; extern Time_zone *default_tz; +extern char *my_bind_addr_str; extern char *default_storage_engine, *default_tmp_storage_engine; extern char *enforced_storage_engine; +extern char *gtid_pos_auto_engines; +extern plugin_ref *opt_gtid_pos_auto_plugins; extern bool opt_endinfo, using_udf_functions; extern my_bool locked_in_memory; extern bool opt_using_transactions; @@ -204,13 +208,14 @@ extern my_bool slave_allow_batching; extern my_bool allow_slave_start; extern LEX_CSTRING reason_slave_blocked; extern ulong slave_trans_retries; +extern ulong slave_trans_retry_interval; extern uint slave_net_timeout; extern int max_user_connections; extern volatile ulong cached_thread_count; extern ulong what_to_log,flush_time; extern ulong max_prepared_stmt_count, prepared_stmt_count; extern ulong open_files_limit; -extern ulonglong binlog_cache_size, binlog_stmt_cache_size; +extern ulonglong binlog_cache_size, binlog_stmt_cache_size, binlog_file_cache_size; extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size; extern ulong max_binlog_size; extern ulong slave_max_allowed_packet; @@ -284,7 +289,7 @@ extern PSI_mutex_key key_LOCK_des_key_file; extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_BINLOG_LOCK_binlog_background_thread, - m_key_LOCK_binlog_end_pos, + key_LOCK_binlog_end_pos, key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, @@ -304,6 +309,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_start_thread, key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc; extern PSI_mutex_key key_RELAYLOG_LOCK_index; +extern PSI_mutex_key key_LOCK_relaylog_end_pos; extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; @@ -335,7 +341,8 @@ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, key_TABLE_SHARE_cond, key_user_level_lock_cond, key_COND_start_thread, key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache; -extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready, +extern PSI_cond_key key_RELAYLOG_COND_relay_log_updated, + key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready, key_COND_wait_commit; extern PSI_cond_key key_RELAYLOG_COND_queue_busy; extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy; @@ -559,6 +566,7 @@ extern MYSQL_PLUGIN_IMPORT char mysql_real_data_home[]; extern char mysql_unpacked_real_data_home[]; extern MYSQL_PLUGIN_IMPORT struct system_variables global_system_variables; extern char default_logfile_name[FN_REFLEN]; +extern char *my_proxy_protocol_networks; #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list)) @@ -589,7 +597,6 @@ extern mysql_rwlock_t LOCK_system_variables_hash; extern mysql_cond_t COND_thread_count, COND_start_thread; extern mysql_cond_t COND_manager; extern mysql_cond_t COND_slave_background; -extern int32 thread_running; extern int32 thread_count, service_thread_count; extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, @@ -779,16 +786,6 @@ inline void thread_safe_decrement64(int64 *value) (void) my_atomic_add64_explicit(value, -1, MY_MEMORY_ORDER_RELAXED); } -inline void inc_thread_running() -{ - thread_safe_increment32(&thread_running); -} - -inline void dec_thread_running() -{ - thread_safe_decrement32(&thread_running); -} - extern void set_server_version(char *buf, size_t size); #define current_thd _current_thd() diff --git a/sql/net_serv.cc b/sql/net_serv.cc index d9d35c5ed3f..fd0139c1e03 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -35,7 +35,7 @@ embedded library */ -#include <my_global.h> +#include "mariadb.h" #include <mysql.h> #include <mysql_com.h> #include <mysqld_error.h> @@ -45,12 +45,7 @@ #include <violite.h> #include <signal.h> #include "probes_mysql.h" - -#ifdef EMBEDDED_LIBRARY -#undef MYSQL_SERVER -#undef MYSQL_CLIENT -#define MYSQL_CLIENT -#endif /*EMBEDDED_LIBRARY */ +#include "proxy_protocol.h" /* to reduce the number of ifdef's in the code @@ -63,8 +58,11 @@ static void inline EXTRA_DEBUG_fprintf(...) {} #ifndef MYSQL_SERVER static int inline EXTRA_DEBUG_fflush(...) { return 0; } #endif -#endif +#endif /* EXTRA_DEBUG */ + #ifdef MYSQL_SERVER +#include <sql_class.h> +#include <sql_connect.h> #define MYSQL_SERVER_my_error my_error #else static void inline MYSQL_SERVER_my_error(...) {} @@ -118,7 +116,6 @@ extern my_bool thd_net_is_killed(); #define thd_net_is_killed() 0 #endif -#define TEST_BLOCKING 8 static my_bool net_write_buff(NET *, const uchar *, ulong); @@ -829,6 +826,70 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, /** + Try to parse and process proxy protocol header. + + This function is called in case MySQL packet header cannot be parsed. + It checks if proxy header was sent, and that it was send from allowed remote + host, as defined by proxy-protocol-networks parameter. + + If proxy header is parsed, then THD and ACL structures and changed to indicate + the new peer address and port. + + Note, that proxy header can only be sent either when the connection is established, + or as the client reply packet to +*/ +#undef IGNORE /* for Windows */ +typedef enum { RETRY, ABORT, IGNORE} handle_proxy_header_result; +static handle_proxy_header_result handle_proxy_header(NET *net) +{ +#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) + return IGNORE; +#else + THD *thd= (THD *)net->thd; + + if (!has_proxy_protocol_header(net) || !thd || + thd->get_command() != COM_CONNECT) + return IGNORE; + + /* + Proxy information found in the first 4 bytes received so far. + Read and parse proxy header , change peer ip address and port in THD. + */ + proxy_peer_info peer_info; + + if (!thd->net.vio) + { + DBUG_ASSERT(0); + return ABORT; + } + + if (!is_proxy_protocol_allowed((sockaddr *)&(thd->net.vio->remote))) + { + /* proxy-protocol-networks variable needs to be set to allow this remote address */ + my_printf_error(ER_HOST_NOT_PRIVILEGED, "Proxy header is not accepted from %s", + MYF(0), thd->main_security_ctx.ip); + return ABORT; + } + + if (parse_proxy_protocol_header(net, &peer_info)) + { + /* Failed to parse proxy header*/ + my_printf_error(ER_UNKNOWN_ERROR, "Failed to parse proxy header", MYF(0)); + return ABORT; + } + + if (peer_info.is_local_command) + /* proxy header indicates LOCAL connection, no action necessary */ + return RETRY; + /* Change peer address in THD and ACL structures.*/ + uint host_errors; + return (handle_proxy_header_result)thd_set_peer_addr(thd, + &(peer_info.peer_addr), NULL, peer_info.port, + false, &host_errors); +#endif +} + +/** Reads one packet to net->buff + net->where_b. Long packets are handled by my_net_read(). This function reallocates the net->buff buffer if necessary. @@ -850,6 +911,9 @@ my_real_read(NET *net, size_t *complen, #ifndef NO_ALARM ALARM alarm_buff; #endif + +retry: + my_bool net_blocking=vio_is_blocking(net->vio); uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : NET_HEADER_SIZE); @@ -1081,6 +1145,17 @@ end: packets_out_of_order: { + switch (handle_proxy_header(net)) { + case ABORT: + /* error happened, message is already written. */ + len= packet_error; + goto end; + case RETRY: + goto retry; + case IGNORE: + break; + } + DBUG_PRINT("error", ("Packets out of order (Found: %d, expected %u)", (int) net->buff[net->where_b + 3], @@ -1171,6 +1246,7 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen) len+= total_length; net->where_b = save_pos; } + net->read_pos = net->buff + net->where_b; if (len != packet_error) { diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index 1dde5228263..b21cbb33c64 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "sql_select.h" #include "sql_test.h" diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3294b8f11f9..25a125dce1f 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -108,7 +108,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "key.h" // is_key_used, key_copy, key_cmp, key_restore #include "sql_parse.h" // check_stack_overrun @@ -1236,7 +1236,8 @@ QUICK_SELECT_I::QUICK_SELECT_I() QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc, bool *create_error) - :free_file(0),cur_range(NULL),last_range(0),dont_free(0) + :thd(thd), no_alloc(no_alloc), parent_alloc(parent_alloc), + free_file(0),cur_range(NULL),last_range(0),dont_free(0) { my_bitmap_map *bitmap; DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT"); @@ -6274,7 +6275,7 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info, DBUG_ENTER("ror_intersect_add"); DBUG_PRINT("info", ("Current out_rows= %g", info->out_rows)); DBUG_PRINT("info", ("Adding scan on %s", - info->param->table->key_info[ror_scan->keynr].name)); + info->param->table->key_info[ror_scan->keynr].name.str)); DBUG_PRINT("info", ("is_cpk_scan: %d",is_cpk_scan)); selectivity_mult = ror_scan_selectivity(info, ror_scan); @@ -6657,7 +6658,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, total_cost += (*ror_scan_mark)->index_read_cost; records += (*ror_scan_mark)->records; DBUG_PRINT("info", ("Adding scan on %s", - param->table->key_info[(*ror_scan_mark)->keynr].name)); + param->table->key_info[(*ror_scan_mark)->keynr].name.str)); if (total_cost > read_time) DBUG_RETURN(NULL); /* F=F-covered by first(I) */ @@ -6819,7 +6820,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, read_plan->mrr_buf_size= best_buf_size; DBUG_PRINT("info", ("Returning range plan for key %s, cost %g, records %lu", - param->table->key_info[param->real_keynr[best_idx]].name, + param->table->key_info[param->real_keynr[best_idx]].name.str, read_plan->read_cost, (ulong) read_plan->records)); } } @@ -10952,7 +10953,7 @@ int read_keys_and_merge_scans(THD *thd, if (unique == NULL) { - DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_ABORT(); ); + DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_SUICIDE(); ); DBUG_EXECUTE_IF("only_one_Unique_may_be_created", DBUG_SET("+d,index_merge_may_not_create_a_Unique"); ); @@ -11066,7 +11067,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() if (doing_pk_scan) DBUG_RETURN(pk_quick_select->get_next()); - if ((result= read_record.read_record(&read_record)) == -1) + if ((result= read_record.read_record()) == -1) { result= HA_ERR_END_OF_FILE; end_read_record(&read_record); @@ -11102,7 +11103,7 @@ int QUICK_INDEX_INTERSECT_SELECT::get_next() int result; DBUG_ENTER("QUICK_INDEX_INTERSECT_SELECT::get_next"); - if ((result= read_record.read_record(&read_record)) == -1) + if ((result= read_record.read_record()) == -1) { result= HA_ERR_END_OF_FILE; end_read_record(&read_record); @@ -11857,7 +11858,7 @@ void QUICK_SELECT_I::add_key_name(String *str, bool *first) *first= FALSE; else str->append(','); - str->append(key_info->name); + str->append(&key_info->name); } @@ -12012,7 +12013,7 @@ void QUICK_SELECT_I::add_key_and_length(String *key_names, key_names->append(','); used_lengths->append(','); } - key_names->append(key_info->name); + key_names->append(&key_info->name); length= longlong10_to_str(max_used_key_length, buf, 10) - buf; used_lengths->append(buf, length); } @@ -14655,7 +14656,7 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, uint keynr= param->real_keynr[idx]; if (tmp.length()) tmp.append(','); - tmp.append(param->table->key_info[keynr].name); + tmp.append(¶m->table->key_info[keynr].name); } } if (!tmp.length()) @@ -14681,7 +14682,7 @@ static void print_ror_scans_arr(TABLE *table, const char *msg, { if (tmp.length()) tmp.append(','); - tmp.append(table->key_info[(*start)->keynr].name); + tmp.append(&table->key_info[(*start)->keynr].name); } if (!tmp.length()) tmp.append(STRING_WITH_LEN("(empty)")); @@ -14763,7 +14764,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose) { /* purecov: begin inspected */ fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n", - indent, "", head->key_info[index].name, max_used_key_length); + indent, "", head->key_info[index].name.str, max_used_key_length); if (verbose) { @@ -14867,7 +14868,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) { fprintf(DBUG_FILE, "%*squick_group_min_max_select: index %s (%d), length: %d\n", - indent, "", index_info->name, index, max_used_key_length); + indent, "", index_info->name.str, index, max_used_key_length); if (key_infix_len > 0) { fprintf(DBUG_FILE, "%*susing key_infix with length %d:\n", diff --git a/sql/opt_range.h b/sql/opt_range.h index 95e231433d2..a436b0e684d 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -1047,6 +1047,10 @@ bool quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range); class QUICK_RANGE_SELECT : public QUICK_SELECT_I { protected: + THD *thd; + bool no_alloc; + MEM_ROOT *parent_alloc; + /* true if we enabled key only reads */ handler *file; @@ -1085,6 +1089,9 @@ public: QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc, MEM_ROOT *parent_alloc, bool *create_err); ~QUICK_RANGE_SELECT(); + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { return new QUICK_RANGE_SELECT(thd, head, index, no_alloc, parent_alloc, + create_error); } void need_sorted_output(); int init(); @@ -1155,6 +1162,12 @@ public: :QUICK_RANGE_SELECT(thd, table, index_arg, no_alloc, parent_alloc, create_err) {}; + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { + DBUG_ASSERT(0); + return new QUICK_RANGE_SELECT_GEOM(thd, head, index, no_alloc, + parent_alloc, create_error); + } virtual int get_next(); }; @@ -1584,6 +1597,8 @@ class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT { public: QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts); + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { DBUG_ASSERT(0); return new QUICK_SELECT_DESC(this, used_key_parts); } int get_next(); bool reverse_sorted() { return 1; } int get_type() { return QS_TYPE_RANGE_DESC; } @@ -1658,6 +1673,8 @@ public: QUICK_RANGE_SELECT (thd, table, key, 1, NULL, create_err) { (void) init(); } ~FT_SELECT() { file->ft_end(); } + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { DBUG_ASSERT(0); return new FT_SELECT(thd, head, index, create_error); } int init() { return file->ft_init(); } int reset() { return 0; } int get_next() { return file->ha_ft_read(record); } diff --git a/sql/opt_split.cc b/sql/opt_split.cc new file mode 100644 index 00000000000..ac2972f4264 --- /dev/null +++ b/sql/opt_split.cc @@ -0,0 +1,1134 @@ +/* + Copyright (c) 2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This file contains functions to support the splitting technique. + This optimization technique can be applied to equi-joins involving + materialized tables such as materialized views, materialized derived tables + and materialized CTEs. The technique also could be applied to materialized + semi-joins though the code below does not support this usage yet. + + Here are the main ideas behind this technique that we'll call SM optimization + (SplitMaterialization). + + Consider the query + SELECT t1.a, t.min + FROM t1, (SELECT t2.a, MIN(t2.b) as min FROM t2 GROUP BY t2.a) t + WHERE t1.a = t.a and t1.b < const + + Re-write the query into + SELECT t1.a, t.min + FROM t1, LATERAL (SELECT t2.a, MIN(t2.b) as min + FROM t2 WHERE t2.a = t1.a GROUP BY t2.a) t + WHERE t1.b < const + + The execution of the original query (Q1) does the following: + 1. Executes the query in the specification of the derived table + and puts the result set into a temporary table with an index + on the first column. + 2. Joins t1 with the temporary table using the its index. + + The execution of the transformed query (Q1R) follows these steps: + 1. For each row of t1 where t1.b < const a temporary table + containing all rows of of t2 with t2.a = t1.a is created + 2. If there are any rows in the temporary table aggregation + is performed for them + 3. The result of the aggregation is joined with t1. + + The second execution can win if: + a) There is an efficient way to select rows of t2 for which t2.a = t1.a + (For example if there is an index on t2.a) + and + b) The number of temporary tables created for partitions + is much smaller that the total number of partitions + + It should be noted that for the transformed query aggregation + for a partition may be performed several times. + + As we can see the optimization basically splits table t2 into + partitions and performs aggregation over each of them + independently. + + If we have only one equi-join condition then we either push it as + for Q1R or we don't. In a general case we may have much more options. + Consider the query (Q3) + SELECT + FROM t1,t2 (SELECT t3.a, t3.b, MIN(t3.c) as min + FROM t3 GROUP BY a,b) t + WHERE t.a = t1.a AND t.b = t2.b + AND t1.c < c1 and t2.c < c2 + AND P(t1,t2); + (P(t1,t2) designates some additional conditions over columns of t1,t2). + + Assuming that there indexes on t3(a,b) and t3(b) here we have several + reasonable options to push equi-join conditions into the derived. + All these options should be taken into account when the optimizer + evaluates different join orders. When the join order (t1,t,t2) is + evaluated there is only one way of splitting : to push the condition + t.a = t1.a into t. With the join order (t2,t,t1) only the condition + t.b = t2.b can be pushed. When the join orders (t1,t2,t) and (t2,t1,t) + are evaluated then the optimizer should consider pushing t.a = t1.a, + t.b = t2.b and (t.a = t1.a AND t.b = t2.b) to choose the best condition + for splitting. Apparently here last condition is the best one because + it provides the miximum possible number of partitions. + + If we dropped the index on t3(a,b) and created the index on t3(a) instead + then we would have two options for splitting: to push t.a = t1.a or to + push t.b = t2.b. If the selectivity of the index t3(a) is better than + the selectivity of t3(b) then the first option is preferred. + + Although the condition (t.a = t1.a AND t.b = t2.b) provides a better + splitting than the condition t.a = t1.a the latter will be used for + splitting if the execution plan with the join order (t1,t,t2) turns out + to be the cheapest one. It's quite possible when the join condition + P(t1,t2) has a bad selectivity. + + Whenever the optimizer evaluates the cost of using a splitting it + compares it with the cost of materialization without splitting. + + If we just drop the index on t3(a,b) the chances that the splitting + will be used becomes much lower but they still exists providing that + the fanout of the partial join of t1 and t2 is small enough. +*/ + +/* + Splitting can be applied to a materialized table specified by the query + with post-join operations that require partitioning of the result set produced + by the join expression used in the FROM clause the query such as GROUP BY + operation and window function operation. In any of these cases the post-join + operation can be executed independently for any partition only over the rows + of this partition. Also if the set of all partitions is divided into disjoint + subsets the operation can applied to each subset independently. In this case + all rows are first partitioned into the groups each of which contains all the + rows from the partitions belonging the same subset and then each group + is subpartitioned into groups in the the post join operation. + + The set of all rows belonging to the union of several partitions is called + here superpartition. If a grouping operation is defined by the list + e_1,...,e_n then any set S = {e_i1,...,e_ik} can be used to devide all rows + into superpartions such that for any two rows r1, r2 the following holds: + e_ij(r1) = e_ij(r2) for each e_ij from S. We use the splitting technique + only if S consists of references to colums of the joined tables. + For example if the GROUP BY list looks like this a, g(b), c we can consider + applying the splitting technique to the superpartitions defined by {a,c}, + {a}, {c} (a and c here may be the references to the columns from different + tables). +*/ + + /* + The following describes when and how the optimizer decides whether it + makes sense to employ the splitting technique. + + 1. For each instance of a materialized table (derived/view/CTE) it is + checked that it is potentially splittable. Now it is done right after the + execution plan for the select specifying this table has been chosen. + + 2. Any potentially splittable materialized table T is subject to two-phase + optimization. It means that the optimizer first builds the best execution + plan for join that specifies T. Then the control is passed back to the + optimization process of the embedding select Q. After the execution plan + for Q has been chosen the optimizer finishes the optimization of the join + specifying T. + + 3. When the optimizer builds the container with the KEYUSE structures + for the join of embedding select it detects the equi-join conditions + PC that potentially could be pushed into a potentially splittable + materialized table T. The collected information about such conditions + is stored together with other facts on potential splittings for table T. + + 4. When the optimizer starts looking for the best execution plan for the + embedding select Q for each potentially splittable materialized table T + it creates special KEYUSE structures for pushable equi-join conditions + PC. These structures are used to add new elements to the container + of KEYUSE structures built for T. The specifics of these elements is + that they can be ebabled and disabled during the process of choosing + the best plan for Q. + + 5. When the optimizer extends a partial join order with a potentially + splittable materialized table T (in function best_access_path) it + first evaluates a new execution plan for the modified specification + of T that adds all equi-join conditions that can be pushed with + current join prefix to the WHERE conditions of the original + specification of T. If the cost of the new plan is better than the + the cost of the original materialized table then the optimizer + prefers to use splitting for the current join prefix. As the cost + of the plan depends only on the pushed conditions it makes sense + to cache this plan for other prefixes. + + 6. The optimizer takes into account the cost of splitting / materialization + of a potentially splittable materialized table T as a startup cost + to access table T. + + 7. When the optimizer finally chooses the best execution plan for + the embedding select Q and this plan prefers using splitting + for table T with pushed equi-join conditions PC then the execution + plan for the underlying join with these conditions is chosen for T. +*/ + +/* + The implementation of the splitting technique below allows to apply + the technique only to a materialized derived table / view / CTE whose + specification is either a select with GROUP BY or a non-grouping select + with window functions that share the same PARTITION BY list. +*/ + +#include "mariadb.h" +#include "sql_select.h" + +/* Info on a splitting field */ +struct SplM_field_info +{ + /* Splitting field in the materialized table T */ + Field *mat_field; + /* The item from the select list of the specification of T */ + Item *producing_item; + /* The corresponding splitting field from the specification of T */ + Field *underlying_field; +}; + + +/* Info on the splitting execution plan saved in SplM_opt_info::cache */ +struct SplM_plan_info +{ + /* The cached splitting execution plan P */ + struct st_position *best_positions; + /* The cost of the above plan */ + double cost; + /* Selectivity of splitting used in P */ + double split_sel; + /* For fast search of KEYUSE_EXT elements used for splitting in P */ + struct KEYUSE_EXT *keyuse_ext_start; + /* The tables that contains the fields used for splitting in P */ + TABLE *table; + /* The number of the key from 'table' used for splitting in P */ + uint key; + /* Number of the components of 'key' used for splitting in P */ + uint parts; +}; + + +/* + The structure contains the information that is used by the optimizer + for potentially splittable materialization of T that is a materialized + derived_table / view / CTE +*/ +class SplM_opt_info : public Sql_alloc +{ +public: + /* The join for the select specifying T */ + JOIN *join; + /* The map of tables from 'join' whose columns can be used for partitioning */ + table_map tables_usable_for_splitting; + /* Info about the fields of the joined tables usable for splitting */ + SplM_field_info *spl_fields; + /* The number of elements in the above list */ + uint spl_field_cnt; + /* Contains the structures to generate all KEYUSEs for pushable equalities */ + List<KEY_FIELD> added_key_fields; + /* The cache of evaluated execution plans for 'join' with pushed equalities */ + List<SplM_plan_info> plan_cache; + /* Cost of best execution plan for join when nothing is pushed */ + double unsplit_cost; + /* Cardinality of T when nothing is pushed */ + double unsplit_card; + /* Lastly evaluated execution plan for 'join' with pushed equalities */ + SplM_plan_info *last_plan; + + SplM_plan_info *find_plan(TABLE *table, uint key, uint parts); +}; + + +void TABLE::set_spl_opt_info(SplM_opt_info *spl_info) +{ + if (spl_info) + spl_info->join->spl_opt_info= spl_info; + spl_opt_info= spl_info; +} + + +void TABLE::deny_splitting() +{ + DBUG_ASSERT(spl_opt_info != NULL); + spl_opt_info->join->spl_opt_info= NULL; + spl_opt_info= NULL; +} + + +/* This structure is auxiliary and used only in the function that follows it */ +struct SplM_field_ext_info: public SplM_field_info +{ + uint item_no; + bool is_usable_for_ref_access; +}; + + +/** + @brief + Check whether this join is one for potentially splittable materialized table + + @details + The function checks whether this join is for select that specifies + a potentially splittable materialized table T. If so, the collected + info on potential splittability of T is attached to the field spl_opt_info + of the TABLE structure for T. + + The function returns a positive answer if the following holds: + 1. the optimizer switch 'split_materialized' is set 'on' + 2. the select owning this join specifies a materialized derived/view/cte T + 3. this is the only select in the specification of T + 4. condition pushdown is not prohibited into T + 5. T is not recursive + 6. not all of this join are constant or optimized away + 7. T is either + 7.1. a grouping table with GROUP BY list P + or + 7.2. a non-grouping table with window functions over the same non-empty + partition specified by the PARTITION BY list P + 8. P contains some references on the columns of the joined tables C + occurred also in the select list of this join + 9. There are defined some keys usable for ref access of fields from C + with available statistics. + + @retval + true if the answer is positive + false otherwise +*/ + +bool JOIN::check_for_splittable_materialized() +{ + ORDER *partition_list= 0; + st_select_lex_unit *unit= select_lex->master_unit(); + TABLE_LIST *derived= unit->derived; + if (!(optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)) || // !(1) + !(derived && derived->is_materialized_derived()) || // !(2) + (unit->first_select()->next_select()) || // !(3) + (derived->prohibit_cond_pushdown) || // !(4) + (derived->is_recursive_with_table()) || // !(5) + (table_count == 0 || const_tables == top_join_tab_count)) // !(6) + return false; + if (group_list) // (7.1) + { + if (!select_lex->have_window_funcs()) + partition_list= group_list; + } + else if (select_lex->have_window_funcs() && + select_lex->window_specs.elements == 1) // (7.2) + { + partition_list= + select_lex->window_specs.head()->partition_list->first; + } + if (!partition_list) + return false; + + ORDER *ord; + Dynamic_array<SplM_field_ext_info> candidates; + + /* + Select from partition_list all candidates for splitting. + A candidate must be + - field item or refer to such (8.1) + - item mentioned in the select list (8.2) + Put info about such candidates into the array candidates + */ + table_map usable_tables= 0; // tables that contains the candidate + for (ord= partition_list; ord; ord= ord->next) + { + Item *ord_item= *ord->item; + if (ord_item->real_item()->type() != Item::FIELD_ITEM) // !(8.1) + continue; + + Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; + + JOIN_TAB *tab= ord_field->table->reginfo.join_tab; + if (tab->is_inner_table_of_outer_join()) + continue; + + List_iterator<Item> li(fields_list); + Item *item; + uint item_no= 0; + while ((item= li++)) + { + if ((*ord->item)->eq(item, 0)) // (8.2) + { + SplM_field_ext_info new_elem; + new_elem.producing_item= item; + new_elem.item_no= item_no; + new_elem.mat_field= derived->table->field[item_no]; + new_elem.underlying_field= ord_field; + new_elem.is_usable_for_ref_access= false; + candidates.push(new_elem); + usable_tables|= ord_field->table->map; + break; + } + item_no++; + } + } + if (candidates.elements() == 0) // no candidates satisfying (8.1) && (8.2) + return false; + + /* + For each table from this join find the keys that can be used for ref access + of the fields mentioned in the 'array candidates' + */ + + SplM_field_ext_info *cand; + SplM_field_ext_info *cand_start= &candidates.at(0); + SplM_field_ext_info *cand_end= cand_start + candidates.elements(); + + for (JOIN_TAB *tab= join_tab; + tab < join_tab + top_join_tab_count; tab++) + { + TABLE *table= tab->table; + if (!(table->map & usable_tables)) + continue; + + table->keys_usable_for_splitting.clear_all(); + uint i; + for (i= 0; i < table->s->keys; i++) + { + if (!table->keys_in_use_for_query.is_set(i)) + continue; + KEY *key_info= table->key_info + i; + uint key_parts= table->actual_n_key_parts(key_info); + uint usable_kp_cnt= 0; + for ( ; usable_kp_cnt < key_parts; usable_kp_cnt++) + { + if (key_info->actual_rec_per_key(usable_kp_cnt) == 0) + break; + int fldnr= key_info->key_part[usable_kp_cnt].fieldnr; + + for (cand= cand_start; cand < cand_end; cand++) + { + if (cand->underlying_field->field_index + 1 == fldnr) + { + cand->is_usable_for_ref_access= true; + break; + } + } + if (cand == cand_end) + break; + } + if (usable_kp_cnt) + table->keys_usable_for_splitting.set_bit(i); + } + } + + /* Count the candidate fields that can be accessed by ref */ + uint spl_field_cnt= candidates.elements(); + for (cand= cand_start; cand < cand_end; cand++) + { + if (!cand->is_usable_for_ref_access) + spl_field_cnt--; + } + + if (!spl_field_cnt) // No candidate field can be accessed by ref => !(9) + return false; + + /* + Create a structure of the type SplM_opt_info and fill it with + the collected info on potential splittability of T + */ + SplM_opt_info *spl_opt_info= new (thd->mem_root) SplM_opt_info(); + SplM_field_info *spl_field= + (SplM_field_info *) (thd->calloc(sizeof(SplM_field_info) * + spl_field_cnt)); + + if (!(spl_opt_info && spl_field)) // consider T as not good for splitting + return false; + + spl_opt_info->join= this; + spl_opt_info->tables_usable_for_splitting= 0; + spl_opt_info->spl_field_cnt= spl_field_cnt; + spl_opt_info->spl_fields= spl_field; + for (cand= cand_start; cand < cand_end; cand++) + { + if (!cand->is_usable_for_ref_access) + continue; + spl_field->producing_item= cand->producing_item; + spl_field->underlying_field= cand->underlying_field; + spl_field->mat_field= cand->mat_field; + spl_opt_info->tables_usable_for_splitting|= + cand->underlying_field->table->map; + spl_field++; + } + + /* Attach this info to the table T */ + derived->table->set_spl_opt_info(spl_opt_info); + + return true; +} + + +/** + @brief + Collect info on KEY_FIELD usable for splitting + + @param + key_field KEY_FIELD to collect info on + + @details + The function assumes that this table is potentially splittable. + The function checks whether the KEY_FIELD structure key_field built for + this table was created for a splitting field f. If so, the function does + the following using info from key_field: + 1. Builds an equality of the form f = key_field->val that could be + pushed into this table. + 2. Creates a new KEY_FIELD structure for this equality and stores + a reference to this structure in this->spl_opt_info. +*/ + +void TABLE::add_splitting_info_for_key_field(KEY_FIELD *key_field) +{ + DBUG_ASSERT(spl_opt_info != NULL); + JOIN *join= spl_opt_info->join; + Field *field= key_field->field; + SplM_field_info *spl_field= spl_opt_info->spl_fields; + uint i= spl_opt_info->spl_field_cnt; + for ( ; i; i--, spl_field++) + { + if (spl_field->mat_field == field) + break; + } + if (!i) // field is not usable for splitting + return; + + /* + Any equality condition that can be potentially pushed into the + materialized derived table is constructed now though later it may turn out + that it is not needed, because it is not used for splitting. + The reason for this is that the failure to construct it when it has to be + injected causes denial for further processing of the query. + Formally this equality is needed in the KEY_FIELD structure constructed + here that will be used to generate additional keyuses usable for splitting. + However key_field.cond could be used for this purpose (see implementations + of virtual function can_optimize_keypart_ref()). + + The condition is built in such a form that it can be added to the WHERE + condition of the select that specifies this table. + */ + THD *thd= in_use; + Item *left_item= spl_field->producing_item->build_clone(thd); + Item *right_item= key_field->val->build_clone(thd); + Item_func_eq *eq_item= 0; + if (left_item && right_item) + { + right_item->walk(&Item::set_fields_as_dependent_processor, + false, join->select_lex); + right_item->update_used_tables(); + eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); + } + if (!eq_item) + return; + KEY_FIELD *added_key_field= + (KEY_FIELD *) thd->alloc(sizeof(KEY_FIELD)); + if (!added_key_field || + spl_opt_info->added_key_fields.push_back(added_key_field,thd->mem_root)) + return; + added_key_field->field= spl_field->underlying_field; + added_key_field->cond= eq_item; + added_key_field->val= key_field->val; + added_key_field->level= 0; + added_key_field->optimize= KEY_OPTIMIZE_EQ; + added_key_field->eq_func= true; + added_key_field->null_rejecting= true; + added_key_field->cond_guard= NULL; + added_key_field->sj_pred_no= UINT_MAX; + return; +} + + +static bool +add_ext_keyuse_for_splitting(Dynamic_array<KEYUSE_EXT> *ext_keyuses, + KEY_FIELD *added_key_field, uint key, uint part) +{ + KEYUSE_EXT keyuse_ext; + Field *field= added_key_field->field; + + JOIN_TAB *tab=field->table->reginfo.join_tab; + key_map possible_keys=field->get_possible_keys(); + possible_keys.intersect(field->table->keys_usable_for_splitting); + tab->keys.merge(possible_keys); + + Item_func_eq *eq_item= (Item_func_eq *) (added_key_field->cond); + keyuse_ext.table= field->table; + keyuse_ext.val= eq_item->arguments()[1]; + keyuse_ext.key= key; + keyuse_ext.keypart=part; + keyuse_ext.keypart_map= (key_part_map) 1 << part; + keyuse_ext.used_tables= keyuse_ext.val->used_tables(); + keyuse_ext.optimize= added_key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; + keyuse_ext.ref_table_rows= 0; + keyuse_ext.null_rejecting= added_key_field->null_rejecting; + keyuse_ext.cond_guard= added_key_field->cond_guard; + keyuse_ext.sj_pred_no= added_key_field->sj_pred_no; + keyuse_ext.validity_ref= 0; + keyuse_ext.needed_in_prefix= added_key_field->val->used_tables(); + keyuse_ext.validity_var= false; + return ext_keyuses->push(keyuse_ext); +} + + +static int +sort_ext_keyuse(KEYUSE_EXT *a, KEYUSE_EXT *b) +{ + if (a->table->tablenr != b->table->tablenr) + return (int) (a->table->tablenr - b->table->tablenr); + if (a->key != b->key) + return (int) (a->key - b->key); + return (int) (a->keypart - b->keypart); +} + + +static void +sort_ext_keyuses(Dynamic_array<KEYUSE_EXT> *keyuses) +{ + KEYUSE_EXT *first_keyuse= &keyuses->at(0); + my_qsort(first_keyuse, keyuses->elements(), sizeof(KEYUSE_EXT), + (qsort_cmp) sort_ext_keyuse); +} + + +/** + @brief + Add info on keyuses usable for splitting into an array +*/ + +static bool +add_ext_keyuses_for_splitting_field(Dynamic_array<KEYUSE_EXT> *ext_keyuses, + KEY_FIELD *added_key_field) +{ + Field *field= added_key_field->field; + TABLE *table= field->table; + for (uint key= 0; key < table->s->keys; key++) + { + if (!(table->keys_usable_for_splitting.is_set(key))) + continue; + KEY *key_info= table->key_info + key; + uint key_parts= table->actual_n_key_parts(key_info); + KEY_PART_INFO *key_part_info= key_info->key_part; + for (uint part=0; part < key_parts; part++, key_part_info++) + { + if (!field->eq(key_part_info->field)) + continue; + if (add_ext_keyuse_for_splitting(ext_keyuses, added_key_field, key, part)) + return true; + } + } + return false; +} + + +/* + @brief + Cost of the post join operation used in specification of splittable table +*/ + +static +double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len) +{ + double cost; + cost= get_tmp_table_write_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to fill tmp table + cost+= get_tmp_table_lookup_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to perform post join operation used here + cost+= get_tmp_table_lookup_cost(thd, join_record_count, rec_len) + + join_record_count * log2 (join_record_count) * + SORT_INDEX_CMP_COST; // cost to perform sorting + return cost; +} + +/** + @brief + Add KEYUSE structures that can be usable for splitting + + @details + This function is called only for joins created for potentially + splittable materialized tables. The function does the following: + 1. Creates the dynamic array ext_keyuses_for_splitting of KEYUSE_EXT + structures and fills is with info about all keyuses that + could be used for splitting. + 2. Sort the array ext_keyuses_for_splitting for fast access by key + on certain columns. + 3. Collects and stores cost and cardinality info on the best execution + plan that does not use splitting and save this plan together with + corresponding array of keyuses. + 4. Expand this array with KEYUSE elements built from the info stored + in ext_keyuses_for_splitting that could be produced by pushed + equalities employed for splitting. + 5. Prepare the extended array of keyuses to be used in the function + best_access_plan() +*/ + +void JOIN::add_keyuses_for_splitting() +{ + uint i; + uint idx; + KEYUSE_EXT *keyuse_ext; + KEYUSE_EXT keyuse_ext_end; + double oper_cost; + uint rec_len; + uint added_keyuse_count; + TABLE *table= select_lex->master_unit()->derived->table; + List_iterator_fast<KEY_FIELD> li(spl_opt_info->added_key_fields); + KEY_FIELD *added_key_field; + if (!spl_opt_info->added_key_fields.elements) + goto err; + if (!(ext_keyuses_for_splitting= new Dynamic_array<KEYUSE_EXT>)) + goto err; + while ((added_key_field= li++)) + { + (void) add_ext_keyuses_for_splitting_field(ext_keyuses_for_splitting, + added_key_field); + } + added_keyuse_count= ext_keyuses_for_splitting->elements(); + if (!added_keyuse_count) + goto err; + sort_ext_keyuses(ext_keyuses_for_splitting); + bzero((char*) &keyuse_ext_end, sizeof(keyuse_ext_end)); + if (ext_keyuses_for_splitting->push(keyuse_ext_end)) + goto err; + + spl_opt_info->unsplit_card= join_record_count; + + rec_len= table->s->rec_buff_length; + + oper_cost= spl_postjoin_oper_cost(thd, join_record_count, rec_len); + + spl_opt_info->unsplit_cost= best_positions[table_count-1].read_time + + oper_cost; + + if (!(save_qep= new Join_plan_state(table_count + 1))) + goto err; + + save_query_plan(save_qep); + + if (!keyuse.buffer && + my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64, + MYF(MY_THREAD_SPECIFIC))) + goto err; + + if (allocate_dynamic(&keyuse, + save_qep->keyuse.elements + + added_keyuse_count)) + goto err; + + memcpy(keyuse.buffer, + save_qep->keyuse.buffer, + (size_t) save_qep->keyuse.elements * keyuse.size_of_element); + keyuse.elements= save_qep->keyuse.elements; + + keyuse_ext= &ext_keyuses_for_splitting->at(0); + idx= save_qep->keyuse.elements; + for (i=0; i < added_keyuse_count; i++, keyuse_ext++, idx++) + { + set_dynamic(&keyuse, (KEYUSE *) keyuse_ext, idx); + KEYUSE *added_keyuse= ((KEYUSE *) (keyuse.buffer)) + idx; + added_keyuse->validity_ref= &keyuse_ext->validity_var; + } + + if (sort_and_filter_keyuse(thd, &keyuse, true)) + goto err; + optimize_keyuse(this, &keyuse); + + for (uint i= 0; i < table_count; i++) + { + JOIN_TAB *tab= join_tab + i; + map2table[tab->table->tablenr]= tab; + } + + return; + +err: + if (save_qep) + restore_query_plan(save_qep); + table->deny_splitting(); + return; +} + + +/** + @brief + Add KEYUSE structures that can be usable for splitting of this joined table +*/ + +void JOIN_TAB::add_keyuses_for_splitting() +{ + DBUG_ASSERT(table->spl_opt_info != NULL); + SplM_opt_info *spl_opt_info= table->spl_opt_info; + spl_opt_info->join->add_keyuses_for_splitting(); +} + + +/* + @brief + Find info on the splitting plan by the splitting key +*/ + +SplM_plan_info *SplM_opt_info::find_plan(TABLE *table, uint key, uint parts) +{ + List_iterator_fast<SplM_plan_info> li(plan_cache); + SplM_plan_info *spl_plan; + while ((spl_plan= li++)) + { + if (spl_plan->table == table && + spl_plan->key == key && + spl_plan->parts == parts) + break; + } + return spl_plan; +} + + +/* + @breaf + Enable/Disable a keyuses that can be used for splitting + */ + +static +void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start, + TABLE *table, uint key, + table_map remaining_tables, + bool validity_val) +{ + KEYUSE_EXT *keyuse_ext= key_keyuse_ext_start; + do + { + if (!(keyuse_ext->needed_in_prefix & remaining_tables)) + { + /* + The enabling/disabling flags are set just in KEYUSE_EXT structures. + Yet keyuses that are used by best_access_path() have pointers + to these flags. + */ + keyuse_ext->validity_var= validity_val; + } + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); +} + + +/** + @brief + Choose the best splitting to extend the evaluated partial join + + @param + record_count estimated cardinality of the extended partial join + remaining_tables tables not joined yet + + @details + This function is called during the search for the best execution + plan of the join that contains this table T. The function is called + every time when the optimizer tries to extend a partial join by + joining it with table T. Depending on what tables are already in the + partial join different equalities usable for splitting can be pushed + into T. The function evaluates different variants and chooses the + best one. Then the function finds the plan for the materializing join + with the chosen equality conditions pushed into it. If the cost of the + plan turns out to be less than the cost of the best plan without + splitting the function set it as the true plan of materialization + of the table T. + The function caches the found plans for materialization of table T + together if the info what key was used for splitting. Next time when + the optimizer prefers to use the same key the plan is taken from + the cache of plans + + @retval + Pointer to the info on the found plan that employs the pushed equalities + if the plan has been chosen, NULL - otherwise. +*/ + +SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count, + table_map remaining_tables) +{ + SplM_opt_info *spl_opt_info= table->spl_opt_info; + DBUG_ASSERT(spl_opt_info != NULL); + JOIN *join= spl_opt_info->join; + THD *thd= join->thd; + table_map tables_usable_for_splitting= + spl_opt_info->tables_usable_for_splitting; + KEYUSE_EXT *keyuse_ext= &join->ext_keyuses_for_splitting->at(0); + KEYUSE_EXT *best_key_keyuse_ext_start; + TABLE *best_table= 0; + double best_rec_per_key= DBL_MAX; + SplM_plan_info *spl_plan= 0; + uint best_key; + uint best_key_parts; + + /* + Check whether there are keys that can be used to join T employing splitting + and if so, select the best out of such keys + */ + for (uint tablenr= 0; tablenr < join->table_count; tablenr++) + { + if (!((1ULL << tablenr) & tables_usable_for_splitting)) + continue; + JOIN_TAB *tab= join->map2table[tablenr]; + TABLE *table= tab->table; + do + { + uint key= keyuse_ext->key; + KEYUSE_EXT *key_keyuse_ext_start= keyuse_ext; + key_part_map found_parts= 0; + do + { + if (keyuse_ext->needed_in_prefix & remaining_tables) + { + keyuse_ext++; + continue; + } + if (!(keyuse_ext->keypart_map & found_parts)) + { + if ((!found_parts && !keyuse_ext->keypart) || + (found_parts && ((keyuse_ext->keypart_map >> 1) & found_parts))) + found_parts|= keyuse_ext->keypart_map; + else + { + do + { + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); + break; + } + } + KEY *key_info= table->key_info + key; + double rec_per_key= + key_info->actual_rec_per_key(keyuse_ext->keypart); + if (rec_per_key < best_rec_per_key) + { + best_table= keyuse_ext->table; + best_key= keyuse_ext->key; + best_key_parts= keyuse_ext->keypart + 1; + best_rec_per_key= rec_per_key; + best_key_keyuse_ext_start= key_keyuse_ext_start; + } + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); + } + while (keyuse_ext->table == table); + } + spl_opt_info->last_plan= 0; + if (best_table) + { + /* + The key for splitting was chosen, look for the plan for this key + in the cache + */ + spl_plan= spl_opt_info->find_plan(best_table, best_key, best_key_parts); + if (!spl_plan && + (spl_plan= (SplM_plan_info *) thd->alloc(sizeof(SplM_plan_info))) && + (spl_plan->best_positions= + (POSITION *) thd->alloc(sizeof(POSITION) * join->table_count)) && + !spl_opt_info->plan_cache.push_back(spl_plan)) + { + /* + The plan for the chosen key has not been found in the cache. + Build a new plan and save info on it in the cache + */ + table_map all_table_map= (1 << join->table_count) - 1; + reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table, + best_key, remaining_tables, true); + choose_plan(join, all_table_map & ~join->const_table_map); + spl_plan->keyuse_ext_start= best_key_keyuse_ext_start; + spl_plan->table= best_table; + spl_plan->key= best_key; + spl_plan->parts= best_key_parts; + spl_plan->split_sel= best_rec_per_key / spl_opt_info->unsplit_card; + + uint rec_len= table->s->rec_buff_length; + + double split_card= spl_opt_info->unsplit_card * spl_plan->split_sel; + double oper_cost= split_card * + spl_postjoin_oper_cost(thd, split_card, rec_len); + spl_plan->cost= join->best_positions[join->table_count-1].read_time + + + oper_cost; + + memcpy((char *) spl_plan->best_positions, + (char *) join->best_positions, + sizeof(POSITION) * join->table_count); + reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table, + best_key, remaining_tables, false); + } + if (spl_plan) + { + if(record_count * spl_plan->cost < spl_opt_info->unsplit_cost) + { + /* + The best plan that employs splitting is cheaper than + the plan without splitting + */ + spl_opt_info->last_plan= spl_plan; + } + } + } + + /* Set the cost of the preferred materialization for this partial join */ + records= (ha_rows)spl_opt_info->unsplit_card; + spl_plan= spl_opt_info->last_plan; + if (spl_plan) + { + startup_cost= record_count * spl_plan->cost; + records= (ha_rows) (records * spl_plan->split_sel); + } + else + startup_cost= spl_opt_info->unsplit_cost; + return spl_plan; +} + + +/** + @brief + Inject equalities for splitting used by the materialization join + + @param + remaining_tables used to filter out the equalities that cannot + be pushed. + + @details + This function is called by JOIN_TAB::fix_splitting that is used + to fix the chosen splitting of a splittable materialized table T + in the final query execution plan. In this plan the table T + is joined just before the 'remaining_tables'. So all equalities + usable for splitting whose right parts do not depend on any of + remaining tables can be pushed into join for T. + The function also marks the select that specifies T as + UNCACHEABLE_DEPENDENT_INJECTED. + + @retval + false on success + true on failure +*/ + +bool JOIN::inject_best_splitting_cond(table_map remaining_tables) +{ + Item *inj_cond= 0; + List<Item> inj_cond_list; + List_iterator<KEY_FIELD> li(spl_opt_info->added_key_fields); + KEY_FIELD *added_key_field; + while ((added_key_field= li++)) + { + if (remaining_tables & added_key_field->val->used_tables()) + continue; + if (inj_cond_list.push_back(added_key_field->cond, thd->mem_root)) + return true; + } + DBUG_ASSERT(inj_cond_list.elements); + switch (inj_cond_list.elements) { + case 1: + inj_cond= inj_cond_list.head(); break; + default: + inj_cond= new (thd->mem_root) Item_cond_and(thd, inj_cond_list); + if (!inj_cond) + return true; + } + if (inj_cond) + inj_cond->fix_fields(thd,0); + + if (inject_cond_into_where(inj_cond)) + return true; + + select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + st_select_lex_unit *unit= select_lex->master_unit(); + unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + + return false; +} + + +/** + @brief + Fix the splitting chosen for a splittable table in the final query plan + + @param + spl_plan info on the splitting plan chosen for the splittable table T + remaining_tables the table T is joined just before these tables + + @details + If in the final query plan the optimizer has chosen a splitting plan + then the function sets this plan as the final execution plan to + materialized the table T. Otherwise the plan that does not use + splitting is set for the materialization. + + @retval + false on success + true on failure +*/ + +bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan, + table_map remaining_tables) +{ + SplM_opt_info *spl_opt_info= table->spl_opt_info; + DBUG_ASSERT(table->spl_opt_info != 0); + JOIN *md_join= spl_opt_info->join; + if (spl_plan) + { + memcpy((char *) md_join->best_positions, + (char *) spl_plan->best_positions, + sizeof(POSITION) * md_join->table_count); + if (md_join->inject_best_splitting_cond(remaining_tables)) + return true; + /* + This is called for a proper work of JOIN::get_best_combination() + called for the join that materializes T + */ + reset_validity_vars_for_keyuses(spl_plan->keyuse_ext_start, + spl_plan->table, + spl_plan->key, + remaining_tables, + true); + } + else + { + md_join->restore_query_plan(md_join->save_qep); + } + return false; +} + + +/** + @brief + Fix the splittings chosen splittable tables in the final query plan + + @details + The function calls JOIN_TAB::fix_splittins for all potentially + splittable tables in this join to set all final materialization + plans chosen for these tables. + + @retval + false on success + true on failure +*/ + +bool JOIN::fix_all_splittings_in_plan() +{ + table_map prev_tables= 0; + table_map all_tables= (1 << table_count) - 1; + for (uint tablenr=0 ; tablenr < table_count ; tablenr++) + { + POSITION *cur_pos= &best_positions[tablenr]; + JOIN_TAB *tab= cur_pos->table; + if (tab->table->is_splittable()) + { + SplM_plan_info *spl_plan= cur_pos->spl_plan; + if (tab->fix_splitting(spl_plan, all_tables & ~prev_tables)) + return true; + } + prev_tables|= tab->table->map; + } + return false; +} diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 75296e76da9..9d6b67e845e 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" #include "sql_select.h" #include "filesort.h" @@ -432,9 +432,9 @@ referred to, we can only generate equalities that refer to the outer (or inner) tables. Note that this will disallow handling of cases like (CASE-FOR-SUBST). Currently, solution #2 is implemented. - */ +LEX_CSTRING weedout_key= {STRING_WITH_LEN("weedout_key")}; static bool subquery_types_allow_materialization(Item_in_subselect *in_subs); @@ -446,10 +446,6 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, Item_in_subselect *subq_pred, bool *remove); static TABLE_LIST *alloc_join_nest(THD *thd); static uint get_tmp_table_rec_length(Ref_ptr_array p_list, uint elements); -static double get_tmp_table_lookup_cost(THD *thd, double row_count, - uint row_size); -static double get_tmp_table_write_cost(THD *thd, double row_count, - uint row_size); bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables); static SJ_MATERIALIZATION_INFO * at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, @@ -461,7 +457,7 @@ void best_access_path(JOIN *join, JOIN_TAB *s, static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm, Item_in_subselect *subq_pred); -static void remove_sj_conds(THD *thd, Item **tree); +static bool remove_sj_conds(THD *thd, Item **tree); static bool is_cond_sj_in_equality(Item *item); static bool sj_table_is_included(JOIN *join, JOIN_TAB *join_tab); static Item *remove_additional_cond(Item* conds); @@ -1065,6 +1061,8 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) DBUG_RETURN(1); if (subq_sel->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(TRUE); + if (subq_sel->join->transform_in_predicates_into_in_subq(thd)) + DBUG_RETURN(TRUE); subq_sel->update_used_tables(); } @@ -1748,9 +1746,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) */ Item_row *row= new (thd->mem_root) Item_row(thd, subq_lex->pre_fix); /* fix fields on subquery was call so they should be the same */ - DBUG_ASSERT(subq_pred->left_expr->cols() == row->cols()); if (!row) DBUG_RETURN(TRUE); + DBUG_ASSERT(subq_pred->left_expr->cols() == row->cols()); nested_join->sj_outer_expr_list.push_back(&subq_pred->left_expr); Item_func_eq *item_eq= new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr_orig, row); @@ -1837,7 +1835,8 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) } parent_lex->have_merged_subqueries= TRUE; - DBUG_RETURN(FALSE); + /* Fatal error may have been set to by fix_after_pullout() */ + DBUG_RETURN(thd->is_fatal_error); } @@ -1878,6 +1877,7 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, bool optimization_delayed= TRUE; TABLE_LIST *jtbm; char *tbl_alias; + THD *thd= parent_join->thd; DBUG_ENTER("convert_subq_to_jtbm"); subq_pred->set_strategy(SUBS_MATERIALIZATION); @@ -1885,8 +1885,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, *remove_item= TRUE; - if (!(tbl_alias= (char*)parent_join->thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) || - !(jtbm= alloc_join_nest(parent_join->thd))) //todo: this is not a join nest! + if (!(tbl_alias= (char*)thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) || + !(jtbm= alloc_join_nest(thd))) //todo: this is not a join nest! { DBUG_RETURN(TRUE); } @@ -1898,13 +1898,13 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, /* Nests do not participate in those 'chains', so: */ /* jtbm->next_leaf= jtbm->next_local= jtbm->next_global == NULL*/ - emb_join_list->push_back(jtbm, parent_join->thd->mem_root); + emb_join_list->push_back(jtbm, thd->mem_root); /* Inject the jtbm table into TABLE_LIST::next_leaf list, so that make_join_statistics() and co. can find it. */ - parent_lex->leaf_tables.push_back(jtbm, parent_join->thd->mem_root); + parent_lex->leaf_tables.push_back(jtbm, thd->mem_root); if (subq_pred->unit->first_select()->options & OPTION_SCHEMA_TABLE) parent_lex->options |= OPTION_SCHEMA_TABLE; @@ -1929,7 +1929,7 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, subq_pred->unit->first_select()->select_number); jtbm->alias= tbl_alias; parent_join->table_count++; - DBUG_RETURN(FALSE); + DBUG_RETURN(thd->is_fatal_error); } subselect_hash_sj_engine *hash_sj_engine= ((subselect_hash_sj_engine*)subq_pred->engine); @@ -1952,27 +1952,10 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, jtbm->alias= tbl_alias; parent_lex->have_merged_subqueries= TRUE; -#if 0 - /* Inject sj_on_expr into the parent's WHERE or ON */ - if (emb_tbl_nest) - { - DBUG_ASSERT(0); - /*emb_tbl_nest->on_expr= and_items(emb_tbl_nest->on_expr, - sj_nest->sj_on_expr); - emb_tbl_nest->on_expr->fix_fields(parent_join->thd, &emb_tbl_nest->on_expr); - */ - } - else - { - /* Inject into the WHERE */ - parent_join->conds= and_items(parent_join->conds, conds); - parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds); - parent_join->select_lex->where= parent_join->conds; - } -#endif + /* Don't unlink the child subselect, as the subquery will be used. */ - DBUG_RETURN(FALSE); + DBUG_RETURN(thd->is_fatal_error); } @@ -1987,6 +1970,9 @@ static TABLE_LIST *alloc_join_nest(THD *thd) return tbl; } +/* + @Note thd->is_fatal_error can be set in case of OOM +*/ void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist) { @@ -2478,7 +2464,7 @@ static uint get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements) @return the cost of one lookup */ -static double +double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size) { if (row_count * row_size > thd->variables.max_heap_table_size) @@ -2498,7 +2484,7 @@ get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size) @return the cost of writing one row */ -static double +double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size) { double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size); @@ -3748,6 +3734,11 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) DBUG_RETURN(FALSE); } +/** + @retval + FALSE ok + TRUE error +*/ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) { @@ -3760,8 +3751,6 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; uint i; - //List<Item> &item_list= emb_sj_nest->sj_subq_pred->unit->first_select()->item_list; - //List_iterator<Item> it(item_list); if (!sjm->is_sj_scan) { @@ -3811,6 +3800,8 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) null_count ? cur_ref_buff : 0, cur_key_part->length, tab_ref->items[i], FALSE); + if (!*ref_key) + DBUG_RETURN(TRUE); cur_ref_buff+= cur_key_part->store_length; } *ref_key= NULL; /* End marker. */ @@ -3836,9 +3827,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) */ for (i= 0; i < sjm->tables; i++) { - remove_sj_conds(thd, &tab[i].select_cond); - if (tab[i].select) - remove_sj_conds(thd, &tab[i].select->cond); + if (remove_sj_conds(thd, &tab[i].select_cond) || + (tab[i].select && remove_sj_conds(thd, &tab[i].select->cond))) + DBUG_RETURN(TRUE); } if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm, emb_sj_nest->sj_subq_pred))) @@ -3875,7 +3866,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) temptable record, we copy its columns to their corresponding columns in the record buffers for the source tables. */ - sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements]; + if (!(sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements])) + DBUG_RETURN(TRUE); + //it.rewind(); Ref_ptr_array p_items= emb_sj_nest->sj_subq_pred->unit->first_select()->ref_pointer_array; for (uint i=0; i < sjm->sjm_table_cols.elements; i++) @@ -3943,7 +3936,7 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) sjm_tab->read_record.copy_field= sjm->copy_field; sjm_tab->read_record.copy_field_end= sjm->copy_field + sjm->sjm_table_cols.elements; - sjm_tab->read_record.read_record= rr_sequential_and_unpack; + sjm_tab->read_record.read_record_func= rr_sequential_and_unpack; } sjm_tab->bush_children->end[-1].next_select= end_sj_materialize; @@ -4002,16 +3995,20 @@ static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm, } +/** + @retval + 0 ok + 1 error +*/ - -static void remove_sj_conds(THD *thd, Item **tree) +static bool remove_sj_conds(THD *thd, Item **tree) { if (*tree) { if (is_cond_sj_in_equality(*tree)) { *tree= NULL; - return; + return 0; } else if ((*tree)->type() == Item::COND_ITEM) { @@ -4020,12 +4017,19 @@ static void remove_sj_conds(THD *thd, Item **tree) while ((item= li++)) { if (is_cond_sj_in_equality(item)) - li.replace(new (thd->mem_root) Item_int(thd, 1)); + { + Item_int *tmp= new (thd->mem_root) Item_int(thd, 1); + if (!tmp) + return 1; + li.replace(tmp); + } } } } + return 0; } + /* Check if given Item was injected by semi-join equality */ static bool is_cond_sj_in_equality(Item *item) { @@ -4213,7 +4217,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) share->db_plugin= ha_lock_engine(0, heap_hton); table->file= get_new_handler(share, &table->mem_root, share->db_type()); - DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length()); + DBUG_ASSERT(!table->file || uniq_tuple_length_arg <= table->file->max_key_length()); } if (!table->file) goto err; @@ -4313,7 +4317,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) keyinfo->key_length=0; keyinfo->rec_per_key=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; - keyinfo->name= (char*) "weedout_key"; + keyinfo->name= weedout_key; { key_part_info->null_bit=0; key_part_info->field= field; @@ -5332,6 +5336,9 @@ TABLE *create_dummy_tmp_table(THD *thd) sjm_table_param.field_count= 1; List<Item> sjm_table_cols; Item *column_item= new (thd->mem_root) Item_int(thd, 1); + if (!column_item) + DBUG_RETURN(NULL); + sjm_table_cols.push_back(column_item, thd->mem_root); if (!(table= create_tmp_table(thd, &sjm_table_param, sjm_table_cols, (ORDER*) 0, diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 7954becfad4..9cb19e0cc6c 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -298,7 +298,7 @@ public: pos->table= tab; // todo need ref_depend_map ? DBUG_PRINT("info", ("Produced a LooseScan plan, key %s, %s", - tab->table->key_info[best_loose_scan_key].name, + tab->table->key_info[best_loose_scan_key].name.str, best_loose_scan_start_key? "(ref access)": "(range/index access)")); } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 8a75aaed8d6..43d1c2de7ad 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -48,7 +48,7 @@ (assuming a index for column d of table t2 is defined) */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "key.h" // key_cmp_if_same #include "sql_select.h" @@ -398,6 +398,8 @@ int opt_sum_query(THD *thd, const_result= 0; break; } + longlong info_limit= 1; + table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit); if (!(error= table->file->ha_index_init((uint) ref.key, 1))) error= (is_max ? get_index_max_value(table, &ref, range_fl) : @@ -410,6 +412,7 @@ int opt_sum_query(THD *thd, error= HA_ERR_KEY_NOT_FOUND; table->file->ha_end_keyread(); table->file->ha_index_end(); + table->file->info_push(INFO_KIND_FORCE_LIMIT_END, NULL); if (error) { if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index 191360a6969..ef9b07cca47 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -28,6 +28,7 @@ #pragma implementation // gcc: Class implementation #endif +#include "mariadb.h" #include "my_bit.h" #include "sql_select.h" @@ -1809,6 +1810,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl) { DBUG_PRINT("info", ("Eliminated table %s", table->alias.c_ptr())); tab->type= JT_CONST; + tab->table->const_table= 1; join->eliminated_tables |= table->map; join->const_table_map|= table->map; set_position(join, join->const_tables++, tab, (KEYUSE*)0); diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 94f72b7b492..aedb7a4f0af 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -20,13 +20,12 @@ Text .frm files management routines */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "parse_file.h" #include "unireg.h" // CREATE_MODE #include "sql_table.h" // build_table_filename #include <m_ctype.h> -#include <my_sys.h> #include <my_dir.h> /* from sql_db.cc */ diff --git a/sql/parse_file.h b/sql/parse_file.h index 3f48b2072db..19c7883f8cc 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -17,9 +17,8 @@ #ifndef _PARSE_FILE_H_ #define _PARSE_FILE_H_ -#include "my_global.h" // uchar #include "sql_string.h" // LEX_STRING -#include "sql_list.h" // Sql_alloc +#include "sql_alloc.h" // Sql_alloc class THD; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index d721453a181..c47c7fc8de9 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -20,7 +20,7 @@ #pragma implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" // Required to get server definitions for mysql/plugin.h right #include "sql_plugin.h" diff --git a/sql/password.c b/sql/password.c index 1f0a55a10fe..47f4fd1d422 100644 --- a/sql/password.c +++ b/sql/password.c @@ -60,7 +60,7 @@ *****************************************************************************/ -#include <my_global.h> +#include "mariadb.h" #include <my_sys.h> #include <m_string.h> #include <password.h> diff --git a/sql/procedure.cc b/sql/procedure.cc index 3d36b7adfa3..e4bf589958e 100644 --- a/sql/procedure.cc +++ b/sql/procedure.cc @@ -20,7 +20,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "procedure.h" #include "sql_analyse.h" // Includes procedure diff --git a/sql/procedure.h b/sql/procedure.h index a1c9b95f20b..fdbfab7f17c 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -59,7 +59,7 @@ public: DBUG_ASSERT(0); // impossible return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE); } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* get_copy(THD *thd) { return 0; } }; class Item_proc_real :public Item_proc diff --git a/sql/protocol.cc b/sql/protocol.cc index d2f91d51910..fae399e66e2 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -25,7 +25,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "protocol.h" #include "sql_class.h" // THD @@ -1235,7 +1235,7 @@ bool Protocol_text::store(Field *field) char buff[MAX_FIELD_WIDTH]; String str(buff,sizeof(buff), &my_charset_bin); CHARSET_INFO *tocs= this->thd->variables.character_set_results; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS TABLE *table= field->table; my_bitmap_map *old_map= 0; if (table->file) @@ -1243,7 +1243,7 @@ bool Protocol_text::store(Field *field) #endif field->val_str(&str); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (old_map) dbug_tmp_restore_column_map(table->read_set, old_map); #endif diff --git a/sql/proxy_protocol.cc b/sql/proxy_protocol.cc new file mode 100644 index 00000000000..0309cf42ddf --- /dev/null +++ b/sql/proxy_protocol.cc @@ -0,0 +1,491 @@ +/* Copyright (c) 2017, 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +#include <my_global.h> +#include <mysql.h> +#include <mysql_com.h> +#include <mysqld_error.h> +#include <my_sys.h> +#include <m_string.h> +#include <my_net.h> +#include <violite.h> +#include <proxy_protocol.h> +#include <log.h> + +#define PROXY_PROTOCOL_V1_SIGNATURE "PROXY" +#define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" +#define MAX_PROXY_HEADER_LEN 256 + +/* + Parse proxy protocol version 1 header (text) +*/ +static int parse_v1_header(char *hdr, size_t len, proxy_peer_info *peer_info) +{ + char address_family[MAX_PROXY_HEADER_LEN + 1]; + char client_address[MAX_PROXY_HEADER_LEN + 1]; + char server_address[MAX_PROXY_HEADER_LEN + 1]; + int client_port; + int server_port; + + int ret = sscanf(hdr, "PROXY %s %s %s %d %d", + address_family, client_address, server_address, + &client_port, &server_port); + + if (ret != 5) + { + if (ret >= 1 && !strcmp(address_family, "UNKNOWN")) + { + peer_info->is_local_command= true; + return 0; + } + return -1; + } + + if (client_port < 0 || client_port > 0xffff + || server_port < 0 || server_port > 0xffff) + return -1; + + if (!strcmp(address_family, "UNKNOWN")) + { + peer_info->is_local_command= true; + return 0; + } + else if (!strcmp(address_family, "TCP4")) + { + /* Initialize IPv4 peer address.*/ + peer_info->peer_addr.ss_family= AF_INET; + if (!inet_pton(AF_INET, client_address, + &((struct sockaddr_in *)(&peer_info->peer_addr))->sin_addr)) + return -1; + } + else if (!strcmp(address_family, "TCP6")) + { + /* Initialize IPv6 peer address.*/ + peer_info->peer_addr.ss_family= AF_INET6; + if (!inet_pton(AF_INET6, client_address, + &((struct sockaddr_in6 *)(&peer_info->peer_addr))->sin6_addr)) + return -1; + } + peer_info->port= client_port; + /* Check if server address is legal.*/ + char addr_bin[16]; + if (!inet_pton(peer_info->peer_addr.ss_family, + server_address, addr_bin)) + return -1; + + return 0; +} + + +/* + Parse proxy protocol V2 (binary) header +*/ +static int parse_v2_header(uchar *hdr, size_t len,proxy_peer_info *peer_info) +{ + /* V2 Signature */ + if (memcmp(hdr, PROXY_PROTOCOL_V2_SIGNATURE, 12)) + return -1; + + /* version + command */ + uint8 ver= (hdr[12] & 0xF0); + if (ver != 0x20) + return -1; /* Wrong version*/ + + uint cmd= (hdr[12] & 0xF); + + /* Address family */ + uchar fam= hdr[13]; + + if (cmd == 0) + { + /* LOCAL command*/ + peer_info->is_local_command= true; + return 0; + } + + if (cmd != 0x01) + { + /* Not PROXY COMMAND */ + return -1; + } + + struct sockaddr_in *sin= (struct sockaddr_in *)(&peer_info->peer_addr); + struct sockaddr_in6 *sin6= (struct sockaddr_in6 *)(&peer_info->peer_addr); + switch (fam) + { + case 0x11: /* TCPv4 */ + sin->sin_family= AF_INET; + memcpy(&(sin->sin_addr), hdr + 16, 4); + peer_info->port= (hdr[24] << 8) + hdr[25]; + break; + case 0x21: /* TCPv6 */ + sin6->sin6_family= AF_INET6; + memcpy(&(sin6->sin6_addr), hdr + 16, 16); + peer_info->port= (hdr[48] << 8) + hdr[49]; + break; + case 0x31: /* AF_UNIX, stream */ + peer_info->peer_addr.ss_family= AF_UNIX; + break; + default: + return -1; + } + return 0; +} + + +bool has_proxy_protocol_header(NET *net) +{ + compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V1_SIGNATURE)); + compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V2_SIGNATURE)); + + const uchar *preread_bytes= net->buff + net->where_b; + return !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE)|| + !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE); +} + + +/** + Try to parse proxy header. + https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt + + Whenever this function is called, client is connecting, and + we have have pre-read 4 bytes (NET_HEADER_SIZE) from the network already. + These 4 bytes did not match MySQL packet header, and (unless the client + is buggy), those bytes must be proxy header. + + @param[in] net - vio and already preread bytes from the header + @param[out] peer_info - parsed proxy header with client host and port + @return 0 in case of success, -1 if error. +*/ +int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info) +{ + uchar hdr[MAX_PROXY_HEADER_LEN]; + size_t pos= 0; + + DBUG_ASSERT(!net->compress); + const uchar *preread_bytes= net->buff + net->where_b; + bool have_v1_header= !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE); + bool have_v2_header= + !have_v1_header && !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE); + if (!have_v1_header && !have_v2_header) + { + // not a proxy protocol header + return -1; + } + memcpy(hdr, preread_bytes, NET_HEADER_SIZE); + pos= NET_HEADER_SIZE; + Vio *vio= net->vio; + memset(peer_info, 0, sizeof (*peer_info)); + + if (have_v1_header) + { + /* Read until end of header (newline character)*/ + while(pos < sizeof(hdr)) + { + long len= (long)vio_read(vio, hdr + pos, 1); + if (len < 0) + return -1; + pos++; + if (hdr[pos-1] == '\n') + break; + } + hdr[pos]= 0; + + if (parse_v1_header((char *)hdr, pos, peer_info)) + return -1; + } + else // if (have_v2_header) + { +#define PROXY_V2_HEADER_LEN 16 + /* read off 16 bytes of the header.*/ + long len= vio_read(vio, hdr + pos, PROXY_V2_HEADER_LEN - pos); + if (len < 0) + return -1; + // 2 last bytes are the length in network byte order of the part following header + ushort trail_len= ((ushort)hdr[PROXY_V2_HEADER_LEN-2] >> 8) + hdr[PROXY_V2_HEADER_LEN-1]; + if (trail_len > sizeof(hdr) - PROXY_V2_HEADER_LEN) + return -1; + len= vio_read(vio, hdr + PROXY_V2_HEADER_LEN, trail_len); + pos= PROXY_V2_HEADER_LEN + trail_len; + if (parse_v2_header(hdr, pos, peer_info)) + return -1; + } + + if (peer_info->peer_addr.ss_family == AF_INET6) + { + /* + Normalize IPv4 compatible or mapped IPv6 addresses. + They will be treated as IPv4. + */ + sockaddr_storage tmp; + int dst_len; + memset(&tmp, 0, sizeof(tmp)); + vio_get_normalized_ip((const struct sockaddr *)&peer_info->peer_addr, + sizeof(sockaddr_storage), (struct sockaddr *)&tmp, &dst_len); + memcpy(&peer_info->peer_addr, &tmp, (size_t)dst_len); + } + return 0; +} + + +/** + CIDR address matching etc (for the proxy_protocol_networks parameter) +*/ + +/** + Subnetwork address in CIDR format, e.g + 192.168.1.0/24 or 2001:db8::/32 +*/ +struct subnet +{ + char addr[16]; /* Binary representation of the address, big endian*/ + unsigned short family; /* Address family, AF_INET or AF_INET6 */ + unsigned short bits; /* subnetwork size */ +}; + +static subnet* proxy_protocol_subnets; +size_t proxy_protocol_subnet_count; + +#define MAX_MASK_BITS(family) (family == AF_INET ? 32 : 128) + + +/** Convert IPv4 that are compat or mapped IPv4 to "normal" IPv4 */ +static int normalize_subnet(struct subnet *subnet) +{ + unsigned char *addr= (unsigned char*)subnet->addr; + if (subnet->family == AF_INET6) + { + const struct in6_addr *src_ip6=(in6_addr *)addr; + if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6)) + { + /* Copy the actual IPv4 address (4 last bytes) */ + if (subnet->bits < 96) + return -1; + subnet->family= AF_INET; + memcpy(addr, addr+12, 4); + subnet->bits -= 96; + } + } + return 0; +} + +/** + Convert string representation of a subnet to subnet struct. +*/ +static int parse_subnet(char *addr_str, struct subnet *subnet) +{ + if (strchr(addr_str, ':')) + subnet->family= AF_INET6; + else if (strchr(addr_str, '.')) + subnet->family= AF_INET; + else if (!strcmp(addr_str, "localhost")) + { + subnet->family= AF_UNIX; + subnet->bits= 0; + return 0; + } + + char *pmask= strchr(addr_str, '/'); + if (!pmask) + { + subnet->bits= MAX_MASK_BITS(subnet->family); + } + else + { + *pmask= 0; + pmask++; + int b= 0; + + do + { + if (*pmask < '0' || *pmask > '9') + return -1; + b= 10 * b + *pmask - '0'; + if (b > MAX_MASK_BITS(subnet->family)) + return -1; + pmask++; + } + while (*pmask); + + subnet->bits= (unsigned short)b; + } + + if (!inet_pton(subnet->family, addr_str, subnet->addr)) + return -1; + + if (normalize_subnet(subnet)) + return -1; + + return 0; +} + +/** + Parse comma separated string subnet list into subnets array, + which is stored in 'proxy_protocol_subnets' variable + + @param[in] subnets_str : networks in CIDR format, + separated by comma and/or space + + @return 0 if success, otherwise -1 +*/ +int set_proxy_protocol_networks(const char *subnets_str) +{ + if (!subnets_str || !*subnets_str) + return 0; + + size_t max_subnets= MY_MAX(3,strlen(subnets_str)/2); + proxy_protocol_subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL); + + /* Check for special case '*'. */ + if (strcmp(subnets_str, "*") == 0) + { + + proxy_protocol_subnets[0].family= AF_INET; + proxy_protocol_subnets[1].family= AF_INET6; + proxy_protocol_subnets[2].family= AF_UNIX; + proxy_protocol_subnet_count= 3; + return 0; + } + + char token[256]; + const char *p= subnets_str; + for(proxy_protocol_subnet_count= 0;; proxy_protocol_subnet_count++) + { + while(*p && (*p ==',' || *p == ' ')) + p++; + if (!*p) + break; + + size_t cnt= 0; + while(*p && *p != ',' && *p != ' ' && cnt < sizeof(token)-1) + token[cnt++]= *p++; + + token[cnt++]=0; + if (cnt == sizeof(token)) + return -1; + + if (parse_subnet(token, &proxy_protocol_subnets[proxy_protocol_subnet_count])) + { + sql_print_error("Error parsing proxy_protocol_networks parameter, near '%s'",token); + return -1; + } + } + return 0; +} + +/** + Compare memory areas, in memcmp().similar fashion. + The difference to memcmp() is that size parameter is the + bit count, not byte count. +*/ +static int compare_bits(const void *s1, const void *s2, int bit_count) +{ + int result= 0; + int byte_count= bit_count / 8; + if (byte_count && (result= memcmp(s1, s2, byte_count))) + return result; + int rem= byte_count % 8; + if (rem) + { + // compare remaining bits i.e partial bytes. + unsigned char s1_bits= (((char *)s1)[byte_count]) >> (8 - rem); + unsigned char s2_bits= (((char *)s2)[byte_count]) >> (8 - rem); + if (s1_bits > s2_bits) + return 1; + if (s1_bits < s2_bits) + return -1; + } + return 0; +} + +/** + Check whether networks address matches network. +*/ +bool addr_matches_subnet(const sockaddr *sock_addr, const subnet *subnet) +{ + DBUG_ASSERT(subnet->family == AF_UNIX || + subnet->family == AF_INET || + subnet->family == AF_INET6); + + if (sock_addr->sa_family != subnet->family) + return false; + + if (subnet->family == AF_UNIX) + return true; + + void *addr= (subnet->family == AF_INET) ? + (void *)&((struct sockaddr_in *)sock_addr)->sin_addr : + (void *)&((struct sockaddr_in6 *)sock_addr)->sin6_addr; + + return (compare_bits(subnet->addr, addr, subnet->bits) == 0); +} + + +/** + Check whether proxy header from client is allowed, as per + specification in 'proxy_protocol_networks' server variable. + + The non-TCP "localhost" clients (unix socket, shared memory, pipes) + are accepted whenever 127.0.0.1 accepted in 'proxy_protocol_networks' +*/ +bool is_proxy_protocol_allowed(const sockaddr *addr) +{ + if (proxy_protocol_subnet_count == 0) + return false; + + sockaddr_storage addr_storage; + struct sockaddr *normalized_addr= (struct sockaddr *)&addr_storage; + + /* + Non-TCP addresses (unix domain socket, windows pipe and shared memory + gets tranlated to TCP4 localhost address. + + Note, that vio remote addresses are initialized with binary zeros + for these protocols (which is AF_UNSPEC everywhere). + */ + switch(addr->sa_family) + { + case AF_UNSPEC: + case AF_UNIX: + normalized_addr->sa_family= AF_UNIX; + break; + case AF_INET: + case AF_INET6: + { + int len= + (addr->sa_family == AF_INET)?sizeof(sockaddr_in):sizeof (sockaddr_in6); + int dst_len; + vio_get_normalized_ip(addr, len,normalized_addr, &dst_len); + } + break; + default: + DBUG_ASSERT(0); + } + + for (size_t i= 0; i < proxy_protocol_subnet_count; i++) + if (addr_matches_subnet(normalized_addr, &proxy_protocol_subnets[i])) + return true; + + return false; +} + + +void cleanup_proxy_protocol_networks() +{ + my_free(proxy_protocol_subnets); + proxy_protocol_subnets= 0; + proxy_protocol_subnet_count= 0; +} + diff --git a/sql/proxy_protocol.h b/sql/proxy_protocol.h new file mode 100644 index 00000000000..6f9bcf73307 --- /dev/null +++ b/sql/proxy_protocol.h @@ -0,0 +1,15 @@ +#include "my_net.h" + +struct proxy_peer_info +{ + struct sockaddr_storage peer_addr; + int port; + bool is_local_command; +}; + +extern bool has_proxy_protocol_header(NET *net); +extern int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info); +extern bool is_proxy_protocol_allowed(const sockaddr *remote_addr); + +extern int set_proxy_protocol_networks(const char *spec); +extern void cleanup_proxy_protocol_networks(); diff --git a/sql/records.cc b/sql/records.cc index f16bdcff6e6..650a51f7f37 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -26,7 +26,7 @@ Functions for easy reading of records, possible through a cache */ -#include <my_global.h> +#include "mariadb.h" #include "records.h" #include "sql_priv.h" #include "records.h" @@ -89,8 +89,8 @@ bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, table->file->print_error(error, MYF(0)); } - /* read_record will be changed to rr_index in rr_index_first */ - info->read_record= reverse ? rr_index_last : rr_index_first; + /* read_record_func will be changed to rr_index in rr_index_first */ + info->read_record_func= reverse ? rr_index_last : rr_index_first; DBUG_RETURN(error != 0); } @@ -229,8 +229,8 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (tempfile && !(select && select->quick)) { DBUG_PRINT("info",("using rr_from_tempfile")); - info->read_record= (addon_field ? - rr_unpack_from_tempfile : rr_from_tempfile); + info->read_record_func= + addon_field ? rr_unpack_from_tempfile : rr_from_tempfile; info->io_cache= tempfile; reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0); info->ref_pos=table->file->ref; @@ -260,14 +260,14 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (! init_rr_cache(thd, info)) { DBUG_PRINT("info",("using rr_from_cache")); - info->read_record=rr_from_cache; + info->read_record_func= rr_from_cache; } } } else if (select && select->quick) { DBUG_PRINT("info",("using rr_quick")); - info->read_record=rr_quick; + info->read_record_func= rr_quick; } else if (filesort && filesort->record_pointers) { @@ -277,13 +277,13 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, info->cache_pos= filesort->record_pointers; info->cache_end= (info->cache_pos+ filesort->return_rows * info->ref_length); - info->read_record= (addon_field ? - rr_unpack_from_buffer : rr_from_pointers); + info->read_record_func= + addon_field ? rr_unpack_from_buffer : rr_from_pointers; } else if (table->file->keyread_enabled()) { int error; - info->read_record= rr_index_first; + info->read_record_func= rr_index_first; if (!table->file->inited && (error= table->file->ha_index_init(table->file->keyread, 1))) { @@ -295,7 +295,7 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, else { DBUG_PRINT("info",("using rr_sequential")); - info->read_record=rr_sequential; + info->read_record_func= rr_sequential; if (table->file->ha_rnd_init_with_error(1)) DBUG_RETURN(1); /* We can use record cache if we don't update dynamic length tables */ @@ -331,7 +331,7 @@ void end_read_record(READ_RECORD *info) { if (info->table->is_created()) (void) info->table->file->extra(HA_EXTRA_NO_CACHE); - if (info->read_record != rr_quick) // otherwise quick_range does it + if (info->read_record_func != rr_quick) // otherwise quick_range does it (void) info->table->file->ha_index_or_rnd_end(); info->table=0; } @@ -399,7 +399,7 @@ static int rr_index_first(READ_RECORD *info) } tmp= info->table->file->ha_index_first(info->record); - info->read_record= rr_index; + info->read_record_func= rr_index; if (tmp) tmp= rr_handle_error(info, tmp); return tmp; @@ -422,7 +422,7 @@ static int rr_index_first(READ_RECORD *info) static int rr_index_last(READ_RECORD *info) { int tmp= info->table->file->ha_index_last(info->record); - info->read_record= rr_index_desc; + info->read_record_func= rr_index_desc; if (tmp) tmp= rr_handle_error(info, tmp); return tmp; diff --git a/sql/records.h b/sql/records.h index 473cb610be5..940c88ca0c7 100644 --- a/sql/records.h +++ b/sql/records.h @@ -55,7 +55,7 @@ struct READ_RECORD //handler *file; TABLE **forms; /* head and ref forms */ Unlock_row_func unlock_row; - Read_func read_record; + Read_func read_record_func; THD *thd; SQL_SELECT *select; uint cache_records; @@ -70,6 +70,8 @@ struct READ_RECORD bool print_error, ignore_not_found_rows; void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *); + int read_record() { return read_record_func(this); } + /* SJ-Materialization runtime may need to read fields from the materialized table and unpack them into original table fields: diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 4cf7df5227f..20290e8bad8 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -24,7 +24,7 @@ functions like register_slave()) are working. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" // check_access #ifdef HAVE_REPLICATION diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index 2cc031a462d..c3584fb17f2 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -19,7 +19,7 @@ #ifdef HAVE_REPLICATION #include "mysql.h" -#include "my_sys.h" +#include <my_sys.h> #include "slave.h" typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE, diff --git a/sql/replication.h b/sql/replication.h index 4731c2246ef..d8672310110 100644 --- a/sql/replication.h +++ b/sql/replication.h @@ -18,16 +18,14 @@ /*************************************************************************** NOTE: plugin locking. - This API was created specifically for the semisync plugin and its locking - logic is also matches semisync plugin usage pattern. In particular, a plugin - is locked on Binlog_transmit_observer::transmit_start and is unlocked after - Binlog_transmit_observer::transmit_stop. All other master observable events - happen between these two and don't lock the plugin at all. This works well - for the semisync_master plugin. + + The plugin is locked on Binlog_transmit_observer::transmit_start and is + unlocked after Binlog_transmit_observer::transmit_stop. All other + master observable events happen between these two and don't lock the + plugin at all. Also a plugin is locked on Binlog_relay_IO_observer::thread_start - and unlocked after Binlog_relay_IO_observer::thread_stop. This works well for - the semisync_slave plugin. + and unlocked after Binlog_relay_IO_observer::thread_stop. ***************************************************************************/ #include <mysql.h> diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index 0ab4c62235f..d09b7aee31c 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "mysqld.h" // system_charset_info #include "rpl_filter.h" diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index bb0f6bf2233..9f48f908102 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -16,16 +16,16 @@ /* Definitions for MariaDB global transaction ID (GTID). */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" -#include "my_sys.h" #include "unireg.h" -#include "my_global.h" +#include "mariadb.h" #include "sql_base.h" #include "sql_parse.h" #include "key.h" #include "rpl_gtid.h" #include "rpl_rli.h" +#include "slave.h" #include "log_event.h" const LEX_STRING rpl_gtid_slave_state_table_name= @@ -33,7 +33,7 @@ const LEX_STRING rpl_gtid_slave_state_table_name= void -rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, +rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton, rpl_group_info *rgi) { int err; @@ -45,7 +45,7 @@ rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, it is even committed. */ mysql_mutex_lock(&LOCK_slave_state); - err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, rgi); + err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, hton, rgi); mysql_mutex_unlock(&LOCK_slave_state); if (err) { @@ -74,12 +74,14 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi) if (rgi->gtid_pending) { uint64 sub_id= rgi->gtid_sub_id; + void *hton= NULL; + rgi->gtid_pending= false; if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE) { - if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false)) + if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false, &hton)) DBUG_RETURN(1); - update_state_hash(sub_id, &rgi->current_gtid, rgi); + update_state_hash(sub_id, &rgi->current_gtid, hton, rgi); } rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL; } @@ -243,7 +245,7 @@ rpl_slave_state_free_element(void *arg) rpl_slave_state::rpl_slave_state() - : last_sub_id(0), loaded(false) + : last_sub_id(0), gtid_pos_tables(0), loaded(false) { mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state, MY_MUTEX_INIT_SLOW); @@ -255,6 +257,7 @@ rpl_slave_state::rpl_slave_state() rpl_slave_state::~rpl_slave_state() { + free_gtid_pos_tables((struct gtid_pos_table *)gtid_pos_tables); truncate_hash(); my_hash_free(&hash); delete_dynamic(>id_sort_array); @@ -286,11 +289,12 @@ rpl_slave_state::truncate_hash() int rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, - uint64 seq_no, rpl_group_info *rgi) + uint64 seq_no, void *hton, rpl_group_info *rgi) { element *elem= NULL; list_element *list_elem= NULL; + DBUG_ASSERT(hton || !loaded); if (!(elem= get_element(domain_id))) return 1; @@ -313,7 +317,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, { if (rgi->gtid_ignore_duplicate_state==rpl_group_info::GTID_DUPLICATE_OWNER) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS Relay_log_info *rli= rgi->rli; #endif uint32 count= elem->owner_count; @@ -335,6 +339,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, list_elem->server_id= server_id; list_elem->sub_id= sub_id; list_elem->seq_no= seq_no; + list_elem->hton= hton; elem->add(list_elem); if (last_sub_id < sub_id) @@ -466,6 +471,94 @@ gtid_check_rpl_slave_state_table(TABLE *table) /* + Attempt to find a mysql.gtid_slave_posXXX table that has a storage engine + that is already in use by the current transaction, if any. +*/ +void +rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename) +{ + struct gtid_pos_table *list, *table_entry, *default_entry; + + /* + See comments on rpl_slave_state::gtid_pos_tables for rules around proper + access to the list. + */ + list= (struct gtid_pos_table *) + my_atomic_loadptr_explicit(>id_pos_tables, MY_MEMORY_ORDER_ACQUIRE); + + Ha_trx_info *ha_info; + uint count = 0; + for (ha_info= thd->transaction.all.ha_list; ha_info; ha_info= ha_info->next()) + { + void *trx_hton= ha_info->ht(); + table_entry= list; + + if (!ha_info->is_trx_read_write() || trx_hton == binlog_hton) + continue; + while (table_entry) + { + if (table_entry->table_hton == trx_hton) + { + if (likely(table_entry->state == GTID_POS_AVAILABLE)) + { + *out_tablename= table_entry->table_name; + /* + Check if this is a cross-engine transaction, so we can correctly + maintain the rpl_transactions_multi_engine status variable. + */ + if (count >= 1) + statistic_increment(rpl_transactions_multi_engine, LOCK_status); + else + { + for (;;) + { + ha_info= ha_info->next(); + if (!ha_info) + break; + if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton) + { + statistic_increment(rpl_transactions_multi_engine, LOCK_status); + break; + } + } + } + return; + } + /* + This engine is marked to automatically create the table. + We cannot easily do this here (possibly in the middle of a + transaction). But we can request the slave background thread + to create it, and in a short while it should become available + for following transactions. + */ +#ifdef HAVE_REPLICATION + slave_background_gtid_pos_create_request(table_entry); +#endif + break; + } + table_entry= table_entry->next; + } + ++count; + } + /* + If we cannot find any table whose engine matches an engine that is + already active in the transaction, or if there is no current transaction + engines available, we return the default gtid_slave_pos table. + */ + default_entry= (struct gtid_pos_table *) + my_atomic_loadptr_explicit(&default_gtid_pos_table, MY_MEMORY_ORDER_ACQUIRE); + *out_tablename= default_entry->table_name; + /* Record in status that we failed to find a suitable gtid_pos table. */ + if (count > 0) + { + statistic_increment(transactions_gtid_foreign_engine, LOCK_status); + if (count > 1) + statistic_increment(rpl_transactions_multi_engine, LOCK_status); + } +} + + +/* Write a gtid to the replication slave state table. Do it as part of the transaction, to get slave crash safety, or as a separate @@ -481,19 +574,24 @@ gtid_check_rpl_slave_state_table(TABLE *table) */ int rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, - bool in_transaction, bool in_statement) + bool in_transaction, bool in_statement, + void **out_hton) { TABLE_LIST tlist; - int err= 0; + int err= 0, not_sql_thread; bool table_opened= false; TABLE *table; - list_element *elist= 0, *next; + list_element *delete_list= 0, *next, *cur, **next_ptr_ptr, **best_ptr_ptr; + uint64_t best_sub_id; element *elem; ulonglong thd_saved_option= thd->variables.option_bits; Query_tables_list lex_backup; wait_for_commit* suspended_wfc; + void *hton= NULL; + LEX_CSTRING gtid_pos_table_name; DBUG_ENTER("record_gtid"); + *out_hton= NULL; if (unlikely(!loaded)) { /* @@ -509,6 +607,25 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, if (!in_statement) thd->reset_for_next_command(); + /* + Only the SQL thread can call select_gtid_pos_table without a mutex + Other threads needs to use a mutex and take into account that the + result may change during execution, so we have to make a copy. + */ + + if ((not_sql_thread= (thd->system_thread != SYSTEM_THREAD_SLAVE_SQL))) + mysql_mutex_lock(&LOCK_slave_state); + select_gtid_pos_table(thd, >id_pos_table_name); + if (not_sql_thread) + { + LEX_CSTRING *tmp= thd->make_clex_string(gtid_pos_table_name.str, + gtid_pos_table_name.length); + mysql_mutex_unlock(&LOCK_slave_state); + if (!tmp) + DBUG_RETURN(1); + gtid_pos_table_name= *tmp; + } + DBUG_EXECUTE_IF("gtid_inject_record_gtid", { my_error(ER_CANNOT_UPDATE_GTID_STATE, MYF(0)); @@ -538,14 +655,13 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, */ suspended_wfc= thd->suspend_subsequent_commits(); thd->lex->reset_n_backup_query_tables_list(&lex_backup); - tlist.init_one_table(STRING_WITH_LEN("mysql"), - rpl_gtid_slave_state_table_name.str, - rpl_gtid_slave_state_table_name.length, - NULL, TL_WRITE); + tlist.init_one_table(STRING_WITH_LEN("mysql"), gtid_pos_table_name.str, + gtid_pos_table_name.length, NULL, TL_WRITE); if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0))) goto end; table_opened= true; table= tlist.table; + hton= table->s->db_type(); if ((err= gtid_check_rpl_slave_state_table(table))) goto end; @@ -581,6 +697,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, table->file->print_error(err, MYF(0)); goto end; } + *out_hton= hton; if(opt_bin_log && (err= mysql_bin_log.bump_seq_no_counter_if_needed(gtid->domain_id, @@ -598,36 +715,62 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, err= 1; goto end; } - if ((elist= elem->grab_list()) != NULL) + + /* Now pull out all GTIDs that were recorded in this engine. */ + delete_list = NULL; + next_ptr_ptr= &elem->list; + cur= elem->list; + best_sub_id= 0; + best_ptr_ptr= NULL; + while (cur) { - /* Delete any old stuff, but keep around the most recent one. */ - list_element *cur= elist; - uint64 best_sub_id= cur->sub_id; - list_element **best_ptr_ptr= &elist; - while ((next= cur->next)) + list_element *next= cur->next; + if (cur->hton == hton) { - if (next->sub_id > best_sub_id) + /* Belongs to same engine, so move it to the delete list. */ + cur->next= delete_list; + delete_list= cur; + if (cur->sub_id > best_sub_id) { - best_sub_id= next->sub_id; + best_sub_id= cur->sub_id; + best_ptr_ptr= &delete_list; + } + else if (best_ptr_ptr == &delete_list) best_ptr_ptr= &cur->next; + } + else + { + /* Another engine, leave it in the list. */ + if (cur->sub_id > best_sub_id) + { + best_sub_id= cur->sub_id; + /* Current best is not on the delete list. */ + best_ptr_ptr= NULL; } - cur= next; + *next_ptr_ptr= cur; + next_ptr_ptr= &cur->next; } - /* - Delete the highest sub_id element from the old list, and put it back as - the single-element new list. - */ + cur= next; + } + *next_ptr_ptr= NULL; + /* + If the highest sub_id element is on the delete list, put it back on the + original list, to preserve the highest sub_id element in the table for + GTID position recovery. + */ + if (best_ptr_ptr) + { cur= *best_ptr_ptr; *best_ptr_ptr= cur->next; - cur->next= NULL; + cur->next= elem->list; elem->list= cur; } mysql_mutex_unlock(&LOCK_slave_state); - if (!elist) + if (!delete_list) goto end; - /* Now delete any already committed rows. */ + /* Now delete any already committed GTIDs. */ bitmap_set_bit(table->read_set, table->field[0]->field_index); bitmap_set_bit(table->read_set, table->field[1]->field_index); @@ -636,7 +779,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, table->file->print_error(err, MYF(0)); goto end; } - while (elist) + while (delete_list) { uchar key_buffer[4+8]; @@ -646,9 +789,9 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, /* `break' does not work inside DBUG_EXECUTE_IF */ goto dbug_break; }); - next= elist->next; + next= delete_list->next; - table->field[1]->store(elist->sub_id, true); + table->field[1]->store(delete_list->sub_id, true); /* domain_id is already set in table->record[0] from write_row() above. */ key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false); if (table->file->ha_index_read_map(table->record[1], key_buffer, @@ -662,8 +805,8 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, not want to endlessly error on the same element in case of table corruption or such. */ - my_free(elist); - elist= next; + my_free(delete_list); + delete_list= next; if (err) break; } @@ -681,13 +824,13 @@ end: if (err || (err= ha_commit_trans(thd, FALSE))) { /* - If error, we need to put any remaining elist back into the HASH so we - can do another delete attempt later. + If error, we need to put any remaining delete_list back into the HASH + so we can do another delete attempt later. */ - if (elist) + if (delete_list) { mysql_mutex_lock(&LOCK_slave_state); - put_back_list(gtid->domain_id, elist); + put_back_list(gtid->domain_id, delete_list); mysql_mutex_unlock(&LOCK_slave_state); } @@ -1077,11 +1220,12 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len, { rpl_gtid gtid; uint64 sub_id; + void *hton= NULL; if (gtid_parser_helper(&state_from_master, end, >id) || !(sub_id= next_sub_id(gtid.domain_id)) || - record_gtid(thd, >id, sub_id, false, in_statement) || - update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, NULL)) + record_gtid(thd, >id, sub_id, false, in_statement, &hton) || + update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL)) return 1; if (state_from_master == end) break; @@ -1115,6 +1259,75 @@ rpl_slave_state::is_empty() } +void +rpl_slave_state::free_gtid_pos_tables(struct rpl_slave_state::gtid_pos_table *list) +{ + struct gtid_pos_table *cur, *next; + + cur= list; + while (cur) + { + next= cur->next; + my_free(cur); + cur= next; + } +} + + +/* + Replace the list of available mysql.gtid_slave_posXXX tables with a new list. + The caller must be holding LOCK_slave_state. Additionally, this function + must only be called while all SQL threads are stopped. +*/ +void +rpl_slave_state::set_gtid_pos_tables_list(rpl_slave_state::gtid_pos_table *new_list, + rpl_slave_state::gtid_pos_table *default_entry) +{ + gtid_pos_table *old_list; + + mysql_mutex_assert_owner(&LOCK_slave_state); + old_list= (struct gtid_pos_table *)gtid_pos_tables; + my_atomic_storeptr_explicit(>id_pos_tables, new_list, MY_MEMORY_ORDER_RELEASE); + my_atomic_storeptr_explicit(&default_gtid_pos_table, default_entry, + MY_MEMORY_ORDER_RELEASE); + free_gtid_pos_tables(old_list); +} + + +void +rpl_slave_state::add_gtid_pos_table(rpl_slave_state::gtid_pos_table *entry) +{ + mysql_mutex_assert_owner(&LOCK_slave_state); + entry->next= (struct gtid_pos_table *)gtid_pos_tables; + my_atomic_storeptr_explicit(>id_pos_tables, entry, MY_MEMORY_ORDER_RELEASE); +} + + +struct rpl_slave_state::gtid_pos_table * +rpl_slave_state::alloc_gtid_pos_table(LEX_CSTRING *table_name, void *hton, + rpl_slave_state::gtid_pos_table_state state) +{ + struct gtid_pos_table *p; + char *allocated_str; + + if (!my_multi_malloc(MYF(MY_WME), + &p, sizeof(*p), + &allocated_str, table_name->length+1, + NULL)) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*p) + table_name->length+1)); + return NULL; + } + memcpy(allocated_str, table_name->str, table_name->length+1); // Also copy '\0' + p->next = NULL; + p->table_hton= hton; + p->table_name.str= allocated_str; + p->table_name.length= table_name->length; + p->state= state; + return p; +} + + void rpl_binlog_state::init() { my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id), @@ -2046,15 +2259,14 @@ void slave_connection_state::remove(const rpl_gtid *in_gtid) { uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool err; rpl_gtid *slave_gtid= &((entry *)rec)->gtid; DBUG_ASSERT(rec /* We should never try to remove not present domain_id. */); DBUG_ASSERT(slave_gtid->server_id == in_gtid->server_id); DBUG_ASSERT(slave_gtid->seq_no == in_gtid->seq_no); + err= #endif - - IF_DBUG(err=, ) my_hash_delete(&hash, rec); DBUG_ASSERT(!err); } diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index 19ff0f3f977..d30bfa64b87 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -120,6 +120,12 @@ struct rpl_slave_state uint64 sub_id; uint64 seq_no; uint32 server_id; + /* + hton of mysql.gtid_slave_pos* table used to record this GTID. + Can be NULL if the gtid table failed to load (eg. missing + mysql.gtid_slave_pos table following an upgrade). + */ + void *hton; }; /* Elements in the HASH that hold the state for one domain_id. */ @@ -163,6 +169,26 @@ struct rpl_slave_state } }; + /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */ + enum gtid_pos_table_state { + GTID_POS_AUTO_CREATE, + GTID_POS_CREATE_REQUESTED, + GTID_POS_CREATE_IN_PROGRESS, + GTID_POS_AVAILABLE + }; + struct gtid_pos_table { + struct gtid_pos_table *next; + /* + Use a void * here, rather than handlerton *, to make explicit that we + are not using the value to access any functionality in the engine. It + is just used as an opaque value to identify which engine we are using + for each GTID row. + */ + void *table_hton; + LEX_CSTRING table_name; + uint8 state; + }; + /* Mapping from domain_id to its element. */ HASH hash; /* Mutex protecting access to the state. */ @@ -171,6 +197,30 @@ struct rpl_slave_state DYNAMIC_ARRAY gtid_sort_array; uint64 last_sub_id; + /* + List of tables available for durably storing the slave GTID position. + + Accesses to this table is protected by LOCK_slave_state. However for + efficiency, there is also a provision for read access to it from a running + slave without lock. + + An element can be added at the head of a list by storing the new + gtid_pos_tables pointer atomically with release semantics, to ensure that + the next pointer of the new element is visible to readers of the new list. + Other changes (like deleting or replacing elements) must happen only while + all SQL driver threads are stopped. LOCK_slave_state must be held in any + case. + + The list can be read without lock by an SQL driver thread or worker thread + by reading the gtid_pos_tables pointer atomically with acquire semantics, + to ensure that it will see the correct next pointer of a new head element. + + The type is struct gtid_pos_table *, but needs to be void * to allow using + my_atomic operations without violating C strict aliasing semantics. + */ + void * volatile gtid_pos_tables; + /* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */ + void * volatile default_gtid_pos_table; bool loaded; rpl_slave_state(); @@ -179,10 +229,11 @@ struct rpl_slave_state void truncate_hash(); ulong count() const { return hash.records; } int update(uint32 domain_id, uint32 server_id, uint64 sub_id, - uint64 seq_no, rpl_group_info *rgi); + uint64 seq_no, void *hton, rpl_group_info *rgi); int truncate_state_table(THD *thd); + void select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename); int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, - bool in_transaction, bool in_statement); + bool in_transaction, bool in_statement, void **out_hton); uint64 next_sub_id(uint32 domain_id); int iterate(int (*cb)(rpl_gtid *, void *), void *data, rpl_gtid *extra_gtids, uint32 num_extra, @@ -196,10 +247,17 @@ struct rpl_slave_state element *get_element(uint32 domain_id); int put_back_list(uint32 domain_id, list_element *list); - void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi); + void update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton, + rpl_group_info *rgi); int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi); int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi); void release_domain_owner(rpl_group_info *rgi); + void set_gtid_pos_tables_list(gtid_pos_table *new_list, + gtid_pos_table *default_entry); + void add_gtid_pos_table(gtid_pos_table *entry); + struct gtid_pos_table *alloc_gtid_pos_table(LEX_CSTRING *table_name, + void *hton, rpl_slave_state::gtid_pos_table_state state); + void free_gtid_pos_tables(struct gtid_pos_table *list); }; diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc deleted file mode 100644 index 520fb61d8c4..00000000000 --- a/sql/rpl_handler.cc +++ /dev/null @@ -1,553 +0,0 @@ -/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - - 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. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include <my_global.h> -#include "sql_priv.h" -#include "unireg.h" - -#include "rpl_mi.h" -#include "sql_repl.h" -#include "log_event.h" -#include "rpl_filter.h" -#include <my_dir.h> -#include "rpl_handler.h" - -Trans_delegate *transaction_delegate; -Binlog_storage_delegate *binlog_storage_delegate; -#ifdef HAVE_REPLICATION -Binlog_transmit_delegate *binlog_transmit_delegate; -Binlog_relay_IO_delegate *binlog_relay_io_delegate; -#endif /* HAVE_REPLICATION */ - -/* - structure to save transaction log filename and position -*/ -typedef struct Trans_binlog_info { - my_off_t log_pos; - char log_file[FN_REFLEN]; -} Trans_binlog_info; - -int get_user_var_int(const char *name, - long long int *value, int *null_value) -{ - bool null_val; - user_var_entry *entry= - (user_var_entry*) my_hash_search(¤t_thd->user_vars, - (uchar*) name, strlen(name)); - if (!entry) - return 1; - *value= entry->val_int(&null_val); - if (null_value) - *null_value= null_val; - return 0; -} - -int get_user_var_real(const char *name, - double *value, int *null_value) -{ - bool null_val; - user_var_entry *entry= - (user_var_entry*) my_hash_search(¤t_thd->user_vars, - (uchar*) name, strlen(name)); - if (!entry) - return 1; - *value= entry->val_real(&null_val); - if (null_value) - *null_value= null_val; - return 0; -} - -int get_user_var_str(const char *name, char *value, - size_t len, unsigned int precision, int *null_value) -{ - String str; - bool null_val; - user_var_entry *entry= - (user_var_entry*) my_hash_search(¤t_thd->user_vars, - (uchar*) name, strlen(name)); - if (!entry) - return 1; - entry->val_str(&null_val, &str, precision); - strncpy(value, str.c_ptr(), len); - if (null_value) - *null_value= null_val; - return 0; -} - -int delegates_init() -{ - static my_aligned_storage<sizeof(Trans_delegate), MY_ALIGNOF(long)> trans_mem; - static my_aligned_storage<sizeof(Binlog_storage_delegate), - MY_ALIGNOF(long)> storage_mem; -#ifdef HAVE_REPLICATION - static my_aligned_storage<sizeof(Binlog_transmit_delegate), - MY_ALIGNOF(long)> transmit_mem; - static my_aligned_storage<sizeof(Binlog_relay_IO_delegate), - MY_ALIGNOF(long)> relay_io_mem; -#endif - - void *place_trans_mem= trans_mem.data; - void *place_storage_mem= storage_mem.data; - - transaction_delegate= new (place_trans_mem) Trans_delegate; - - if (!transaction_delegate->is_inited()) - { - sql_print_error("Initialization of transaction delegates failed. " - "Please report a bug."); - return 1; - } - - binlog_storage_delegate= new (place_storage_mem) Binlog_storage_delegate; - - if (!binlog_storage_delegate->is_inited()) - { - sql_print_error("Initialization binlog storage delegates failed. " - "Please report a bug."); - return 1; - } - -#ifdef HAVE_REPLICATION - void *place_transmit_mem= transmit_mem.data; - void *place_relay_io_mem= relay_io_mem.data; - - binlog_transmit_delegate= new (place_transmit_mem) Binlog_transmit_delegate; - - if (!binlog_transmit_delegate->is_inited()) - { - sql_print_error("Initialization of binlog transmit delegates failed. " - "Please report a bug."); - return 1; - } - - binlog_relay_io_delegate= new (place_relay_io_mem) Binlog_relay_IO_delegate; - - if (!binlog_relay_io_delegate->is_inited()) - { - sql_print_error("Initialization binlog relay IO delegates failed. " - "Please report a bug."); - return 1; - } -#endif - - return 0; -} - -void delegates_destroy() -{ - if (transaction_delegate) - transaction_delegate->~Trans_delegate(); - if (binlog_storage_delegate) - binlog_storage_delegate->~Binlog_storage_delegate(); -#ifdef HAVE_REPLICATION - if (binlog_transmit_delegate) - binlog_transmit_delegate->~Binlog_transmit_delegate(); - if (binlog_relay_io_delegate) - binlog_relay_io_delegate->~Binlog_relay_IO_delegate(); -#endif /* HAVE_REPLICATION */ -} - -/* - This macro is used by almost all the Delegate methods to iterate - over all the observers running given callback function of the - delegate. - */ -#define FOREACH_OBSERVER(r, f, do_lock, args) \ - param.server_id= thd->variables.server_id; \ - read_lock(); \ - Observer_info_iterator iter= observer_info_iter(); \ - Observer_info *info= iter++; \ - for (; info; info= iter++) \ - { \ - if (do_lock) plugin_lock(thd, plugin_int_to_ref(info->plugin_int)); \ - if (((Observer *)info->observer)->f \ - && ((Observer *)info->observer)->f args) \ - { \ - r= 1; \ - sql_print_error("Run function '" #f "' in plugin '%s' failed", \ - info->plugin_int->name.str); \ - break; \ - } \ - } \ - unlock(); - - -int Trans_delegate::after_commit(THD *thd, bool all) -{ - Trans_param param; - Trans_binlog_info *log_info; - bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - int ret= 0; - - param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; - - log_info= thd->semisync_info; - - param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; - param.log_pos= log_info ? log_info->log_pos : 0; - - FOREACH_OBSERVER(ret, after_commit, false, (¶m)); - - /* - This is the end of a real transaction or autocommit statement, we - can mark the memory unused. - */ - if (is_real_trans && log_info) - { - log_info->log_file[0]= 0; - log_info->log_pos= 0; - } - return ret; -} - -int Trans_delegate::after_rollback(THD *thd, bool all) -{ - Trans_param param; - Trans_binlog_info *log_info; - bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - int ret= 0; - - param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; - - log_info= thd->semisync_info; - - param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; - param.log_pos= log_info ? log_info->log_pos : 0; - - FOREACH_OBSERVER(ret, after_rollback, false, (¶m)); - - /* - This is the end of a real transaction or autocommit statement, we - can mark the memory unused. - */ - if (is_real_trans && log_info) - { - log_info->log_file[0]= 0; - log_info->log_pos= 0; - } - return ret; -} - -int Binlog_storage_delegate::after_flush(THD *thd, - const char *log_file, - my_off_t log_pos, - bool synced, - bool first_in_group, - bool last_in_group) -{ - Binlog_storage_param param; - Trans_binlog_info *log_info; - uint32 flags=0; - int ret= 0; - - if (synced) - flags |= BINLOG_STORAGE_IS_SYNCED; - if (first_in_group) - flags|= BINLOG_GROUP_COMMIT_LEADER; - if (last_in_group) - flags|= BINLOG_GROUP_COMMIT_TRAILER; - - if (!(log_info= thd->semisync_info)) - { - if(!(log_info= - (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0)))) - return 1; - thd->semisync_info= log_info; - } - - strmake_buf(log_info->log_file, log_file+dirname_length(log_file)); - log_info->log_pos = log_pos; - - FOREACH_OBSERVER(ret, after_flush, false, - (¶m, log_info->log_file, log_info->log_pos, flags)); - return ret; -} - -int Binlog_storage_delegate::after_sync(THD *thd, - const char *log_file, - my_off_t log_pos, - bool first_in_group, - bool last_in_group) -{ - Binlog_storage_param param; - uint32 flags=0; - - if (first_in_group) - flags|= BINLOG_GROUP_COMMIT_LEADER; - if (last_in_group) - flags|= BINLOG_GROUP_COMMIT_TRAILER; - - int ret= 0; - FOREACH_OBSERVER(ret, after_sync, false, - (¶m, log_file+dirname_length(log_file), log_pos, flags)); - - return ret; -} - -#ifdef HAVE_REPLICATION -int Binlog_transmit_delegate::transmit_start(THD *thd, ushort flags, - const char *log_file, - my_off_t log_pos) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, transmit_start, true, (¶m, log_file, log_pos)); - return ret; -} - -int Binlog_transmit_delegate::transmit_stop(THD *thd, ushort flags) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, transmit_stop, false, (¶m)); - return ret; -} - -int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags, - String *packet) -{ - /* NOTE2ME: Maximum extra header size for each observer, I hope 32 - bytes should be enough for each Observer to reserve their extra - header. If later found this is not enough, we can increase this - /HEZX - */ -#define RESERVE_HEADER_SIZE 32 - unsigned char header[RESERVE_HEADER_SIZE]; - ulong hlen; - Binlog_transmit_param param; - param.flags= flags; - param.server_id= thd->variables.server_id; - - int ret= 0; - read_lock(); - Observer_info_iterator iter= observer_info_iter(); - Observer_info *info= iter++; - for (; info; info= iter++) - { - hlen= 0; - if (((Observer *)info->observer)->reserve_header - && ((Observer *)info->observer)->reserve_header(¶m, - header, - RESERVE_HEADER_SIZE, - &hlen)) - { - ret= 1; - break; - } - if (hlen == 0) - continue; - if (hlen > RESERVE_HEADER_SIZE || packet->append((char *)header, hlen)) - { - ret= 1; - break; - } - } - unlock(); - return ret; -} - -int Binlog_transmit_delegate::before_send_event(THD *thd, ushort flags, - String *packet, - const char *log_file, - my_off_t log_pos) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, before_send_event, false, - (¶m, (uchar *)packet->c_ptr(), - packet->length(), - log_file+dirname_length(log_file), log_pos)); - return ret; -} - -int Binlog_transmit_delegate::after_send_event(THD *thd, ushort flags, - String *packet) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, after_send_event, false, - (¶m, packet->c_ptr(), packet->length())); - return ret; -} - -int Binlog_transmit_delegate::after_reset_master(THD *thd, ushort flags) - -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, after_reset_master, false, (¶m)); - return ret; -} - -void Binlog_relay_IO_delegate::init_param(Binlog_relay_IO_param *param, - Master_info *mi) -{ - param->mysql= mi->mysql; - param->user= mi->user; - param->host= mi->host; - param->port= mi->port; - param->master_log_name= mi->master_log_name; - param->master_log_pos= mi->master_log_pos; -} - -int Binlog_relay_IO_delegate::thread_start(THD *thd, Master_info *mi) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, thread_start, true, (¶m)); - return ret; -} - - -int Binlog_relay_IO_delegate::thread_stop(THD *thd, Master_info *mi) -{ - - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, thread_stop, false, (¶m)); - return ret; -} - -int Binlog_relay_IO_delegate::before_request_transmit(THD *thd, - Master_info *mi, - ushort flags) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, before_request_transmit, false, (¶m, (uint32)flags)); - return ret; -} - -int Binlog_relay_IO_delegate::after_read_event(THD *thd, Master_info *mi, - const char *packet, ulong len, - const char **event_buf, - ulong *event_len) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, after_read_event, false, - (¶m, packet, len, event_buf, event_len)); - return ret; -} - -int Binlog_relay_IO_delegate::after_queue_event(THD *thd, Master_info *mi, - const char *event_buf, - ulong event_len, - bool synced) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - uint32 flags=0; - if (synced) - flags |= BINLOG_STORAGE_IS_SYNCED; - - int ret= 0; - FOREACH_OBSERVER(ret, after_queue_event, false, - (¶m, event_buf, event_len, flags)); - return ret; -} - -int Binlog_relay_IO_delegate::after_reset_slave(THD *thd, Master_info *mi) - -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, after_reset_slave, false, (¶m)); - return ret; -} -#endif /* HAVE_REPLICATION */ - -int register_trans_observer(Trans_observer *observer, void *p) -{ - return transaction_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_trans_observer(Trans_observer *observer, void *p) -{ - return transaction_delegate->remove_observer(observer, (st_plugin_int *)p); -} - -int register_binlog_storage_observer(Binlog_storage_observer *observer, void *p) -{ - return binlog_storage_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_binlog_storage_observer(Binlog_storage_observer *observer, void *p) -{ - return binlog_storage_delegate->remove_observer(observer, (st_plugin_int *)p); -} - -#ifdef HAVE_REPLICATION -int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return binlog_transmit_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return binlog_transmit_delegate->remove_observer(observer, (st_plugin_int *)p); -} - -int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return binlog_relay_io_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return binlog_relay_io_delegate->remove_observer(observer, (st_plugin_int *)p); -} -#else -int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return 0; -} - -int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return 0; -} - -int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return 0; -} - -int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return 0; -} -#endif /* HAVE_REPLICATION */ diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h deleted file mode 100644 index afcfd9d55b1..00000000000 --- a/sql/rpl_handler.h +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. - - 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. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifndef RPL_HANDLER_H -#define RPL_HANDLER_H - -#include "sql_priv.h" -#include "rpl_mi.h" -#include "rpl_rli.h" -#include "sql_plugin.h" -#include "replication.h" - -class Observer_info { -public: - void *observer; - st_plugin_int *plugin_int; - - Observer_info(void *ob, st_plugin_int *p) - :observer(ob), plugin_int(p) - { } -}; - -class Delegate { -public: - typedef List<Observer_info> Observer_info_list; - typedef List_iterator<Observer_info> Observer_info_iterator; - - int add_observer(void *observer, st_plugin_int *plugin) - { - int ret= FALSE; - if (!inited) - return TRUE; - write_lock(); - Observer_info_iterator iter(observer_info_list); - Observer_info *info= iter++; - while (info && info->observer != observer) - info= iter++; - if (!info) - { - info= new Observer_info(observer, plugin); - if (!info || observer_info_list.push_back(info, &memroot)) - ret= TRUE; - } - else - ret= TRUE; - unlock(); - return ret; - } - - int remove_observer(void *observer, st_plugin_int *plugin) - { - int ret= FALSE; - if (!inited) - return TRUE; - write_lock(); - Observer_info_iterator iter(observer_info_list); - Observer_info *info= iter++; - while (info && info->observer != observer) - info= iter++; - if (info) - { - iter.remove(); - delete info; - } - else - ret= TRUE; - unlock(); - return ret; - } - - inline Observer_info_iterator observer_info_iter() - { - return Observer_info_iterator(observer_info_list); - } - - inline bool is_empty() - { - return observer_info_list.is_empty(); - } - - inline int read_lock() - { - if (!inited) - return TRUE; - return rw_rdlock(&lock); - } - - inline int write_lock() - { - if (!inited) - return TRUE; - return rw_wrlock(&lock); - } - - inline int unlock() - { - if (!inited) - return TRUE; - return rw_unlock(&lock); - } - - inline bool is_inited() - { - return inited; - } - - Delegate() - { - inited= FALSE; - if (my_rwlock_init(&lock, NULL)) - return; - init_sql_alloc(&memroot, 1024, 0, MYF(0)); - inited= TRUE; - } - ~Delegate() - { - inited= FALSE; - rwlock_destroy(&lock); - free_root(&memroot, MYF(0)); - } - -private: - Observer_info_list observer_info_list; - rw_lock_t lock; - MEM_ROOT memroot; - bool inited; -}; - -class Trans_delegate - :public Delegate { -public: - typedef Trans_observer Observer; - int before_commit(THD *thd, bool all); - int before_rollback(THD *thd, bool all); - int after_commit(THD *thd, bool all); - int after_rollback(THD *thd, bool all); -}; - -class Binlog_storage_delegate - :public Delegate { -public: - typedef Binlog_storage_observer Observer; - int after_flush(THD *thd, const char *log_file, - my_off_t log_pos, bool synced, - bool first_in_group, bool last_in_group); - int after_sync(THD *thd, const char *log_file, my_off_t log_pos, - bool first_in_group, bool last_in_group); -}; - -#ifdef HAVE_REPLICATION -class Binlog_transmit_delegate - :public Delegate { -public: - typedef Binlog_transmit_observer Observer; - int transmit_start(THD *thd, ushort flags, - const char *log_file, my_off_t log_pos); - int transmit_stop(THD *thd, ushort flags); - int reserve_header(THD *thd, ushort flags, String *packet); - int before_send_event(THD *thd, ushort flags, - String *packet, const - char *log_file, my_off_t log_pos ); - int after_send_event(THD *thd, ushort flags, - String *packet); - int after_reset_master(THD *thd, ushort flags); -}; - -class Binlog_relay_IO_delegate - :public Delegate { -public: - typedef Binlog_relay_IO_observer Observer; - int thread_start(THD *thd, Master_info *mi); - int thread_stop(THD *thd, Master_info *mi); - int before_request_transmit(THD *thd, Master_info *mi, ushort flags); - int after_read_event(THD *thd, Master_info *mi, - const char *packet, ulong len, - const char **event_buf, ulong *event_len); - int after_queue_event(THD *thd, Master_info *mi, - const char *event_buf, ulong event_len, - bool synced); - int after_reset_slave(THD *thd, Master_info *mi); -private: - void init_param(Binlog_relay_IO_param *param, Master_info *mi); -}; -#endif /* HAVE_REPLICATION */ - -int delegates_init(); -void delegates_destroy(); - -extern Trans_delegate *transaction_delegate; -extern Binlog_storage_delegate *binlog_storage_delegate; -#ifdef HAVE_REPLICATION -extern Binlog_transmit_delegate *binlog_transmit_delegate; -extern Binlog_relay_IO_delegate *binlog_relay_io_delegate; -#endif /* HAVE_REPLICATION */ - -/* - if there is no observers in the delegate, we can return 0 - immediately. -*/ -#define RUN_HOOK(group, hook, args) \ - (group ##_delegate->is_empty() ? \ - 0 : group ##_delegate->hook args) - -#endif /* RPL_HANDLER_H */ diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index bff0da26862..d7081f766ec 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "rpl_injector.h" #include "transaction.h" diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index 41e1fcf460c..dc7bfb6503a 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -17,7 +17,6 @@ #define INJECTOR_H /* Pull in 'byte', 'my_off_t', and 'uint32' */ -#include <my_global.h> #include <my_bitmap.h> #include "rpl_constants.h" diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 96d9ee4a1b4..9661e0b0353 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> // For HAVE_REPLICATION +#include "mariadb.h" // For HAVE_REPLICATION #include "sql_priv.h" #include <my_dir.h> #include "rpl_mi.h" @@ -1557,6 +1557,9 @@ bool give_error_if_slave_running(bool already_locked) /** any_slave_sql_running() + @param + already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked + @return 0 No Slave SQL thread is running # Number of slave SQL thread running @@ -1567,26 +1570,28 @@ bool give_error_if_slave_running(bool already_locked) hash entries can't be accessed. */ -uint any_slave_sql_running() +uint any_slave_sql_running(bool already_locked) { uint count= 0; HASH *hash; DBUG_ENTER("any_slave_sql_running"); - mysql_mutex_lock(&LOCK_active_mi); + if (!already_locked) + mysql_mutex_lock(&LOCK_active_mi); if (unlikely(shutdown_in_progress || !master_info_index)) + count= 1; + else { - mysql_mutex_unlock(&LOCK_active_mi); - DBUG_RETURN(1); - } - hash= &master_info_index->master_info_hash; - for (uint i= 0; i< hash->records; ++i) - { - Master_info *mi= (Master_info *)my_hash_element(hash, i); - if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) - count++; + hash= &master_info_index->master_info_hash; + for (uint i= 0; i< hash->records; ++i) + { + Master_info *mi= (Master_info *)my_hash_element(hash, i); + if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) + count++; + } } - mysql_mutex_unlock(&LOCK_active_mi); + if (!already_locked) + mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(count); } diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index ccc1be6e5ce..14d74dc4bb7 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -20,7 +20,7 @@ #include "rpl_rli.h" #include "rpl_reporting.h" -#include "my_sys.h" +#include <my_sys.h> #include "rpl_filter.h" #include "keycaches.h" @@ -311,6 +311,11 @@ class Master_info : public Slave_reporting_capability /* The parallel replication mode. */ enum_slave_parallel_mode parallel_mode; + /* + semi_ack is used to identify if the current binlog event needs an + ACK from slave, or if delay_master is enabled. + */ + int semi_ack; }; int init_master_info(Master_info* mi, const char* master_info_fname, @@ -379,7 +384,7 @@ void create_logfile_name_with_suffix(char *res_file_name, size_t length, uchar *get_key_master_info(Master_info *mi, size_t *length, my_bool not_used __attribute__((unused))); void free_key_master_info(Master_info *mi); -uint any_slave_sql_running(); +uint any_slave_sql_running(bool already_locked); bool give_error_if_slave_running(bool already_lock); #endif /* HAVE_REPLICATION */ diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 973b9d899c1..9ebd19a90e2 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -1,4 +1,4 @@ -#include "my_global.h" +#include "mariadb.h" #include "rpl_parallel.h" #include "slave.h" #include "rpl_mi.h" @@ -255,8 +255,8 @@ signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi, int err) rgi->rli->abort_slave= true; rgi->rli->stop_for_until= false; mysql_mutex_lock(rgi->rli->relay_log.get_log_lock()); + rgi->rli->relay_log.signal_relay_log_update(); mysql_mutex_unlock(rgi->rli->relay_log.get_log_lock()); - rgi->rli->relay_log.signal_update(); } @@ -821,7 +821,7 @@ do_retry: for (;;) { old_offset= cur_offset; - ev= Log_event::read_log_event(&rlog, 0, description_event, + ev= Log_event::read_log_event(&rlog, description_event, opt_slave_sql_verify_checksum); cur_offset= my_b_tell(&rlog); @@ -1002,8 +1002,7 @@ handle_rpl_parallel_thread(void *arg) thd->security_ctx->skip_grants(); thd->variables.max_allowed_packet= slave_max_allowed_packet; thd->slave_thread= 1; - thd->variables.sql_log_slow= opt_log_slow_slave_statements; - thd->variables.log_slow_filter= global_system_variables.log_slow_filter; + set_slave_thread_options(thd); thd->client_capabilities = CLIENT_LOCAL_FILES; thd->net.reading_or_writing= 0; @@ -1462,7 +1461,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, */ if (!new_count && !force) { - if (any_slave_sql_running()) + if (any_slave_sql_running(false)) { DBUG_PRINT("warning", ("SQL threads running while trying to reset parallel pool")); @@ -1617,7 +1616,7 @@ err: int rpl_parallel_resize_pool_if_no_slaves(void) { /* master_info_index is set to NULL on shutdown */ - if (opt_slave_parallel_threads > 0 && !any_slave_sql_running()) + if (opt_slave_parallel_threads > 0 && !any_slave_sql_running(false)) return rpl_parallel_inactivate_pool(&global_rpl_thread_pool); return 0; } diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 1b1059cc529..6da3f15cfb9 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "rpl_rli.h" diff --git a/sql/rpl_record.h b/sql/rpl_record.h index be69716d9d5..8d565845c4b 100644 --- a/sql/rpl_record.h +++ b/sql/rpl_record.h @@ -18,7 +18,6 @@ #define RPL_RECORD_H #include <rpl_reporting.h> -#include "my_global.h" /* uchar */ struct rpl_group_info; struct TABLE; diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc index fd37c6f9142..523049cf959 100644 --- a/sql/rpl_record_old.cc +++ b/sql/rpl_record_old.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "rpl_rli.h" #include "rpl_record_old.h" diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc index ad949402511..800682fab91 100644 --- a/sql/rpl_reporting.cc +++ b/sql/rpl_reporting.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "rpl_reporting.h" #include "log.h" // sql_print_error, sql_print_warning, diff --git a/sql/rpl_reporting.h b/sql/rpl_reporting.h index d90b7ad6650..17748f587b1 100644 --- a/sql/rpl_reporting.h +++ b/sql/rpl_reporting.h @@ -16,7 +16,7 @@ #ifndef RPL_REPORTING_H #define RPL_REPORTING_H -#include "my_sys.h" /* loglevel */ +#include <my_sys.h> /* loglevel */ /** Maximum size of an error message from a slave thread. diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index ed9f0369f5d..321eef97700 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" // HAVE_* #include "rpl_mi.h" @@ -31,6 +31,8 @@ #include "slave.h" #include <mysql/plugin.h> #include <mysql/service_thd_wait.h> +#include "lock.h" +#include "sql_table.h" static int count_relay_log_space(Relay_log_info* rli); @@ -68,10 +70,12 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) relay_log_state.init(); #ifdef HAVE_PSI_INTERFACE relay_log.set_psi_keys(key_RELAYLOG_LOCK_index, - key_RELAYLOG_update_cond, + key_RELAYLOG_COND_relay_log_updated, + key_RELAYLOG_COND_bin_log_updated, key_file_relaylog, key_file_relaylog_index, - key_RELAYLOG_COND_queue_busy); + key_RELAYLOG_COND_queue_busy, + key_LOCK_relaylog_end_pos); #endif group_relay_log_name[0]= event_relay_log_name[0]= @@ -536,7 +540,7 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos, if (my_b_tell(cur_log) >= start_pos) break; - if (!(ev= Log_event::read_log_event(cur_log, 0, fdev, + if (!(ev= Log_event::read_log_event(cur_log, fdev, opt_slave_sql_verify_checksum))) { DBUG_PRINT("info",("could not read event, cur_log->error=%d", @@ -1509,41 +1513,22 @@ Relay_log_info::update_relay_log_state(rpl_gtid *gtid_list, uint32 count) #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int -rpl_load_gtid_slave_state(THD *thd) +struct gtid_pos_element { uint64 sub_id; rpl_gtid gtid; void *hton; }; + +static int +scan_one_gtid_slave_pos_table(THD *thd, HASH *hash, DYNAMIC_ARRAY *array, + LEX_CSTRING *tablename, void **out_hton) { TABLE_LIST tlist; TABLE *table; bool table_opened= false; bool table_scanned= false; - bool array_inited= false; - struct local_element { uint64 sub_id; rpl_gtid gtid; }; - struct local_element tmp_entry, *entry; - HASH hash; - DYNAMIC_ARRAY array; + struct gtid_pos_element tmp_entry, *entry; int err= 0; - uint32 i; - DBUG_ENTER("rpl_load_gtid_slave_state"); - - mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); - bool loaded= rpl_global_gtid_slave_state->loaded; - mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); - if (loaded) - DBUG_RETURN(0); - - my_hash_init(&hash, &my_charset_bin, 32, - offsetof(local_element, gtid) + offsetof(rpl_gtid, domain_id), - sizeof(uint32), NULL, my_free, HASH_UNIQUE); - if ((err= my_init_dynamic_array(&array, sizeof(local_element), 0, 0, MYF(0)))) - goto end; - array_inited= true; thd->reset_for_next_command(); - - tlist.init_one_table(STRING_WITH_LEN("mysql"), - rpl_gtid_slave_state_table_name.str, - rpl_gtid_slave_state_table_name.length, - NULL, TL_READ); + tlist.init_one_table(STRING_WITH_LEN("mysql"), tablename->str, + tablename->length, NULL, TL_READ); if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0))) goto end; table_opened= true; @@ -1589,26 +1574,28 @@ rpl_load_gtid_slave_state(THD *thd) tmp_entry.gtid.domain_id= domain_id; tmp_entry.gtid.server_id= server_id; tmp_entry.gtid.seq_no= seq_no; - if ((err= insert_dynamic(&array, (uchar *)&tmp_entry))) + tmp_entry.hton= table->s->db_type(); + if ((err= insert_dynamic(array, (uchar *)&tmp_entry))) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto end; } - if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0))) + if ((rec= my_hash_search(hash, (const uchar *)&domain_id, 0))) { - entry= (struct local_element *)rec; + entry= (struct gtid_pos_element *)rec; if (entry->sub_id >= sub_id) continue; entry->sub_id= sub_id; DBUG_ASSERT(entry->gtid.domain_id == domain_id); entry->gtid.server_id= server_id; entry->gtid.seq_no= seq_no; + entry->hton= table->s->db_type(); } else { - if (!(entry= (struct local_element *)my_malloc(sizeof(*entry), - MYF(MY_WME)))) + if (!(entry= (struct gtid_pos_element *)my_malloc(sizeof(*entry), + MYF(MY_WME)))) { my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry)); err= 1; @@ -1618,7 +1605,8 @@ rpl_load_gtid_slave_state(THD *thd) entry->gtid.domain_id= domain_id; entry->gtid.server_id= server_id; entry->gtid.seq_no= seq_no; - if ((err= my_hash_insert(&hash, (uchar *)entry))) + entry->hton= table->s->db_type(); + if ((err= my_hash_insert(hash, (uchar *)entry))) { my_free(entry); my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -1626,6 +1614,249 @@ rpl_load_gtid_slave_state(THD *thd) } } } + err= 0; /* Clear HA_ERR_END_OF_FILE */ + +end: + if (table_scanned) + { + table->file->ha_index_or_rnd_end(); + ha_commit_trans(thd, FALSE); + ha_commit_trans(thd, TRUE); + } + if (table_opened) + { + *out_hton= table->s->db_type(); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + } + return err; +} + + +/* + Look for all tables mysql.gtid_slave_pos*. Read all rows from each such + table found into ARRAY. For each domain id, put the row with highest sub_id + into HASH. +*/ +static int +scan_all_gtid_slave_pos_table(THD *thd, int (*cb)(THD *, LEX_CSTRING *, void *), + void *cb_data) +{ + static LEX_CSTRING mysql_db_name= {C_STRING_WITH_LEN("mysql")}; + char path[FN_REFLEN]; + MY_DIR *dirp; + + thd->reset_for_next_command(); + if (lock_schema_name(thd, mysql_db_name.str)) + return 1; + + build_table_filename(path, sizeof(path) - 1, mysql_db_name.str, "", "", 0); + if (!(dirp= my_dir(path, MYF(MY_DONT_SORT)))) + { + my_error(ER_FILE_NOT_FOUND, MYF(0), path, my_errno); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + return 1; + } + else + { + size_t i; + Dynamic_array<LEX_CSTRING*> files(dirp->number_of_files); + Discovered_table_list tl(thd, &files); + int err; + + err= ha_discover_table_names(thd, &mysql_db_name, dirp, &tl, false); + my_dirend(dirp); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + if (err) + return err; + + for (i = 0; i < files.elements(); ++i) + { + if (strncmp(files.at(i)->str, + rpl_gtid_slave_state_table_name.str, + rpl_gtid_slave_state_table_name.length) == 0) + { + if ((err= (*cb)(thd, files.at(i), cb_data))) + return err; + } + } + } + + return 0; +} + + +struct load_gtid_state_cb_data { + HASH *hash; + DYNAMIC_ARRAY *array; + struct rpl_slave_state::gtid_pos_table *table_list; + struct rpl_slave_state::gtid_pos_table *default_entry; +}; + +static int +process_gtid_pos_table(THD *thd, LEX_CSTRING *table_name, void *hton, + struct load_gtid_state_cb_data *data) +{ + struct rpl_slave_state::gtid_pos_table *p, *entry, **next_ptr; + bool is_default= + (strcmp(table_name->str, rpl_gtid_slave_state_table_name.str) == 0); + + /* + Ignore tables with duplicate storage engine, with a warning. + Prefer the default mysql.gtid_slave_pos over another table + mysql.gtid_slave_posXXX with the same storage engine. + */ + next_ptr= &data->table_list; + entry= data->table_list; + while (entry) + { + if (entry->table_hton == hton) + { + static const char *warning_msg= "Ignoring redundant table mysql.%s " + "since mysql.%s has the same storage engine"; + if (!is_default) + { + /* Ignore the redundant table. */ + sql_print_warning(warning_msg, table_name->str, entry->table_name); + return 0; + } + else + { + sql_print_warning(warning_msg, entry->table_name, table_name->str); + /* Delete the redundant table, and proceed to add this one instead. */ + *next_ptr= entry->next; + my_free(entry); + break; + } + } + next_ptr= &entry->next; + entry= entry->next; + } + + p= rpl_global_gtid_slave_state->alloc_gtid_pos_table(table_name, + hton, rpl_slave_state::GTID_POS_AVAILABLE); + if (!p) + return 1; + p->next= data->table_list; + data->table_list= p; + if (is_default) + data->default_entry= p; + return 0; +} + + +/* + Put tables corresponding to @@gtid_pos_auto_engines at the end of the list, + marked to be auto-created if needed. +*/ +static int +gtid_pos_auto_create_tables(rpl_slave_state::gtid_pos_table **list_ptr) +{ + plugin_ref *auto_engines; + int err= 0; + mysql_mutex_lock(&LOCK_global_system_variables); + for (auto_engines= opt_gtid_pos_auto_plugins; + !err && auto_engines && *auto_engines; + ++auto_engines) + { + void *hton= plugin_hton(*auto_engines); + char buf[FN_REFLEN+1]; + LEX_CSTRING table_name; + char *p; + rpl_slave_state::gtid_pos_table *entry, **next_ptr; + + /* See if this engine is already in the list. */ + next_ptr= list_ptr; + entry= *list_ptr; + while (entry) + { + if (entry->table_hton == hton) + break; + next_ptr= &entry->next; + entry= entry->next; + } + if (entry) + continue; + + /* Add an auto-create entry for this engine at end of list. */ + p= strmake(buf, rpl_gtid_slave_state_table_name.str, FN_REFLEN); + p= strmake(p, "_", FN_REFLEN - (p - buf)); + p= strmake(p, plugin_name(*auto_engines)->str, FN_REFLEN - (p - buf)); + table_name.str= buf; + table_name.length= p - buf; + entry= rpl_global_gtid_slave_state->alloc_gtid_pos_table + (&table_name, hton, rpl_slave_state::GTID_POS_AUTO_CREATE); + if (!entry) + { + err= 1; + break; + } + *next_ptr= entry; + } + mysql_mutex_unlock(&LOCK_global_system_variables); + return err; +} + + +static int +load_gtid_state_cb(THD *thd, LEX_CSTRING *table_name, void *arg) +{ + int err; + load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg); + void *hton; + + if ((err= scan_one_gtid_slave_pos_table(thd, data->hash, data->array, + table_name, &hton))) + return err; + return process_gtid_pos_table(thd, table_name, hton, data); +} + + +int +rpl_load_gtid_slave_state(THD *thd) +{ + bool array_inited= false; + struct gtid_pos_element tmp_entry, *entry; + HASH hash; + DYNAMIC_ARRAY array; + int err= 0; + uint32 i; + load_gtid_state_cb_data cb_data; + DBUG_ENTER("rpl_load_gtid_slave_state"); + + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + bool loaded= rpl_global_gtid_slave_state->loaded; + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (loaded) + DBUG_RETURN(0); + + cb_data.table_list= NULL; + cb_data.default_entry= NULL; + my_hash_init(&hash, &my_charset_bin, 32, + offsetof(gtid_pos_element, gtid) + offsetof(rpl_gtid, domain_id), + sizeof(uint32), NULL, my_free, HASH_UNIQUE); + if ((err= my_init_dynamic_array(&array, sizeof(gtid_pos_element), 0, 0, MYF(0)))) + goto end; + array_inited= true; + + cb_data.hash = &hash; + cb_data.array = &array; + if ((err= scan_all_gtid_slave_pos_table(thd, load_gtid_state_cb, &cb_data))) + goto end; + + if (!cb_data.default_entry) + { + /* + If the mysql.gtid_slave_pos table does not exist, but at least one other + table is available, arbitrarily pick the first in the list to use as + default. + */ + cb_data.default_entry= cb_data.table_list; + } + if ((err= gtid_pos_auto_create_tables(&cb_data.table_list))) + goto end; mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); if (rpl_global_gtid_slave_state->loaded) @@ -1634,14 +1865,24 @@ rpl_load_gtid_slave_state(THD *thd) goto end; } + if (!cb_data.table_list) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql", + rpl_gtid_slave_state_table_name.str); + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + err= 1; + goto end; + } + for (i= 0; i < array.elements; ++i) { get_dynamic(&array, (uchar *)&tmp_entry, i); if ((err= rpl_global_gtid_slave_state->update(tmp_entry.gtid.domain_id, - tmp_entry.gtid.server_id, - tmp_entry.sub_id, - tmp_entry.gtid.seq_no, - NULL))) + tmp_entry.gtid.server_id, + tmp_entry.sub_id, + tmp_entry.gtid.seq_no, + tmp_entry.hton, + NULL))) { mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -1651,7 +1892,7 @@ rpl_load_gtid_slave_state(THD *thd) for (i= 0; i < hash.records; ++i) { - entry= (struct local_element *)my_hash_element(&hash, i); + entry= (struct gtid_pos_element *)my_hash_element(&hash, i); if (opt_bin_log && mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id, entry->gtid.seq_no)) @@ -1662,27 +1903,175 @@ rpl_load_gtid_slave_state(THD *thd) } } + rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list, + cb_data.default_entry); + cb_data.table_list= NULL; rpl_global_gtid_slave_state->loaded= true; mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); - err= 0; /* Clear HA_ERR_END_OF_FILE */ +end: + if (array_inited) + delete_dynamic(&array); + my_hash_free(&hash); + if (cb_data.table_list) + rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list); + DBUG_RETURN(err); +} + + +static int +find_gtid_pos_tables_cb(THD *thd, LEX_CSTRING *table_name, void *arg) +{ + load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg); + TABLE_LIST tlist; + TABLE *table= NULL; + int err; + + thd->reset_for_next_command(); + tlist.init_one_table(STRING_WITH_LEN("mysql"), table_name->str, + table_name->length, NULL, TL_READ); + if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0))) + goto end; + table= tlist.table; + + if ((err= gtid_check_rpl_slave_state_table(table))) + goto end; + err= process_gtid_pos_table(thd, table_name, table->s->db_type(), data); end: - if (table_scanned) + if (table) { - table->file->ha_index_or_rnd_end(); ha_commit_trans(thd, FALSE); ha_commit_trans(thd, TRUE); - } - if (table_opened) - { close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); } - if (array_inited) - delete_dynamic(&array); - my_hash_free(&hash); - DBUG_RETURN(err); + + return err; +} + + +/* + Re-compute the list of available mysql.gtid_slave_posXXX tables. + + This is done at START SLAVE to pick up any newly created tables without + requiring server restart. +*/ +int +find_gtid_slave_pos_tables(THD *thd) +{ + int err= 0; + load_gtid_state_cb_data cb_data; + uint num_running; + + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + bool loaded= rpl_global_gtid_slave_state->loaded; + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (!loaded) + return 0; + + cb_data.table_list= NULL; + cb_data.default_entry= NULL; + if ((err= scan_all_gtid_slave_pos_table(thd, find_gtid_pos_tables_cb, &cb_data))) + goto end; + + if (!cb_data.table_list) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql", + rpl_gtid_slave_state_table_name.str); + err= 1; + goto end; + } + if (!cb_data.default_entry) + { + /* + If the mysql.gtid_slave_pos table does not exist, but at least one other + table is available, arbitrarily pick the first in the list to use as + default. + */ + cb_data.default_entry= cb_data.table_list; + } + if ((err= gtid_pos_auto_create_tables(&cb_data.table_list))) + goto end; + + mysql_mutex_lock(&LOCK_active_mi); + num_running= any_slave_sql_running(true); + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (num_running <= 1) + { + /* + If no slave is running now, the count will be 1, since this SQL thread + which is starting is included in the count. In this case, we can safely + replace the list, no-one can be trying to read it without lock. + */ + DBUG_ASSERT(num_running == 1); + rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list, + cb_data.default_entry); + cb_data.table_list= NULL; + } + else + { + /* + If there are SQL threads running, we cannot safely remove the old list. + However we can add new entries, and warn about any tables that + disappeared, but may still be visible to running SQL threads. + */ + rpl_slave_state::gtid_pos_table *old_entry, *new_entry, **next_ptr_ptr; + + old_entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (old_entry) + { + new_entry= cb_data.table_list; + while (new_entry) + { + if (new_entry->table_hton == old_entry->table_hton) + break; + new_entry= new_entry->next; + } + if (!new_entry) + sql_print_warning("The table mysql.%s was removed. " + "This change will not take full effect " + "until all SQL threads have been restarted", + old_entry->table_name.str); + old_entry= old_entry->next; + } + next_ptr_ptr= &cb_data.table_list; + new_entry= cb_data.table_list; + while (new_entry) + { + /* Check if we already have a table with this storage engine. */ + old_entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (old_entry) + { + if (new_entry->table_hton == old_entry->table_hton) + break; + old_entry= old_entry->next; + } + if (old_entry) + { + /* This new_entry is already available in the list. */ + next_ptr_ptr= &new_entry->next; + new_entry= new_entry->next; + } + else + { + /* Move this new_entry to the list. */ + rpl_slave_state::gtid_pos_table *next= new_entry->next; + rpl_global_gtid_slave_state->add_gtid_pos_table(new_entry); + *next_ptr_ptr= next; + new_entry= next; + } + } + } + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + mysql_mutex_unlock(&LOCK_active_mi); + +end: + if (cb_data.table_list) + rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list); + return err; } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index d88bc9f6ecd..b8b153c34be 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -752,7 +752,7 @@ struct rpl_group_info Runtime state for printing a note when slave is taking too long while processing a row event. */ - time_t row_stmt_start_timestamp; + longlong row_stmt_start_timestamp; bool long_find_row_note_printed; /* Needs room for "Gtid D-S-N\x00". */ char gtid_info_buf[5+10+1+10+1+20+1]; @@ -898,17 +898,15 @@ struct rpl_group_info char *gtid_info(); void unmark_start_commit(); - time_t get_row_stmt_start_timestamp() + longlong get_row_stmt_start_timestamp() { return row_stmt_start_timestamp; } - time_t set_row_stmt_start_timestamp() + void set_row_stmt_start_timestamp() { if (row_stmt_start_timestamp == 0) - row_stmt_start_timestamp= my_time(0); - - return row_stmt_start_timestamp; + row_stmt_start_timestamp= microsecond_interval_timer(); } void reset_row_stmt_start_timestamp() @@ -967,6 +965,7 @@ extern struct rpl_slave_state *rpl_global_gtid_slave_state; extern gtid_waiting rpl_global_gtid_waiting; int rpl_load_gtid_slave_state(THD *thd); +int find_gtid_slave_pos_tables(THD *thd); int event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev); void delete_or_keep_event_post_apply(rpl_group_info *rgi, Log_event_type typ, Log_event *ev); diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc index 48111bc5d0a..80d093722f7 100644 --- a/sql/rpl_tblmap.cc +++ b/sql/rpl_tblmap.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifdef HAVE_REPLICATION diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index dd7a6fd85c7..9c1892e0c6c 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <my_bit.h> #include "rpl_utility.h" #include "log_event.h" @@ -128,6 +128,8 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata) case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: return metadata; + case MYSQL_TYPE_VARCHAR_COMPRESSED: + return metadata - 1; /* The actual length for these types does not really matter since @@ -145,6 +147,7 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata) return (uint32)my_set_bits(3 * 8); case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: /* For the blob type, Field::real_type() lies and say that all blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look @@ -294,6 +297,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const break; } case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: { length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length() length+= length == 1 ? (uint32) *master_data : uint2korr(master_data); @@ -303,6 +307,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_GEOMETRY: { /* @@ -406,11 +411,14 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: { CHARSET_INFO *cs= str->charset(); uint32 length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "varchar(%u)", metadata); + "varchar(%u)%s", metadata, + type == MYSQL_TYPE_VARCHAR_COMPRESSED ? " compressed" + : ""); str->length(length); } break; @@ -455,6 +463,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ break; case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: /* Field::real_type() lies regarding the actual type of a BLOB, so it is necessary to check the pack length to figure out what kind @@ -482,6 +491,9 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ DBUG_ASSERT(0); break; } + + if (type == MYSQL_TYPE_BLOB_COMPRESSED) + str->append(STRING_WITH_LEN(" compressed")); break; case MYSQL_TYPE_STRING: @@ -583,6 +595,7 @@ can_convert_field_to(Field *field, int *order_var) { DBUG_ENTER("can_convert_field_to"); + bool same_type; #ifndef DBUG_OFF char field_type_buf[MAX_FIELD_WIDTH]; String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1); @@ -590,11 +603,30 @@ can_convert_field_to(Field *field, DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x", field_type.c_ptr_safe(), field->real_type(), source_type, metadata)); #endif + /** + @todo + Implement Field_varstring_cmopressed::real_type() and + Field_blob_compressed::real_type() properly. All occurencies + of Field::real_type() have to be inspected and adjusted if needed. + + Until it is not ready we have to compare source_type against + binlog_type() when replicating from or to compressed data types. + + @sa Comment for Field::binlog_type() + */ + if (source_type == MYSQL_TYPE_VARCHAR_COMPRESSED || + source_type == MYSQL_TYPE_BLOB_COMPRESSED || + field->binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED || + field->binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED) + same_type= field->binlog_type() == source_type; + else + same_type= field->real_type() == source_type; + /* If the real type is the same, we need to check the metadata to decide if conversions are allowed. */ - if (field->real_type() == source_type) + if (same_type) { if (metadata == 0) // Metadata can only be zero if no metadata was provided { @@ -731,18 +763,22 @@ can_convert_field_to(Field *field, case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: switch (field->real_type()) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: *order_var= compare_lengths(field, source_type, metadata); /* Here we know that the types are different, so if the order @@ -1036,6 +1072,7 @@ table_def::table_def(unsigned char *types, ulong size, switch (binlog_type(i)) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_DOUBLE: @@ -1066,6 +1103,7 @@ table_def::table_def(unsigned char *types, ulong size, break; } case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: { /* These types store two bytes. @@ -1127,7 +1165,7 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, enum enum_binlog_che if (event_buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS int8 fd_alg= event_buf[event_len - BINLOG_CHECKSUM_LEN - BINLOG_CHECKSUM_ALG_DESC_LEN]; #endif diff --git a/sql/scheduler.cc b/sql/scheduler.cc index de472ae2504..818bb3818f4 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -22,6 +22,7 @@ #pragma implementation #endif +#include "mariadb.h" #include "mysqld.h" #include "sql_connect.h" // init_new_connection_handler_thread #include "scheduler.h" diff --git a/sql/scheduler.h b/sql/scheduler.h index 71553372999..b067763d9b4 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -25,8 +25,6 @@ #pragma interface #endif -#include <my_global.h> - class THD; /* Functions used when manipulating threads */ diff --git a/sql/semisync.cc b/sql/semisync.cc new file mode 100644 index 00000000000..a8a11f091db --- /dev/null +++ b/sql/semisync.cc @@ -0,0 +1,32 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (C) 2008 MySQL AB + Use is subject to license terms + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include <my_global.h> +#include "semisync.h" + +const unsigned char Repl_semi_sync_base::k_packet_magic_num= 0xef; +const unsigned char Repl_semi_sync_base::k_packet_flag_sync= 0x01; + + +const unsigned long Trace::k_trace_general= 0x0001; +const unsigned long Trace::k_trace_detail= 0x0010; +const unsigned long Trace::k_trace_net_wait= 0x0020; +const unsigned long Trace::k_trace_function= 0x0040; + +const unsigned char Repl_semi_sync_base::k_sync_header[2]= + {Repl_semi_sync_base::k_packet_magic_num, 0}; diff --git a/sql/semisync.h b/sql/semisync.h new file mode 100644 index 00000000000..9deb6c5fd01 --- /dev/null +++ b/sql/semisync.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (C) 2008 MySQL AB + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef SEMISYNC_H +#define SEMISYNC_H + +#include "mysqld.h" +#include "log_event.h" +#include "replication.h" + +/** + This class is used to trace function calls and other process + information +*/ +class Trace { +public: + static const unsigned long k_trace_function; + static const unsigned long k_trace_general; + static const unsigned long k_trace_detail; + static const unsigned long k_trace_net_wait; + + unsigned long m_trace_level; /* the level for tracing */ + + Trace() + :m_trace_level(0L) + {} + Trace(unsigned long trace_level) + :m_trace_level(trace_level) + {} +}; + +/** + Base class for semi-sync master and slave classes +*/ +class Repl_semi_sync_base + :public Trace { +public: + static const unsigned char k_sync_header[2]; /* three byte packet header */ + + /* Constants in network packet header. */ + static const unsigned char k_packet_magic_num; + static const unsigned char k_packet_flag_sync; +}; + +/* The layout of a semisync slave reply packet: + 1 byte for the magic num + 8 bytes for the binlog positon + n bytes for the binlog filename, terminated with a '\0' +*/ +#define REPLY_MAGIC_NUM_LEN 1 +#define REPLY_BINLOG_POS_LEN 8 +#define REPLY_BINLOG_NAME_LEN (FN_REFLEN + 1) +#define REPLY_MAGIC_NUM_OFFSET 0 +#define REPLY_BINLOG_POS_OFFSET (REPLY_MAGIC_NUM_OFFSET + REPLY_MAGIC_NUM_LEN) +#define REPLY_BINLOG_NAME_OFFSET (REPLY_BINLOG_POS_OFFSET + REPLY_BINLOG_POS_LEN) +#define REPLY_MESSAGE_MAX_LENGTH \ + (REPLY_MAGIC_NUM_LEN + REPLY_BINLOG_POS_LEN + REPLY_BINLOG_NAME_LEN) + +#endif /* SEMISYNC_H */ diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc new file mode 100644 index 00000000000..99d8b75ece8 --- /dev/null +++ b/sql/semisync_master.cc @@ -0,0 +1,1352 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (c) 2008, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2016, 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include <my_global.h> +#include "semisync_master.h" + +#define TIME_THOUSAND 1000 +#define TIME_MILLION 1000000 +#define TIME_BILLION 1000000000 + +/* This indicates whether semi-synchronous replication is enabled. */ +my_bool rpl_semi_sync_master_enabled= 0; +unsigned long long rpl_semi_sync_master_request_ack = 0; +unsigned long long rpl_semi_sync_master_get_ack = 0; +my_bool rpl_semi_sync_master_wait_no_slave = 1; +my_bool rpl_semi_sync_master_status = 0; +ulong rpl_semi_sync_master_wait_point = + SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT; +ulong rpl_semi_sync_master_timeout; +ulong rpl_semi_sync_master_trace_level; +ulong rpl_semi_sync_master_yes_transactions = 0; +ulong rpl_semi_sync_master_no_transactions = 0; +ulong rpl_semi_sync_master_off_times = 0; +ulong rpl_semi_sync_master_timefunc_fails = 0; +ulong rpl_semi_sync_master_wait_timeouts = 0; +ulong rpl_semi_sync_master_wait_sessions = 0; +ulong rpl_semi_sync_master_wait_pos_backtraverse = 0; +ulong rpl_semi_sync_master_avg_trx_wait_time = 0; +ulonglong rpl_semi_sync_master_trx_wait_num = 0; +ulong rpl_semi_sync_master_avg_net_wait_time = 0; +ulonglong rpl_semi_sync_master_net_wait_num = 0; +ulong rpl_semi_sync_master_clients = 0; +ulonglong rpl_semi_sync_master_net_wait_time = 0; +ulonglong rpl_semi_sync_master_trx_wait_time = 0; + +Repl_semi_sync_master repl_semisync_master; +Ack_receiver ack_receiver; + +/* + structure to save transaction log filename and position +*/ +typedef struct Trans_binlog_info { + my_off_t log_pos; + char log_file[FN_REFLEN]; +} Trans_binlog_info; + +static int get_wait_time(const struct timespec& start_ts); + +static ulonglong timespec_to_usec(const struct timespec *ts) +{ + return (ulonglong) ts->tv_sec * TIME_MILLION + ts->tv_nsec / TIME_THOUSAND; +} + +/******************************************************************************* + * + * <Active_tranx> class : manage all active transaction nodes + * + ******************************************************************************/ + +Active_tranx::Active_tranx(mysql_mutex_t *lock, + ulong trace_level) + : Trace(trace_level), m_allocator(max_connections), + m_num_entries(max_connections << 1), /* Transaction hash table size + * is set to double the size + * of max_connections */ + m_lock(lock) +{ + /* No transactions are in the list initially. */ + m_trx_front = NULL; + m_trx_rear = NULL; + + /* Create the hash table to find a transaction's ending event. */ + m_trx_htb = new Tranx_node *[m_num_entries]; + for (int idx = 0; idx < m_num_entries; ++idx) + m_trx_htb[idx] = NULL; + + sql_print_information("Semi-sync replication initialized for transactions."); +} + +Active_tranx::~Active_tranx() +{ + delete [] m_trx_htb; + m_trx_htb = NULL; + m_num_entries = 0; +} + +unsigned int Active_tranx::calc_hash(const unsigned char *key, + unsigned int length) +{ + unsigned int nr = 1, nr2 = 4; + + /* The hash implementation comes from calc_hashnr() in mysys/hash.c. */ + while (length--) + { + nr ^= (((nr & 63)+nr2)*((unsigned int) (unsigned char) *key++))+ (nr << 8); + nr2 += 3; + } + return((unsigned int) nr); +} + +unsigned int Active_tranx::get_hash_value(const char *log_file_name, + my_off_t log_file_pos) +{ + unsigned int hash1 = calc_hash((const unsigned char *)log_file_name, + strlen(log_file_name)); + unsigned int hash2 = calc_hash((const unsigned char *)(&log_file_pos), + sizeof(log_file_pos)); + + return (hash1 + hash2) % m_num_entries; +} + +int Active_tranx::compare(const char *log_file_name1, my_off_t log_file_pos1, + const char *log_file_name2, my_off_t log_file_pos2) +{ + int cmp = strcmp(log_file_name1, log_file_name2); + + if (cmp != 0) + return cmp; + + if (log_file_pos1 > log_file_pos2) + return 1; + else if (log_file_pos1 < log_file_pos2) + return -1; + return 0; +} + +int Active_tranx::insert_tranx_node(const char *log_file_name, + my_off_t log_file_pos) +{ + Tranx_node *ins_node; + int result = 0; + unsigned int hash_val; + + DBUG_ENTER("Active_tranx:insert_tranx_node"); + + ins_node = m_allocator.allocate_node(); + if (!ins_node) + { + sql_print_error("%s: transaction node allocation failed for: (%s, %lu)", + "Active_tranx:insert_tranx_node", + log_file_name, (ulong)log_file_pos); + result = -1; + goto l_end; + } + + /* insert the binlog position in the active transaction list. */ + strncpy(ins_node->log_name, log_file_name, FN_REFLEN-1); + ins_node->log_name[FN_REFLEN-1] = 0; /* make sure it ends properly */ + ins_node->log_pos = log_file_pos; + + if (!m_trx_front) + { + /* The list is empty. */ + m_trx_front = m_trx_rear = ins_node; + } + else + { + int cmp = compare(ins_node, m_trx_rear); + if (cmp > 0) + { + /* Compare with the tail first. If the transaction happens later in + * binlog, then make it the new tail. + */ + m_trx_rear->next = ins_node; + m_trx_rear = ins_node; + } + else + { + /* Otherwise, it is an error because the transaction should hold the + * mysql_bin_log.LOCK_log when appending events. + */ + sql_print_error("%s: binlog write out-of-order, tail (%s, %lu), " + "new node (%s, %lu)", "Active_tranx:insert_tranx_node", + m_trx_rear->log_name, (ulong)m_trx_rear->log_pos, + ins_node->log_name, (ulong)ins_node->log_pos); + result = -1; + goto l_end; + } + } + + hash_val = get_hash_value(ins_node->log_name, ins_node->log_pos); + ins_node->hash_next = m_trx_htb[hash_val]; + m_trx_htb[hash_val] = ins_node; + + DBUG_PRINT("semisync", ("%s: insert (%s, %lu) in entry(%u)", + "Active_tranx:insert_tranx_node", + ins_node->log_name, (ulong)ins_node->log_pos, + hash_val)); + l_end: + + DBUG_RETURN(result); +} + +bool Active_tranx::is_tranx_end_pos(const char *log_file_name, + my_off_t log_file_pos) +{ + DBUG_ENTER("Active_tranx::is_tranx_end_pos"); + + unsigned int hash_val = get_hash_value(log_file_name, log_file_pos); + Tranx_node *entry = m_trx_htb[hash_val]; + + while (entry != NULL) + { + if (compare(entry, log_file_name, log_file_pos) == 0) + break; + + entry = entry->hash_next; + } + + DBUG_PRINT("semisync", ("%s: probe (%s, %lu) in entry(%u)", + "Active_tranx::is_tranx_end_pos", + log_file_name, (ulong)log_file_pos, hash_val)); + + DBUG_RETURN(entry != NULL); +} + +int Active_tranx::clear_active_tranx_nodes(const char *log_file_name, + my_off_t log_file_pos) +{ + Tranx_node *new_front; + + DBUG_ENTER("Active_tranx::::clear_active_tranx_nodes"); + + if (log_file_name != NULL) + { + new_front = m_trx_front; + + while (new_front) + { + if (compare(new_front, log_file_name, log_file_pos) > 0) + break; + new_front = new_front->next; + } + } + else + { + /* If log_file_name is NULL, clear everything. */ + new_front = NULL; + } + + if (new_front == NULL) + { + /* No active transaction nodes after the call. */ + + /* Clear the hash table. */ + memset(m_trx_htb, 0, m_num_entries * sizeof(Tranx_node *)); + m_allocator.free_all_nodes(); + + /* Clear the active transaction list. */ + if (m_trx_front != NULL) + { + m_trx_front = NULL; + m_trx_rear = NULL; + } + + DBUG_PRINT("semisync", ("%s: cleared all nodes", + "Active_tranx::::clear_active_tranx_nodes")); + } + else if (new_front != m_trx_front) + { + Tranx_node *curr_node, *next_node; + + /* Delete all transaction nodes before the confirmation point. */ + int n_frees = 0; + curr_node = m_trx_front; + while (curr_node != new_front) + { + next_node = curr_node->next; + n_frees++; + + /* Remove the node from the hash table. */ + unsigned int hash_val = get_hash_value(curr_node->log_name, curr_node->log_pos); + Tranx_node **hash_ptr = &(m_trx_htb[hash_val]); + while ((*hash_ptr) != NULL) + { + if ((*hash_ptr) == curr_node) + { + (*hash_ptr) = curr_node->hash_next; + break; + } + hash_ptr = &((*hash_ptr)->hash_next); + } + + curr_node = next_node; + } + + m_trx_front = new_front; + m_allocator.free_nodes_before(m_trx_front); + + DBUG_PRINT("semisync", ("%s: cleared %d nodes back until pos (%s, %lu)", + "Active_tranx::::clear_active_tranx_nodes", + n_frees, + m_trx_front->log_name, (ulong)m_trx_front->log_pos)); + } + + DBUG_RETURN(0); +} + + +/******************************************************************************* + * + * <Repl_semi_sync_master> class: the basic code layer for syncsync master. + * <Repl_semi_sync_slave> class: the basic code layer for syncsync slave. + * + * The most important functions during semi-syn replication listed: + * + * Master: + * . report_reply_binlog(): called by the binlog dump thread when it receives + * the slave's status information. + * . update_sync_header(): based on transaction waiting information, decide + * whether to request the slave to reply. + * . write_tranx_in_binlog(): called by the transaction thread when it finishes + * writing all transaction events in binlog. + * . commit_trx(): transaction thread wait for the slave reply. + * + * Slave: + * . slave_read_sync_header(): read the semi-sync header from the master, get + * the sync status and get the payload for events. + * . slave_reply(): reply to the master about the replication progress. + * + ******************************************************************************/ + +Repl_semi_sync_master::Repl_semi_sync_master() + : m_active_tranxs(NULL), + m_init_done(false), + m_reply_file_name_inited(false), + m_reply_file_pos(0L), + m_wait_file_name_inited(false), + m_wait_file_pos(0), + m_master_enabled(false), + m_wait_timeout(0L), + m_state(0), + m_wait_point(0) +{ + strcpy(m_reply_file_name, ""); + strcpy(m_wait_file_name, ""); +} + +int Repl_semi_sync_master::init_object() +{ + int result; + + m_init_done = true; + + /* References to the parameter works after set_options(). */ + set_wait_timeout(rpl_semi_sync_master_timeout); + set_trace_level(rpl_semi_sync_master_trace_level); + set_wait_point(rpl_semi_sync_master_wait_point); + + /* Mutex initialization can only be done after MY_INIT(). */ + mysql_mutex_init(key_LOCK_binlog, + &LOCK_binlog, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_COND_binlog_send, + &COND_binlog_send, NULL); + + if (rpl_semi_sync_master_enabled) + { + result = enable_master(); + if (!result) + result= ack_receiver.start(); /* Start the ACK thread. */ + } + else + { + result = disable_master(); + } + + /* + If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily + switch off semisync to avoid hang if there's none active slave. + */ + if (!rpl_semi_sync_master_wait_no_slave) + switch_off(); + + return result; +} + +int Repl_semi_sync_master::enable_master() +{ + int result = 0; + + /* Must have the lock when we do enable of disable. */ + lock(); + + if (!get_master_enabled()) + { + m_active_tranxs = new Active_tranx(&LOCK_binlog, m_trace_level); + if (m_active_tranxs != NULL) + { + m_commit_file_name_inited = false; + m_reply_file_name_inited = false; + m_wait_file_name_inited = false; + + set_master_enabled(true); + m_state = true; + sql_print_information("Semi-sync replication enabled on the master."); + } + else + { + sql_print_error("Cannot allocate memory to enable semi-sync on the master."); + result = -1; + } + } + + unlock(); + + return result; +} + +int Repl_semi_sync_master::disable_master() +{ + /* Must have the lock when we do enable of disable. */ + lock(); + + if (get_master_enabled()) + { + /* Switch off the semi-sync first so that waiting transaction will be + * waken up. + */ + switch_off(); + + assert(m_active_tranxs != NULL); + delete m_active_tranxs; + m_active_tranxs = NULL; + + m_reply_file_name_inited = false; + m_wait_file_name_inited = false; + m_commit_file_name_inited = false; + + set_master_enabled(false); + sql_print_information("Semi-sync replication disabled on the master."); + } + + unlock(); + + return 0; +} + +void Repl_semi_sync_master::cleanup() +{ + if (m_init_done) + { + mysql_mutex_destroy(&LOCK_binlog); + mysql_cond_destroy(&COND_binlog_send); + m_init_done= 0; + } + + delete m_active_tranxs; +} + +void Repl_semi_sync_master::lock() +{ + mysql_mutex_lock(&LOCK_binlog); +} + +void Repl_semi_sync_master::unlock() +{ + mysql_mutex_unlock(&LOCK_binlog); +} + +void Repl_semi_sync_master::cond_broadcast() +{ + mysql_cond_broadcast(&COND_binlog_send); +} + +int Repl_semi_sync_master::cond_timewait(struct timespec *wait_time) +{ + int wait_res; + + DBUG_ENTER("Repl_semi_sync_master::cond_timewait()"); + + wait_res= mysql_cond_timedwait(&COND_binlog_send, + &LOCK_binlog, wait_time); + + DBUG_RETURN(wait_res); +} + +void Repl_semi_sync_master::add_slave() +{ + lock(); + rpl_semi_sync_master_clients++; + unlock(); +} + +void Repl_semi_sync_master::remove_slave() +{ + lock(); + rpl_semi_sync_master_clients--; + + /* Only switch off if semi-sync is enabled and is on */ + if (get_master_enabled() && is_on()) + { + /* If user has chosen not to wait if no semi-sync slave available + and the last semi-sync slave exits, turn off semi-sync on master + immediately. + */ + if (!rpl_semi_sync_master_wait_no_slave && + rpl_semi_sync_master_clients == 0) + switch_off(); + } + unlock(); +} + +int Repl_semi_sync_master::report_reply_packet(uint32 server_id, + const uchar *packet, + ulong packet_len) +{ + int result= -1; + char log_file_name[FN_REFLEN+1]; + my_off_t log_file_pos; + ulong log_file_len = 0; + + DBUG_ENTER("Repl_semi_sync_master::report_reply_packet"); + + if (unlikely(packet[REPLY_MAGIC_NUM_OFFSET] != + Repl_semi_sync_master::k_packet_magic_num)) + { + sql_print_error("Read semi-sync reply magic number error"); + goto l_end; + } + + if (unlikely(packet_len < REPLY_BINLOG_NAME_OFFSET)) + { + sql_print_error("Read semi-sync reply length error: packet is too small"); + goto l_end; + } + + log_file_pos = uint8korr(packet + REPLY_BINLOG_POS_OFFSET); + log_file_len = packet_len - REPLY_BINLOG_NAME_OFFSET; + if (unlikely(log_file_len >= FN_REFLEN)) + { + sql_print_error("Read semi-sync reply binlog file length too large"); + goto l_end; + } + strncpy(log_file_name, (const char*)packet + REPLY_BINLOG_NAME_OFFSET, log_file_len); + log_file_name[log_file_len] = 0; + + DBUG_ASSERT(dirname_length(log_file_name) == 0); + + DBUG_PRINT("semisync", ("%s: Got reply(%s, %lu) from server %u", + "Repl_semi_sync_master::report_reply_packet", + log_file_name, (ulong)log_file_pos, server_id)); + + rpl_semi_sync_master_get_ack++; + report_reply_binlog(server_id, log_file_name, log_file_pos); + +l_end: + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::report_reply_binlog(uint32 server_id, + const char *log_file_name, + my_off_t log_file_pos) +{ + int cmp; + bool can_release_threads = false; + bool need_copy_send_pos = true; + + DBUG_ENTER("Repl_semi_sync_master::report_reply_binlog"); + + if (!(get_master_enabled())) + DBUG_RETURN(0); + + lock(); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled()) + goto l_end; + + if (!is_on()) + /* We check to see whether we can switch semi-sync ON. */ + try_switch_on(server_id, log_file_name, log_file_pos); + + /* The position should increase monotonically, if there is only one + * thread sending the binlog to the slave. + * In reality, to improve the transaction availability, we allow multiple + * sync replication slaves. So, if any one of them get the transaction, + * the transaction session in the primary can move forward. + */ + if (m_reply_file_name_inited) + { + cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_reply_file_name, m_reply_file_pos); + + /* If the requested position is behind the sending binlog position, + * would not adjust sending binlog position. + * We based on the assumption that there are multiple semi-sync slave, + * and at least one of them shou/ld be up to date. + * If all semi-sync slaves are behind, at least initially, the primary + * can find the situation after the waiting timeout. After that, some + * slaves should catch up quickly. + */ + if (cmp < 0) + { + /* If the position is behind, do not copy it. */ + need_copy_send_pos = false; + } + } + + if (need_copy_send_pos) + { + strmake_buf(m_reply_file_name, log_file_name); + m_reply_file_pos = log_file_pos; + m_reply_file_name_inited = true; + + /* Remove all active transaction nodes before this point. */ + assert(m_active_tranxs != NULL); + m_active_tranxs->clear_active_tranx_nodes(log_file_name, log_file_pos); + + DBUG_PRINT("semisync", ("%s: Got reply at (%s, %lu)", + "Repl_semi_sync_master::report_reply_binlog", + log_file_name, (ulong)log_file_pos)); + } + + if (rpl_semi_sync_master_wait_sessions > 0) + { + /* Let us check if some of the waiting threads doing a trx + * commit can now proceed. + */ + cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos, + m_wait_file_name, m_wait_file_pos); + if (cmp >= 0) + { + /* Yes, at least one waiting thread can now proceed: + * let us release all waiting threads with a broadcast + */ + can_release_threads = true; + m_wait_file_name_inited = false; + } + } + + l_end: + unlock(); + + if (can_release_threads) + { + DBUG_PRINT("semisync", ("%s: signal all waiting threads.", + "Repl_semi_sync_master::report_reply_binlog")); + + cond_broadcast(); + } + + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::wait_after_sync(const char *log_file, my_off_t log_pos) +{ + if (!get_master_enabled()) + return 0; + + int ret= 0; + if(log_pos && + wait_point() == SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC) + ret= commit_trx(log_file + dirname_length(log_file), log_pos); + + return ret; +} + +int Repl_semi_sync_master::wait_after_commit(THD* thd, bool all) +{ + if (!get_master_enabled()) + return 0; + + int ret= 0; + const char *log_file; + my_off_t log_pos; + + bool is_real_trans= + (all || thd->transaction.all.ha_list == 0); + /* + The coordinates are propagated to this point having been computed + in report_binlog_update + */ + Trans_binlog_info *log_info= thd->semisync_info; + log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; + log_pos= log_info ? log_info->log_pos : 0; + + DBUG_ASSERT(!log_file || dirname_length(log_file) == 0); + + if (is_real_trans && + log_pos && + wait_point() == SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT) + ret= commit_trx(log_file, log_pos); + + if (is_real_trans && log_info) + { + log_info->log_file[0]= 0; + log_info->log_pos= 0; + } + + return ret; +} + +int Repl_semi_sync_master::wait_after_rollback(THD *thd, bool all) +{ + return wait_after_commit(thd, all); +} + +/** + The method runs after flush to binary log is done. +*/ +int Repl_semi_sync_master::report_binlog_update(THD* thd, const char *log_file, + my_off_t log_pos) +{ + if (get_master_enabled()) + { + Trans_binlog_info *log_info; + + if (!(log_info= thd->semisync_info)) + { + if(!(log_info= + (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0)))) + return 1; + thd->semisync_info= log_info; + } + strcpy(log_info->log_file, log_file + dirname_length(log_file)); + log_info->log_pos = log_pos; + + return write_tranx_in_binlog(log_info->log_file, log_pos); + } + + return 0; +} + +int Repl_semi_sync_master::dump_start(THD* thd, + const char *log_file, + my_off_t log_pos) +{ + if (!thd->semi_sync_slave) + return 0; + + if (ack_receiver.add_slave(thd)) + { + sql_print_error("Failed to register slave to semi-sync ACK receiver " + "thread. Turning off semisync"); + thd->semi_sync_slave= 0; + return 1; + } + + add_slave(); + report_reply_binlog(thd->variables.server_id, + log_file + dirname_length(log_file), log_pos); + sql_print_information("Start semi-sync binlog_dump to slave (server_id: %d), " + "pos(%s, %lu", + thd->variables.server_id, log_file, + (unsigned long)log_pos); + + return 0; +} + +void Repl_semi_sync_master::dump_end(THD* thd) +{ + if (!thd->semi_sync_slave) + return; + + sql_print_information("Stop semi-sync binlog_dump to slave (server_id: %d)", thd->variables.server_id); + + remove_slave(); + ack_receiver.remove_slave(thd); + + return; +} + +int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, + my_off_t trx_wait_binlog_pos) +{ + + DBUG_ENTER("Repl_semi_sync_master::commit_trx"); + + if (get_master_enabled() && trx_wait_binlog_name) + { + struct timespec start_ts; + struct timespec abstime; + int wait_result; + PSI_stage_info old_stage; + + set_timespec(start_ts, 0); + + DEBUG_SYNC(current_thd, "rpl_semisync_master_commit_trx_before_lock"); + /* Acquire the mutex. */ + lock(); + + /* This must be called after acquired the lock */ + THD_ENTER_COND(NULL, &COND_binlog_send, &LOCK_binlog, + & stage_waiting_for_semi_sync_ack_from_slave, + & old_stage); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled() || !is_on()) + goto l_end; + + DBUG_PRINT("semisync", ("%s: wait pos (%s, %lu), repl(%d)\n", + "Repl_semi_sync_master::commit_trx", + trx_wait_binlog_name, (ulong)trx_wait_binlog_pos, + (int)is_on())); + + while (is_on() && !thd_killed(current_thd)) + { + if (m_reply_file_name_inited) + { + int cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos, + trx_wait_binlog_name, + trx_wait_binlog_pos); + if (cmp >= 0) + { + /* We have already sent the relevant binlog to the slave: no need to + * wait here. + */ + DBUG_PRINT("semisync", ("%s: Binlog reply is ahead (%s, %lu),", + "Repl_semi_sync_master::commit_trx", + m_reply_file_name, + (ulong)m_reply_file_pos)); + break; + } + } + + /* Let us update the info about the minimum binlog position of waiting + * threads. + */ + if (m_wait_file_name_inited) + { + int cmp = Active_tranx::compare(trx_wait_binlog_name, + trx_wait_binlog_pos, + m_wait_file_name, m_wait_file_pos); + if (cmp <= 0) + { + /* This thd has a lower position, let's update the minimum info. */ + strmake_buf(m_wait_file_name, trx_wait_binlog_name); + m_wait_file_pos = trx_wait_binlog_pos; + + rpl_semi_sync_master_wait_pos_backtraverse++; + DBUG_PRINT("semisync", ("%s: move back wait position (%s, %lu),", + "Repl_semi_sync_master::commit_trx", + m_wait_file_name, (ulong)m_wait_file_pos)); + } + } + else + { + strmake_buf(m_wait_file_name, trx_wait_binlog_name); + m_wait_file_pos = trx_wait_binlog_pos; + m_wait_file_name_inited = true; + + DBUG_PRINT("semisync", ("%s: init wait position (%s, %lu),", + "Repl_semi_sync_master::commit_trx", + m_wait_file_name, (ulong)m_wait_file_pos)); + } + + /* Calcuate the waiting period. */ + long diff_secs = (long) (m_wait_timeout / TIME_THOUSAND); + long diff_nsecs = (long) ((m_wait_timeout % TIME_THOUSAND) * TIME_MILLION); + long nsecs = start_ts.tv_nsec + diff_nsecs; + abstime.tv_sec = start_ts.tv_sec + diff_secs + nsecs/TIME_BILLION; + abstime.tv_nsec = nsecs % TIME_BILLION; + + /* In semi-synchronous replication, we wait until the binlog-dump + * thread has received the reply on the relevant binlog segment from the + * replication slave. + * + * Let us suspend this thread to wait on the condition; + * when replication has progressed far enough, we will release + * these waiting threads. + */ + rpl_semi_sync_master_wait_sessions++; + + DBUG_PRINT("semisync", ("%s: wait %lu ms for binlog sent (%s, %lu)", + "Repl_semi_sync_master::commit_trx", + m_wait_timeout, + m_wait_file_name, (ulong)m_wait_file_pos)); + + wait_result = cond_timewait(&abstime); + rpl_semi_sync_master_wait_sessions--; + + if (wait_result != 0) + { + /* This is a real wait timeout. */ + sql_print_warning("Timeout waiting for reply of binlog (file: %s, pos: %lu), " + "semi-sync up to file %s, position %lu.", + trx_wait_binlog_name, (ulong)trx_wait_binlog_pos, + m_reply_file_name, (ulong)m_reply_file_pos); + rpl_semi_sync_master_wait_timeouts++; + + /* switch semi-sync off */ + switch_off(); + } + else + { + int wait_time; + + wait_time = get_wait_time(start_ts); + if (wait_time < 0) + { + DBUG_PRINT("semisync", ("Replication semi-sync getWaitTime fail at " + "wait position (%s, %lu)", + trx_wait_binlog_name, + (ulong)trx_wait_binlog_pos)); + rpl_semi_sync_master_timefunc_fails++; + } + else + { + rpl_semi_sync_master_trx_wait_num++; + rpl_semi_sync_master_trx_wait_time += wait_time; + } + } + } + + /* + At this point, the binlog file and position of this transaction + must have been removed from Active_tranx. + m_active_tranxs may be NULL if someone disabled semi sync during + cond_timewait() + */ + assert(thd_killed(current_thd) || !m_active_tranxs || + !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name, + trx_wait_binlog_pos)); + + l_end: + /* Update the status counter. */ + if (is_on()) + rpl_semi_sync_master_yes_transactions++; + else + rpl_semi_sync_master_no_transactions++; + + /* The lock held will be released by thd_exit_cond, so no need to + call unlock() here */ + THD_EXIT_COND(NULL, & old_stage); + } + + DBUG_RETURN(0); +} + +/* Indicate that semi-sync replication is OFF now. + * + * What should we do when it is disabled? The problem is that we want + * the semi-sync replication enabled again when the slave catches up + * later. But, it is not that easy to detect that the slave has caught + * up. This is caused by the fact that MySQL's replication protocol is + * asynchronous, meaning that if the master does not use the semi-sync + * protocol, the slave would not send anything to the master. + * Still, if the master is sending (N+1)-th event, we assume that it is + * an indicator that the slave has received N-th event and earlier ones. + * + * If semi-sync is disabled, all transactions still update the wait + * position with the last position in binlog. But no transactions will + * wait for confirmations and the active transaction list would not be + * maintained. In binlog dump thread, update_sync_header() checks whether + * the current sending event catches up with last wait position. If it + * does match, semi-sync will be switched on again. + */ +int Repl_semi_sync_master::switch_off() +{ + int result; + + DBUG_ENTER("Repl_semi_sync_master::switch_off"); + + m_state = false; + + /* Clear the active transaction list. */ + assert(m_active_tranxs != NULL); + result = m_active_tranxs->clear_active_tranx_nodes(NULL, 0); + + rpl_semi_sync_master_off_times++; + m_wait_file_name_inited = false; + m_reply_file_name_inited = false; + sql_print_information("Semi-sync replication switched OFF."); + cond_broadcast(); /* wake up all waiting threads */ + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::try_switch_on(int server_id, + const char *log_file_name, + my_off_t log_file_pos) +{ + bool semi_sync_on = false; + + DBUG_ENTER("Repl_semi_sync_master::try_switch_on"); + + /* If the current sending event's position is larger than or equal to the + * 'largest' commit transaction binlog position, the slave is already + * catching up now and we can switch semi-sync on here. + * If m_commit_file_name_inited indicates there are no recent transactions, + * we can enable semi-sync immediately. + */ + if (m_commit_file_name_inited) + { + int cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_commit_file_name, m_commit_file_pos); + semi_sync_on = (cmp >= 0); + } + else + { + semi_sync_on = true; + } + + if (semi_sync_on) + { + /* Switch semi-sync replication on. */ + m_state = true; + + sql_print_information("Semi-sync replication switched ON with slave (server_id: %d) " + "at (%s, %lu)", + server_id, log_file_name, + (ulong)log_file_pos); + } + + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::reserve_sync_header(String* packet) +{ + DBUG_ENTER("Repl_semi_sync_master::reserve_sync_header"); + + /* Set the magic number and the sync status. By default, no sync + * is required. + */ + packet->append(reinterpret_cast<const char*>(k_sync_header), + sizeof(k_sync_header)); + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, + const char *log_file_name, + my_off_t log_file_pos, + bool* need_sync) +{ + int cmp = 0; + bool sync = false; + + DBUG_ENTER("Repl_semi_sync_master::update_sync_header"); + + /* If the semi-sync master is not enabled, or the slave is not a semi-sync + * target, do not request replies from the slave. + */ + if (!get_master_enabled() || !thd->semi_sync_slave) + { + *need_sync = false; + DBUG_RETURN(0); + } + + lock(); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled()) + { + assert(sync == false); + goto l_end; + } + + if (is_on()) + { + /* semi-sync is ON */ + sync = false; /* No sync unless a transaction is involved. */ + + if (m_reply_file_name_inited) + { + cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_reply_file_name, m_reply_file_pos); + if (cmp <= 0) + { + /* If we have already got the reply for the event, then we do + * not need to sync the transaction again. + */ + goto l_end; + } + } + + if (m_wait_file_name_inited) + { + cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_wait_file_name, m_wait_file_pos); + } + else + { + cmp = 1; + } + + /* If we are already waiting for some transaction replies which + * are later in binlog, do not wait for this one event. + */ + if (cmp >= 0) + { + /* + * We only wait if the event is a transaction's ending event. + */ + assert(m_active_tranxs != NULL); + sync = m_active_tranxs->is_tranx_end_pos(log_file_name, + log_file_pos); + } + } + else + { + if (m_commit_file_name_inited) + { + int cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_commit_file_name, m_commit_file_pos); + sync = (cmp >= 0); + } + else + { + sync = true; + } + } + + DBUG_PRINT("semisync", ("%s: server(%lu), (%s, %lu) sync(%d), repl(%d)", + "Repl_semi_sync_master::update_sync_header", + thd->variables.server_id, log_file_name, + (ulong)log_file_pos, sync, (int)is_on())); + *need_sync= sync; + + l_end: + unlock(); + + /* We do not need to clear sync flag because we set it to 0 when we + * reserve the packet header. + */ + if (sync) + { + (packet)[2] = k_packet_flag_sync; + } + + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::write_tranx_in_binlog(const char* log_file_name, + my_off_t log_file_pos) +{ + int result = 0; + + DBUG_ENTER("Repl_semi_sync_master::write_tranx_in_binlog"); + + lock(); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled()) + goto l_end; + + /* Update the 'largest' transaction commit position seen so far even + * though semi-sync is switched off. + * It is much better that we update m_commit_file* here, instead of + * inside commit_trx(). This is mostly because update_sync_header() + * will watch for m_commit_file* to decide whether to switch semi-sync + * on. The detailed reason is explained in function update_sync_header(). + */ + if (m_commit_file_name_inited) + { + int cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_commit_file_name, m_commit_file_pos); + if (cmp > 0) + { + /* This is a larger position, let's update the maximum info. */ + strncpy(m_commit_file_name, log_file_name, FN_REFLEN-1); + m_commit_file_name[FN_REFLEN-1] = 0; /* make sure it ends properly */ + m_commit_file_pos = log_file_pos; + } + } + else + { + strncpy(m_commit_file_name, log_file_name, FN_REFLEN-1); + m_commit_file_name[FN_REFLEN-1] = 0; /* make sure it ends properly */ + m_commit_file_pos = log_file_pos; + m_commit_file_name_inited = true; + } + + if (is_on()) + { + assert(m_active_tranxs != NULL); + if(m_active_tranxs->insert_tranx_node(log_file_name, log_file_pos)) + { + /* + if insert tranx_node failed, print a warning message + and turn off semi-sync + */ + sql_print_warning("Semi-sync failed to insert tranx_node for binlog file: %s, position: %lu", + log_file_name, (ulong)log_file_pos); + switch_off(); + } + else + { + rpl_semi_sync_master_request_ack++; + } + } + + l_end: + unlock(); + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::flush_net(THD *thd, + const char *event_buf) +{ + int result = -1; + NET* net= &thd->net; + + DBUG_ENTER("Repl_semi_sync_master::flush_net"); + + assert((unsigned char)event_buf[1] == k_packet_magic_num); + if ((unsigned char)event_buf[2] != k_packet_flag_sync) + { + /* current event does not require reply */ + result = 0; + goto l_end; + } + + /* We flush to make sure that the current event is sent to the network, + * instead of being buffered in the TCP/IP stack. + */ + if (net_flush(net)) + { + sql_print_error("Semi-sync master failed on net_flush() " + "before waiting for slave reply"); + goto l_end; + } + + net_clear(net, 0); + net->pkt_nr++; + result = 0; + rpl_semi_sync_master_net_wait_num++; + + l_end: + thd->clear_error(); + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::after_reset_master() +{ + int result = 0; + + DBUG_ENTER("Repl_semi_sync_master::after_reset_master"); + + if (rpl_semi_sync_master_enabled) + { + sql_print_information("Enable Semi-sync Master after reset master"); + enable_master(); + } + + lock(); + + if (rpl_semi_sync_master_clients == 0 && + !rpl_semi_sync_master_wait_no_slave) + m_state = 0; + else + m_state = get_master_enabled()? 1 : 0; + + m_wait_file_name_inited = false; + m_reply_file_name_inited = false; + m_commit_file_name_inited = false; + + rpl_semi_sync_master_yes_transactions = 0; + rpl_semi_sync_master_no_transactions = 0; + rpl_semi_sync_master_off_times = 0; + rpl_semi_sync_master_timefunc_fails = 0; + rpl_semi_sync_master_wait_sessions = 0; + rpl_semi_sync_master_wait_pos_backtraverse = 0; + rpl_semi_sync_master_trx_wait_num = 0; + rpl_semi_sync_master_trx_wait_time = 0; + rpl_semi_sync_master_net_wait_num = 0; + rpl_semi_sync_master_net_wait_time = 0; + + unlock(); + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::before_reset_master() +{ + int result = 0; + + DBUG_ENTER("Repl_semi_sync_master::before_reset_master"); + + if (rpl_semi_sync_master_enabled) + disable_master(); + + DBUG_RETURN(result); +} + +void Repl_semi_sync_master::check_and_switch() +{ + lock(); + if (get_master_enabled() && is_on()) + { + if (!rpl_semi_sync_master_wait_no_slave + && rpl_semi_sync_master_clients == 0) + switch_off(); + } + unlock(); +} + +void Repl_semi_sync_master::set_export_stats() +{ + lock(); + + rpl_semi_sync_master_status = m_state; + rpl_semi_sync_master_avg_trx_wait_time= + ((rpl_semi_sync_master_trx_wait_num) ? + (ulong)((double)rpl_semi_sync_master_trx_wait_time / + ((double)rpl_semi_sync_master_trx_wait_num)) : 0); + rpl_semi_sync_master_avg_net_wait_time= + ((rpl_semi_sync_master_net_wait_num) ? + (ulong)((double)rpl_semi_sync_master_net_wait_time / + ((double)rpl_semi_sync_master_net_wait_num)) : 0); + + unlock(); +} + +/* Get the waiting time given the wait's staring time. + * + * Return: + * >= 0: the waiting time in microsecons(us) + * < 0: error in get time or time back traverse + */ +static int get_wait_time(const struct timespec& start_ts) +{ + ulonglong start_usecs, end_usecs; + struct timespec end_ts; + + /* Starting time in microseconds(us). */ + start_usecs = timespec_to_usec(&start_ts); + + /* Get the wait time interval. */ + set_timespec(end_ts, 0); + + /* Ending time in microseconds(us). */ + end_usecs = timespec_to_usec(&end_ts); + + if (end_usecs < start_usecs) + return -1; + + return (int)(end_usecs - start_usecs); +} + +void semi_sync_master_deinit() +{ + repl_semisync_master.cleanup(); + ack_receiver.cleanup(); +} diff --git a/sql/semisync_master.h b/sql/semisync_master.h new file mode 100644 index 00000000000..a58c1a7ae6e --- /dev/null +++ b/sql/semisync_master.h @@ -0,0 +1,674 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef SEMISYNC_MASTER_H +#define SEMISYNC_MASTER_H + +#include "semisync.h" +#include "semisync_master_ack_receiver.h" + +#ifdef HAVE_PSI_INTERFACE +extern PSI_mutex_key key_LOCK_binlog; +extern PSI_cond_key key_COND_binlog_send; +#endif + +struct Tranx_node { + char log_name[FN_REFLEN]; + my_off_t log_pos; + struct Tranx_node *next; /* the next node in the sorted list */ + struct Tranx_node *hash_next; /* the next node during hash collision */ +}; + +/** + @class Tranx_node_allocator + + This class provides memory allocating and freeing methods for + Tranx_node. The main target is performance. + + @section ALLOCATE How to allocate a node + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + The list starts up empty (ie, there is no allocated Block). + + After some nodes are freed, there probably are some free nodes before + the sequence of the allocated nodes, but we do not reuse it. It is better + to keep the allocated nodes are in the sequence, for it is more efficient + for allocating and freeing Tranx_node. + + @section FREENODE How to free nodes + There are two methods for freeing nodes. They are free_all_nodes and + free_nodes_before. + + 'A Block is free' means all of its nodes are free. + @subsection free_nodes_before + As all allocated nodes are in the sequence, 'Before one node' means all + nodes before given node in the same Block and all Blocks before the Block + which containing the given node. As such, all Blocks before the given one + ('node') are free Block and moved into the rear of the Block link table. + The Block containing the given 'node', however, is not. For at least the + given 'node' is still in use. This will waste at most one Block, but it is + more efficient. + */ +#define BLOCK_TRANX_NODES 16 +class Tranx_node_allocator +{ +public: + /** + @param reserved_nodes + The number of reserved Tranx_nodes. It is used to set 'reserved_blocks' + which can contain at least 'reserved_nodes' number of Tranx_nodes. When + freeing memory, we will reserve at least reserved_blocks of Blocks not + freed. + */ + Tranx_node_allocator(uint reserved_nodes) : + reserved_blocks(reserved_nodes/BLOCK_TRANX_NODES + + (reserved_nodes%BLOCK_TRANX_NODES > 1 ? 2 : 1)), + first_block(NULL), last_block(NULL), + current_block(NULL), last_node(-1), block_num(0) {} + + ~Tranx_node_allocator() + { + Block *block= first_block; + while (block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + } + + /** + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + @return Return a Tranx_node *, or NULL if an error occurred. + */ + Tranx_node *allocate_node() + { + Tranx_node *trx_node; + Block *block= current_block; + + if (last_node == BLOCK_TRANX_NODES-1) + { + current_block= current_block->next; + last_node= -1; + } + + if (current_block == NULL && allocate_block()) + { + current_block= block; + if (current_block) + last_node= BLOCK_TRANX_NODES-1; + return NULL; + } + + trx_node= &(current_block->nodes[++last_node]); + trx_node->log_name[0] = '\0'; + trx_node->log_pos= 0; + trx_node->next= 0; + trx_node->hash_next= 0; + return trx_node; + } + + /** + All nodes are freed. + + @return Return 0, or 1 if an error occurred. + */ + int free_all_nodes() + { + current_block= first_block; + last_node= -1; + free_blocks(); + return 0; + } + + /** + All Blocks before the given 'node' are free Block and moved into the rear + of the Block link table. + + @param node All nodes before 'node' will be freed + + @return Return 0, or 1 if an error occurred. + */ + int free_nodes_before(Tranx_node* node) + { + Block *block; + Block *prev_block= NULL; + + block= first_block; + while (block != current_block->next) + { + /* Find the Block containing the given node */ + if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node) + { + /* All Blocks before the given node are put into the rear */ + if (first_block != block) + { + last_block->next= first_block; + first_block= block; + last_block= prev_block; + last_block->next= NULL; + free_blocks(); + } + return 0; + } + prev_block= block; + block= block->next; + } + + /* Node does not find should never happen */ + DBUG_ASSERT(0); + return 1; + } + +private: + uint reserved_blocks; + + /** + A sequence memory which contains BLOCK_TRANX_NODES Tranx_nodes. + + BLOCK_TRANX_NODES The number of Tranx_nodes which are in a Block. + + next Every Block has a 'next' pointer which points to the next Block. + These linking Blocks constitute a Block link table. + */ + struct Block { + Block *next; + Tranx_node nodes[BLOCK_TRANX_NODES]; + }; + + /** + The 'first_block' is the head of the Block link table; + */ + Block *first_block; + /** + The 'last_block' is the rear of the Block link table; + */ + Block *last_block; + + /** + current_block always points the Block in the Block link table in + which the last allocated node is. The Blocks before it are all in use + and the Blocks after it are all free. + */ + Block *current_block; + + /** + It always points to the last node which has been allocated in the + current_block. + */ + int last_node; + + /** + How many Blocks are in the Block link table. + */ + uint block_num; + + /** + Allocate a block and then assign it to current_block. + */ + int allocate_block() + { + Block *block= (Block *)my_malloc(sizeof(Block), MYF(0)); + if (block) + { + block->next= NULL; + + if (first_block == NULL) + first_block= block; + else + last_block->next= block; + + /* New Block is always put into the rear */ + last_block= block; + /* New Block is always the current_block */ + current_block= block; + ++block_num; + return 0; + } + return 1; + } + + /** + Free a given Block. + @param block The Block will be freed. + */ + void free_block(Block *block) + { + my_free(block); + --block_num; + } + + + /** + If there are some free Blocks and the total number of the Blocks in the + Block link table is larger than the 'reserved_blocks', Some free Blocks + will be freed until the total number of the Blocks is equal to the + 'reserved_blocks' or there is only one free Block behind the + 'current_block'. + */ + void free_blocks() + { + if (current_block == NULL || current_block->next == NULL) + return; + + /* One free Block is always kept behind the current block */ + Block *block= current_block->next->next; + while (block_num > reserved_blocks && block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + current_block->next->next= block; + if (block == NULL) + last_block= current_block->next; + } +}; + +/** + This class manages memory for active transaction list. + + We record each active transaction with a Tranx_node, each session + can have only one open transaction. Because of EVENT, the total + active transaction nodes can exceed the maximum allowed + connections. +*/ +class Active_tranx + :public Trace { +private: + + Tranx_node_allocator m_allocator; + /* These two record the active transaction list in sort order. */ + Tranx_node *m_trx_front, *m_trx_rear; + + Tranx_node **m_trx_htb; /* A hash table on active transactions. */ + + int m_num_entries; /* maximum hash table entries */ + mysql_mutex_t *m_lock; /* mutex lock */ + + inline void assert_lock_owner(); + + inline unsigned int calc_hash(const unsigned char *key,unsigned int length); + unsigned int get_hash_value(const char *log_file_name, my_off_t log_file_pos); + + int compare(const char *log_file_name1, my_off_t log_file_pos1, + const Tranx_node *node2) { + return compare(log_file_name1, log_file_pos1, + node2->log_name, node2->log_pos); + } + int compare(const Tranx_node *node1, + const char *log_file_name2, my_off_t log_file_pos2) { + return compare(node1->log_name, node1->log_pos, + log_file_name2, log_file_pos2); + } + int compare(const Tranx_node *node1, const Tranx_node *node2) { + return compare(node1->log_name, node1->log_pos, + node2->log_name, node2->log_pos); + } + +public: + Active_tranx(mysql_mutex_t *lock, unsigned long trace_level); + ~Active_tranx(); + + /* Insert an active transaction node with the specified position. + * + * Return: + * 0: success; non-zero: error + */ + int insert_tranx_node(const char *log_file_name, my_off_t log_file_pos); + + /* Clear the active transaction nodes until(inclusive) the specified + * position. + * If log_file_name is NULL, everything will be cleared: the sorted + * list and the hash table will be reset to empty. + * + * Return: + * 0: success; non-zero: error + */ + int clear_active_tranx_nodes(const char *log_file_name, + my_off_t log_file_pos); + + /* Given a position, check to see whether the position is an active + * transaction's ending position by probing the hash table. + */ + bool is_tranx_end_pos(const char *log_file_name, my_off_t log_file_pos); + + /* Given two binlog positions, compare which one is bigger based on + * (file_name, file_position). + */ + static int compare(const char *log_file_name1, my_off_t log_file_pos1, + const char *log_file_name2, my_off_t log_file_pos2); + +}; + +/** + The extension class for the master of semi-synchronous replication +*/ +class Repl_semi_sync_master + :public Repl_semi_sync_base { + private: + Active_tranx *m_active_tranxs; /* active transaction list: the list will + be cleared when semi-sync switches off. */ + + /* True when init_object has been called */ + bool m_init_done; + + /* This cond variable is signaled when enough binlog has been sent to slave, + * so that a waiting trx can return the 'ok' to the client for a commit. + */ + mysql_cond_t COND_binlog_send; + + /* Mutex that protects the following state variables and the active + * transaction list. + * Under no cirumstances we can acquire mysql_bin_log.LOCK_log if we are + * already holding m_LOCK_binlog because it can cause deadlocks. + */ + mysql_mutex_t LOCK_binlog; + + /* This is set to true when m_reply_file_name contains meaningful data. */ + bool m_reply_file_name_inited; + + /* The binlog name up to which we have received replies from any slaves. */ + char m_reply_file_name[FN_REFLEN]; + + /* The position in that file up to which we have the reply from any slaves. */ + my_off_t m_reply_file_pos; + + /* This is set to true when we know the 'smallest' wait position. */ + bool m_wait_file_name_inited; + + /* NULL, or the 'smallest' filename that a transaction is waiting for + * slave replies. + */ + char m_wait_file_name[FN_REFLEN]; + + /* The smallest position in that file that a trx is waiting for: the trx + * can proceed and send an 'ok' to the client when the master has got the + * reply from the slave indicating that it already got the binlog events. + */ + my_off_t m_wait_file_pos; + + /* This is set to true when we know the 'largest' transaction commit + * position in the binlog file. + * We always maintain the position no matter whether semi-sync is switched + * on switched off. When a transaction wait timeout occurs, semi-sync will + * switch off. Binlog-dump thread can use the three fields to detect when + * slaves catch up on replication so that semi-sync can switch on again. + */ + bool m_commit_file_name_inited; + + /* The 'largest' binlog filename that a commit transaction is seeing. */ + char m_commit_file_name[FN_REFLEN]; + + /* The 'largest' position in that file that a commit transaction is seeing. */ + my_off_t m_commit_file_pos; + + /* All global variables which can be set by parameters. */ + volatile bool m_master_enabled; /* semi-sync is enabled on the master */ + unsigned long m_wait_timeout; /* timeout period(ms) during tranx wait */ + + bool m_state; /* whether semi-sync is switched */ + + /*Waiting for ACK before/after innodb commit*/ + ulong m_wait_point; + + void lock(); + void unlock(); + void cond_broadcast(); + int cond_timewait(struct timespec *wait_time); + + /* Is semi-sync replication on? */ + bool is_on() { + return (m_state); + } + + void set_master_enabled(bool enabled) { + m_master_enabled = enabled; + } + + /* Switch semi-sync off because of timeout in transaction waiting. */ + int switch_off(); + + /* Switch semi-sync on when slaves catch up. */ + int try_switch_on(int server_id, + const char *log_file_name, my_off_t log_file_pos); + + public: + Repl_semi_sync_master(); + ~Repl_semi_sync_master() {} + + void cleanup(); + + bool get_master_enabled() { + return m_master_enabled; + } + void set_trace_level(unsigned long trace_level) { + m_trace_level = trace_level; + if (m_active_tranxs) + m_active_tranxs->m_trace_level = trace_level; + } + + /* Set the transaction wait timeout period, in milliseconds. */ + void set_wait_timeout(unsigned long wait_timeout) { + m_wait_timeout = wait_timeout; + } + + /*set the ACK point, after binlog sync or after transaction commit*/ + void set_wait_point(unsigned long ack_point) + { + m_wait_point = ack_point; + } + + ulong wait_point() //no cover line + { + return m_wait_point; //no cover line + } + + /* Initialize this class after MySQL parameters are initialized. this + * function should be called once at bootstrap time. + */ + int init_object(); + + /* Enable the object to enable semi-sync replication inside the master. */ + int enable_master(); + + /* Enable the object to enable semi-sync replication inside the master. */ + int disable_master(); + + /* Add a semi-sync replication slave */ + void add_slave(); + + /* Remove a semi-sync replication slave */ + void remove_slave(); + + /* It parses a reply packet and call report_reply_binlog to handle it. */ + int report_reply_packet(uint32 server_id, const uchar *packet, + ulong packet_len); + + /* In semi-sync replication, reports up to which binlog position we have + * received replies from the slave indicating that it already get the events. + * + * Input: + * server_id - (IN) master server id number + * log_file_name - (IN) binlog file name + * end_offset - (IN) the offset in the binlog file up to which we have + * the replies from the slave + * + * Return: + * 0: success; non-zero: error + */ + int report_reply_binlog(uint32 server_id, + const char* log_file_name, + my_off_t end_offset); + + /* Commit a transaction in the final step. This function is called from + * InnoDB before returning from the low commit. If semi-sync is switch on, + * the function will wait to see whether binlog-dump thread get the reply for + * the events of the transaction. Remember that this is not a direct wait, + * instead, it waits to see whether the binlog-dump thread has reached the + * point. If the wait times out, semi-sync status will be switched off and + * all other transaction would not wait either. + * + * Input: (the transaction events' ending binlog position) + * trx_wait_binlog_name - (IN) ending position's file name + * trx_wait_binlog_pos - (IN) ending position's file offset + * + * Return: + * 0: success; non-zero: error + */ + int commit_trx(const char* trx_wait_binlog_name, + my_off_t trx_wait_binlog_pos); + + /*Wait for ACK after writing/sync binlog to file*/ + int wait_after_sync(const char* log_file, my_off_t log_pos); + + /*Wait for ACK after commting the transaction*/ + int wait_after_commit(THD* thd, bool all); + + /*Wait after the transaction is rollback*/ + int wait_after_rollback(THD *thd, bool all); + /*Store the current binlog position in m_active_tranxs. This position should + * be acked by slave*/ + int report_binlog_update(THD *thd, const char *log_file,my_off_t log_pos); + + int dump_start(THD* thd, + const char *log_file, + my_off_t log_pos); + + void dump_end(THD* thd); + + /* Reserve space in the replication event packet header: + * . slave semi-sync off: 1 byte - (0) + * . slave semi-sync on: 3 byte - (0, 0xef, 0/1} + * + * Input: + * packet - (IN) the header buffer + * + * Return: + * size of the bytes reserved for header + */ + int reserve_sync_header(String* packet); + + /* Update the sync bit in the packet header to indicate to the slave whether + * the master will wait for the reply of the event. If semi-sync is switched + * off and we detect that the slave is catching up, we switch semi-sync on. + * + * Input: + * THD - (IN) current dump thread + * packet - (IN) the packet containing the replication event + * log_file_name - (IN) the event ending position's file name + * log_file_pos - (IN) the event ending position's file offset + * need_sync - (IN) identify if flush_net is needed to call. + * server_id - (IN) master server id number + * + * Return: + * 0: success; non-zero: error + */ + int update_sync_header(THD* thd, unsigned char *packet, + const char *log_file_name, + my_off_t log_file_pos, + bool* need_sync); + + /* Called when a transaction finished writing binlog events. + * . update the 'largest' transactions' binlog event position + * . insert the ending position in the active transaction list if + * semi-sync is on + * + * Input: (the transaction events' ending binlog position) + * log_file_name - (IN) transaction ending position's file name + * log_file_pos - (IN) transaction ending position's file offset + * + * Return: + * 0: success; non-zero: error + */ + int write_tranx_in_binlog(const char* log_file_name, my_off_t log_file_pos); + + /* Read the slave's reply so that we know how much progress the slave makes + * on receive replication events. + */ + int flush_net(THD* thd, const char *event_buf); + + /* Export internal statistics for semi-sync replication. */ + void set_export_stats(); + + /* 'reset master' command is issued from the user and semi-sync need to + * go off for that. + */ + int after_reset_master(); + + /*called before reset master*/ + int before_reset_master(); + + void check_and_switch(); +}; + +enum rpl_semi_sync_master_wait_point_t { + SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC, + SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT, +}; + +extern Repl_semi_sync_master repl_semisync_master; +extern Ack_receiver ack_receiver; + +/* System and status variables for the master component */ +extern my_bool rpl_semi_sync_master_enabled; +extern my_bool rpl_semi_sync_master_status; +extern ulong rpl_semi_sync_master_wait_point; +extern ulong rpl_semi_sync_master_clients; +extern ulong rpl_semi_sync_master_timeout; +extern ulong rpl_semi_sync_master_trace_level; +extern ulong rpl_semi_sync_master_yes_transactions; +extern ulong rpl_semi_sync_master_no_transactions; +extern ulong rpl_semi_sync_master_off_times; +extern ulong rpl_semi_sync_master_wait_timeouts; +extern ulong rpl_semi_sync_master_timefunc_fails; +extern ulong rpl_semi_sync_master_num_timeouts; +extern ulong rpl_semi_sync_master_wait_sessions; +extern ulong rpl_semi_sync_master_wait_pos_backtraverse; +extern ulong rpl_semi_sync_master_avg_trx_wait_time; +extern ulong rpl_semi_sync_master_avg_net_wait_time; +extern ulonglong rpl_semi_sync_master_net_wait_num; +extern ulonglong rpl_semi_sync_master_trx_wait_num; +extern ulonglong rpl_semi_sync_master_net_wait_time; +extern ulonglong rpl_semi_sync_master_trx_wait_time; +extern unsigned long long rpl_semi_sync_master_request_ack; +extern unsigned long long rpl_semi_sync_master_get_ack; + +/* + This indicates whether we should keep waiting if no semi-sync slave + is available. + 0 : stop waiting if detected no avaialable semi-sync slave. + 1 (default) : keep waiting until timeout even no available semi-sync slave. +*/ +extern char rpl_semi_sync_master_wait_no_slave; +extern Repl_semi_sync_master repl_semisync_master; + +extern PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave; +extern PSI_stage_info stage_reading_semi_sync_ack; +extern PSI_stage_info stage_waiting_for_semi_sync_slave; + +void semi_sync_master_deinit(); + +#endif /* SEMISYNC_MASTER_H */ diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc new file mode 100644 index 00000000000..90d2190ef2c --- /dev/null +++ b/sql/semisync_master_ack_receiver.cc @@ -0,0 +1,303 @@ +/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include <my_global.h> +#include "semisync_master.h" +#include "semisync_master_ack_receiver.h" + +extern PSI_mutex_key key_LOCK_ack_receiver; +extern PSI_cond_key key_COND_ack_receiver; +extern PSI_thread_key key_thread_ack_receiver; +extern Repl_semi_sync_master repl_semisync; + +/* Callback function of ack receive thread */ +pthread_handler_t ack_receive_handler(void *arg) +{ + Ack_receiver *recv= reinterpret_cast<Ack_receiver *>(arg); + + my_thread_init(); + recv->run(); + my_thread_end(); + + return NULL; +} + +Ack_receiver::Ack_receiver() +{ + DBUG_ENTER("Ack_receiver::Ack_receiver"); + + m_status= ST_DOWN; + mysql_mutex_init(key_LOCK_ack_receiver, &m_mutex, + MY_MUTEX_INIT_FAST); + mysql_cond_init(key_COND_ack_receiver, &m_cond, NULL); + m_pid= 0; + + DBUG_VOID_RETURN; +} + +void Ack_receiver::cleanup() +{ + DBUG_ENTER("Ack_receiver::~Ack_receiver"); + + stop(); + mysql_mutex_destroy(&m_mutex); + mysql_cond_destroy(&m_cond); + + DBUG_VOID_RETURN; +} + +bool Ack_receiver::start() +{ + DBUG_ENTER("Ack_receiver::start"); + + mysql_mutex_lock(&m_mutex); + if(m_status == ST_DOWN) + { + pthread_attr_t attr; + + m_status= ST_UP; + + if (DBUG_EVALUATE_IF("rpl_semisync_simulate_create_thread_failure", 1, 0) || + pthread_attr_init(&attr) != 0 || + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) != 0 || +#ifndef _WIN32 + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0 || +#endif + mysql_thread_create(key_thread_ack_receiver, &m_pid, + &attr, ack_receive_handler, this)) + { + sql_print_error("Failed to start semi-sync ACK receiver thread, " + " could not create thread(errno:%d)", errno); + + m_status= ST_DOWN; + mysql_mutex_unlock(&m_mutex); + + DBUG_RETURN(true); + } + (void) pthread_attr_destroy(&attr); + } + mysql_mutex_unlock(&m_mutex); + + DBUG_RETURN(false); +} + +void Ack_receiver::stop() +{ + DBUG_ENTER("Ack_receiver::stop"); + + mysql_mutex_lock(&m_mutex); + if (m_status == ST_UP) + { + m_status= ST_STOPPING; + mysql_cond_broadcast(&m_cond); + + while (m_status == ST_STOPPING) + mysql_cond_wait(&m_cond, &m_mutex); + + DBUG_ASSERT(m_status == ST_DOWN); + + m_pid= 0; + } + mysql_mutex_unlock(&m_mutex); + + DBUG_VOID_RETURN; +} + +bool Ack_receiver::add_slave(THD *thd) +{ + Slave *slave; + DBUG_ENTER("Ack_receiver::add_slave"); + + if (!(slave= new Slave)) + DBUG_RETURN(true); + + slave->thd= thd; + slave->vio= *thd->net.vio; + slave->vio.mysql_socket.m_psi= NULL; + slave->vio.read_timeout= 1; + + mysql_mutex_lock(&m_mutex); + m_slaves.push_back(slave); + m_slaves_changed= true; + mysql_cond_broadcast(&m_cond); + mysql_mutex_unlock(&m_mutex); + + DBUG_RETURN(false); +} + +void Ack_receiver::remove_slave(THD *thd) +{ + I_List_iterator<Slave> it(m_slaves); + Slave *slave; + DBUG_ENTER("Ack_receiver::remove_slave"); + + mysql_mutex_lock(&m_mutex); + + while ((slave= it++)) + { + if (slave->thd == thd) + { + delete slave; + m_slaves_changed= true; + break; + } + } + mysql_mutex_unlock(&m_mutex); + + DBUG_VOID_RETURN; +} + +inline void Ack_receiver::set_stage_info(const PSI_stage_info &stage) +{ + MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__); +} + +inline void Ack_receiver::wait_for_slave_connection() +{ + set_stage_info(stage_waiting_for_semi_sync_slave); + mysql_cond_wait(&m_cond, &m_mutex); +} + +my_socket Ack_receiver::get_slave_sockets(fd_set *fds, uint *count) +{ + my_socket max_fd= INVALID_SOCKET; + Slave *slave; + I_List_iterator<Slave> it(m_slaves); + + *count= 0; + FD_ZERO(fds); + while ((slave= it++)) + { + (*count)++; + my_socket fd= slave->sock_fd(); + max_fd= (fd > max_fd ? fd : max_fd); + FD_SET(fd, fds); + } + + return max_fd; +} + +/* Auxilary function to initialize a NET object with given net buffer. */ +static void init_net(NET *net, unsigned char *buff, unsigned int buff_len) +{ + memset(net, 0, sizeof(NET)); + net->max_packet= buff_len; + net->buff= buff; + net->buff_end= buff + buff_len; + net->read_pos= net->buff; +} + +void Ack_receiver::run() +{ + // skip LOCK_global_system_variables due to the 3rd arg + THD *thd= new THD(next_thread_id(), false, true); + NET net; + unsigned char net_buff[REPLY_MESSAGE_MAX_LENGTH]; + fd_set read_fds; + my_socket max_fd= INVALID_SOCKET; + Slave *slave; + + my_thread_init(); + + DBUG_ENTER("Ack_receiver::run"); + + sql_print_information("Starting ack receiver thread"); + thd->system_thread= SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND; + thd->thread_stack= (char*) &thd; + thd->store_globals(); + thd->security_ctx->skip_grants(); + thread_safe_increment32(&service_thread_count); + thd->set_command(COM_DAEMON); + init_net(&net, net_buff, REPLY_MESSAGE_MAX_LENGTH); + + mysql_mutex_lock(&m_mutex); + m_slaves_changed= true; + mysql_mutex_unlock(&m_mutex); + + while (1) + { + fd_set fds; + int ret; + uint slave_count; + + mysql_mutex_lock(&m_mutex); + if (unlikely(m_status == ST_STOPPING)) + goto end; + + set_stage_info(stage_waiting_for_semi_sync_ack_from_slave); + if (unlikely(m_slaves_changed)) + { + if (unlikely(m_slaves.is_empty())) + { + wait_for_slave_connection(); + mysql_mutex_unlock(&m_mutex); + continue; + } + + max_fd= get_slave_sockets(&read_fds, &slave_count); + m_slaves_changed= false; + DBUG_PRINT("info", ("fd count %u, max_fd %d", slave_count, max_fd)); + } + + struct timeval tv= {1, 0}; + fds= read_fds; + /* select requires max fd + 1 for the first argument */ + ret= select((int)(max_fd+1), &fds, NULL, NULL, &tv); + if (ret <= 0) + { + mysql_mutex_unlock(&m_mutex); + + ret= DBUG_EVALUATE_IF("rpl_semisync_simulate_select_error", -1, ret); + + if (ret == -1) + sql_print_information("Failed to select() on semi-sync dump sockets, " + "error: errno=%d", socket_errno); + /* Sleep 1us, so other threads can catch the m_mutex easily. */ + my_sleep(1); + continue; + } + + set_stage_info(stage_reading_semi_sync_ack); + I_List_iterator<Slave> it(m_slaves); + + while ((slave= it++)) + { + if (FD_ISSET(slave->sock_fd(), &fds)) + { + ulong len; + + net_clear(&net, 0); + net.vio= &slave->vio; + + len= my_net_read(&net); + if (likely(len != packet_error)) + repl_semisync_master.report_reply_packet(slave->server_id(), + net.read_pos, len); + else if (net.last_errno == ER_NET_READ_ERROR) + FD_CLR(slave->sock_fd(), &read_fds); + } + } + mysql_mutex_unlock(&m_mutex); + } +end: + sql_print_information("Stopping ack receiver thread"); + m_status= ST_DOWN; + delete thd; + thread_safe_decrement32(&service_thread_count); + signal_thd_deleted(); + mysql_cond_broadcast(&m_cond); + mysql_mutex_unlock(&m_mutex); + DBUG_VOID_RETURN; +} diff --git a/sql/semisync_master_ack_receiver.h b/sql/semisync_master_ack_receiver.h new file mode 100644 index 00000000000..619748a2159 --- /dev/null +++ b/sql/semisync_master_ack_receiver.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef SEMISYNC_MASTER_ACK_RECEIVER_DEFINED +#define SEMISYNC_MASTER_ACK_RECEIVER_DEFINED + +#include "my_global.h" +#include "my_pthread.h" +#include "sql_class.h" +#include "semisync.h" +/** + Ack_receiver is responsible to control ack receive thread and maintain + slave information used by ack receive thread. + + There are mainly four operations on ack receive thread: + start: start ack receive thread + stop: stop ack receive thread + add_slave: maintain a new semisync slave's information + remove_slave: remove a semisync slave's information + */ +class Ack_receiver : public Repl_semi_sync_base +{ +public: + Ack_receiver(); + ~Ack_receiver() {} + void cleanup(); + /** + Notify ack receiver to receive acks on the dump session. + + It adds the given dump thread into the slave list and wakes + up ack thread if it is waiting for any slave coming. + + @param[in] thd THD of a dump thread. + + @return it return false if succeeds, otherwise true is returned. + */ + bool add_slave(THD *thd); + + /** + Notify ack receiver not to receive ack on the dump session. + + it removes the given dump thread from slave list. + + @param[in] thd THD of a dump thread. + */ + void remove_slave(THD *thd); + + /** + Start ack receive thread + + @return it return false if succeeds, otherwise true is returned. + */ + bool start(); + + /** + Stop ack receive thread + */ + void stop(); + + /** + The core of ack receive thread. + + It monitors all slaves' sockets and receives acks when they come. + */ + void run(); + + void set_trace_level(unsigned long trace_level) + { + m_trace_level= trace_level; + } +private: + enum status {ST_UP, ST_DOWN, ST_STOPPING}; + uint8 m_status; + /* + Protect m_status, m_slaves_changed and m_slaves. ack thread and other + session may access the variables at the same time. + */ + mysql_mutex_t m_mutex; + mysql_cond_t m_cond; + /* If slave list is updated(add or remove). */ + bool m_slaves_changed; + + class Slave :public ilink + { +public: + THD *thd; + Vio vio; + + my_socket sock_fd() { return vio.mysql_socket.fd; } + uint server_id() { return thd->variables.server_id; } + }; + + I_List<Slave> m_slaves; + + pthread_t m_pid; + +/* Declare them private, so no one can copy the object. */ + Ack_receiver(const Ack_receiver &ack_receiver); + Ack_receiver& operator=(const Ack_receiver &ack_receiver); + + void set_stage_info(const PSI_stage_info &stage); + void wait_for_slave_connection(); + my_socket get_slave_sockets(fd_set *fds, uint *count); +}; + +extern Ack_receiver ack_receiver; +#endif diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc new file mode 100644 index 00000000000..2d77ee7b10c --- /dev/null +++ b/sql/semisync_slave.cc @@ -0,0 +1,251 @@ +/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include <my_global.h> +#include "semisync_slave.h" + +Repl_semi_sync_slave repl_semisync_slave; + +my_bool rpl_semi_sync_slave_enabled= 0; + +char rpl_semi_sync_slave_delay_master; +my_bool rpl_semi_sync_slave_status= 0; +ulong rpl_semi_sync_slave_trace_level; + +/* + indicate whether or not the slave should send a reply to the master. + + This is set to true in repl_semi_slave_read_event if the current + event read is the last event of a transaction. And the value is + checked in repl_semi_slave_queue_event. +*/ +bool semi_sync_need_reply= false; +unsigned int rpl_semi_sync_slave_kill_conn_timeout; +unsigned long long rpl_semi_sync_slave_send_ack = 0; + +int Repl_semi_sync_slave::init_object() +{ + int result= 0; + + m_init_done = true; + + /* References to the parameter works after set_options(). */ + set_slave_enabled(rpl_semi_sync_slave_enabled); + set_trace_level(rpl_semi_sync_slave_trace_level); + set_delay_master(rpl_semi_sync_slave_delay_master); + set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout); + + return result; +} + +int Repl_semi_sync_slave::slave_read_sync_header(const char *header, + unsigned long total_len, + int *semi_flags, + const char **payload, + unsigned long *payload_len) +{ + int read_res = 0; + DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header"); + + if (rpl_semi_sync_slave_status) + { + if (DBUG_EVALUATE_IF("semislave_corrupt_log", 0, 1) + && (unsigned char)(header[0]) == k_packet_magic_num) + { + semi_sync_need_reply = (header[1] & k_packet_flag_sync); + *payload_len = total_len - 2; + *payload = header + 2; + + DBUG_PRINT("semisync", ("%s: reply - %d", + "Repl_semi_sync_slave::slave_read_sync_header", + semi_sync_need_reply)); + + if (semi_sync_need_reply) + *semi_flags |= SEMI_SYNC_NEED_ACK; + if (is_delay_master()) + *semi_flags |= SEMI_SYNC_SLAVE_DELAY_SYNC; + } + else + { + sql_print_error("Missing magic number for semi-sync packet, packet " + "len: %lu", total_len); + read_res = -1; + } + } else { + *payload= header; + *payload_len= total_len; + } + + DBUG_RETURN(read_res); +} + +int Repl_semi_sync_slave::slave_start(Master_info *mi) +{ + bool semi_sync= get_slave_enabled(); + + sql_print_information("Slave I/O thread: Start %s replication to\ + master '%s@%s:%d' in log '%s' at position %lu", + semi_sync ? "semi-sync" : "asynchronous", + const_cast<char *>(mi->user), mi->host, mi->port, + const_cast<char *>(mi->master_log_name), + (unsigned long)(mi->master_log_pos)); + + if (semi_sync && !rpl_semi_sync_slave_status) + rpl_semi_sync_slave_status= 1; + + /*clear the counter*/ + rpl_semi_sync_slave_send_ack= 0; + return 0; +} + +int Repl_semi_sync_slave::slave_stop(Master_info *mi) +{ + if (rpl_semi_sync_slave_status) + rpl_semi_sync_slave_status= 0; + if (get_slave_enabled()) + kill_connection(mi->mysql); + return 0; +} + +int Repl_semi_sync_slave::reset_slave(Master_info *mi) +{ + return 0; +} + +void Repl_semi_sync_slave::kill_connection(MYSQL *mysql) +{ + if (!mysql) + return; + + char kill_buffer[30]; + MYSQL *kill_mysql = NULL; + kill_mysql = mysql_init(kill_mysql); + mysql_options(kill_mysql, MYSQL_OPT_CONNECT_TIMEOUT, &m_kill_conn_timeout); + mysql_options(kill_mysql, MYSQL_OPT_READ_TIMEOUT, &m_kill_conn_timeout); + mysql_options(kill_mysql, MYSQL_OPT_WRITE_TIMEOUT, &m_kill_conn_timeout); + + bool ret= (!mysql_real_connect(kill_mysql, mysql->host, + mysql->user, mysql->passwd,0, mysql->port, mysql->unix_socket, 0)); + if (DBUG_EVALUATE_IF("semisync_slave_failed_kill", 1, 0) || ret) + { + sql_print_information("cannot connect to master to kill slave io_thread's " + "connection"); + if (!ret) + mysql_close(kill_mysql); + return; + } + uint kill_buffer_length = my_snprintf(kill_buffer, 30, "KILL %lu", + mysql->thread_id); + mysql_real_query(kill_mysql, kill_buffer, kill_buffer_length); + mysql_close(kill_mysql); +} + +int Repl_semi_sync_slave::request_transmit(Master_info *mi) +{ + MYSQL *mysql= mi->mysql; + MYSQL_RES *res= 0; + MYSQL_ROW row; + const char *query; + + if (!get_slave_enabled()) + return 0; + + query= "SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled'"; + if (mysql_real_query(mysql, query, strlen(query)) || + !(res= mysql_store_result(mysql))) + { + sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql)); + return 1; + } + + row= mysql_fetch_row(res); + if (DBUG_EVALUATE_IF("master_not_support_semisync", 1, 0) + || !row) + { + /* Master does not support semi-sync */ + sql_print_warning("Master server does not support semi-sync, " + "fallback to asynchronous replication"); + rpl_semi_sync_slave_status= 0; + mysql_free_result(res); + return 0; + } + mysql_free_result(res); + + /* + Tell master dump thread that we want to do semi-sync + replication + */ + query= "SET @rpl_semi_sync_slave= 1"; + if (mysql_real_query(mysql, query, strlen(query))) + { + sql_print_error("Set 'rpl_semi_sync_slave=1' on master failed"); + return 1; + } + mysql_free_result(mysql_store_result(mysql)); + rpl_semi_sync_slave_status= 1; + + return 0; +} + +int Repl_semi_sync_slave::slave_reply(Master_info *mi) +{ + MYSQL* mysql= mi->mysql; + const char *binlog_filename= const_cast<char *>(mi->master_log_name); + my_off_t binlog_filepos= mi->master_log_pos; + + NET *net= &mysql->net; + uchar reply_buffer[REPLY_MAGIC_NUM_LEN + + REPLY_BINLOG_POS_LEN + + REPLY_BINLOG_NAME_LEN]; + int reply_res = 0; + int name_len = strlen(binlog_filename); + + DBUG_ENTER("Repl_semi_sync_slave::slave_reply"); + + if (rpl_semi_sync_slave_status && semi_sync_need_reply) + { + /* Prepare the buffer of the reply. */ + reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num; + int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos); + memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET, + binlog_filename, + name_len + 1 /* including trailing '\0' */); + + DBUG_PRINT("semisync", ("%s: reply (%s, %lu)", + "Repl_semi_sync_slave::slave_reply", + binlog_filename, (ulong)binlog_filepos)); + + net_clear(net, 0); + /* Send the reply. */ + reply_res = my_net_write(net, reply_buffer, + name_len + REPLY_BINLOG_NAME_OFFSET); + if (!reply_res) + { + reply_res = DBUG_EVALUATE_IF("semislave_failed_net_flush", 1, net_flush(net)); + if (reply_res) + sql_print_error("Semi-sync slave net_flush() reply failed"); + rpl_semi_sync_slave_send_ack++; + } + else + { + sql_print_error("Semi-sync slave send reply failed: %s (%d)", + net->last_error, net->last_errno); + } + } + + DBUG_RETURN(reply_res); +} diff --git a/sql/semisync_slave.h b/sql/semisync_slave.h new file mode 100644 index 00000000000..d65262f151d --- /dev/null +++ b/sql/semisync_slave.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2006 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef SEMISYNC_SLAVE_H +#define SEMISYNC_SLAVE_H + +#include "semisync.h" +#include "my_global.h" +#include "sql_priv.h" +#include "rpl_mi.h" +#include "mysql.h" + +class Master_info; + +/** + The extension class for the slave of semi-synchronous replication +*/ +class Repl_semi_sync_slave + :public Repl_semi_sync_base { +public: + Repl_semi_sync_slave() :m_slave_enabled(false) {} + ~Repl_semi_sync_slave() {} + + void set_trace_level(unsigned long trace_level) { + m_trace_level = trace_level; + } + + /* Initialize this class after MySQL parameters are initialized. this + * function should be called once at bootstrap time. + */ + int init_object(); + + bool get_slave_enabled() { + return m_slave_enabled; + } + + void set_slave_enabled(bool enabled) { + m_slave_enabled = enabled; + } + + bool is_delay_master(){ + return m_delay_master; + } + + void set_delay_master(bool enabled) { + m_delay_master = enabled; + } + + void set_kill_conn_timeout(unsigned int timeout) { + m_kill_conn_timeout = timeout; + } + + /* A slave reads the semi-sync packet header and separate the metadata + * from the payload data. + * + * Input: + * header - (IN) packet header pointer + * total_len - (IN) total packet length: metadata + payload + * semi_flags - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and + SEMI_SYNC_NEED_ACK + * payload - (IN) payload: the replication event + * payload_len - (IN) payload length + * + * Return: + * 0: success; non-zero: error + */ + int slave_read_sync_header(const char *header, unsigned long total_len, + int *semi_flags, + const char **payload, unsigned long *payload_len); + + /* A slave replies to the master indicating its replication process. It + * indicates that the slave has received all events before the specified + * binlog position. + */ + int slave_reply(Master_info* mi); + int slave_start(Master_info *mi); + int slave_stop(Master_info *mi); + int request_transmit(Master_info*); + void kill_connection(MYSQL *mysql); + int reset_slave(Master_info *mi); + +private: + /* True when init_object has been called */ + bool m_init_done; + bool m_slave_enabled; /* semi-sycn is enabled on the slave */ + bool m_delay_master; + unsigned int m_kill_conn_timeout; +}; + + +/* System and status variables for the slave component */ +extern my_bool rpl_semi_sync_slave_enabled; +extern my_bool rpl_semi_sync_slave_status; +extern ulong rpl_semi_sync_slave_trace_level; +extern Repl_semi_sync_slave repl_semisync_slave; + +extern char rpl_semi_sync_slave_delay_master; +extern unsigned int rpl_semi_sync_slave_kill_conn_timeout; +extern unsigned long long rpl_semi_sync_slave_send_ack; + +#endif /* SEMISYNC_SLAVE_H */ diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index 4b449f3991a..ff9780a9531 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -1216,7 +1216,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf) tx_isolation_typelib as it hyphenates its items. */ buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL ")); - buf->append(isol[tx_isol_level - 1].str, isol[tx_isol_level - 1].length); + buf->append(&isol[tx_isol_level - 1]); buf->append(STRING_WITH_LEN("; ")); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 15f6bbdafc5..311b33bc0dd 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -16,7 +16,7 @@ /* variable declarations are in sys_vars.cc now !!! */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" #include "sql_class.h" // set_var.h: session_var_ptr #include "set_var.h" #include "sql_priv.h" @@ -1162,6 +1162,7 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond) { STRING_WITH_LEN("SET") }, // GET_SET 13 { STRING_WITH_LEN("DOUBLE") }, // GET_DOUBLE 14 { STRING_WITH_LEN("FLAGSET") }, // GET_FLAGSET 15 + { STRING_WITH_LEN("BOOLEAN") }, // GET_BIT 16 }; const ulong vartype= (var->option.var_type & GET_TYPE_MASK); const LEX_CSTRING *type= types + vartype; @@ -1293,3 +1294,222 @@ enum sys_var::where get_sys_var_value_origin(void *ptr) return sys_var::CONFIG; } + +/* + Find the next item in string of comma-separated items. + END_POS points at the end of the string. + ITEM_START and ITEM_END return the limits of the next item. + Returns true while items are available, false at the end. +*/ +static bool +engine_list_next_item(const char **pos, const char *end_pos, + const char **item_start, const char **item_end) +{ + if (*pos >= end_pos) + return false; + *item_start= *pos; + while (*pos < end_pos && **pos != ',') + ++*pos; + *item_end= *pos; + ++*pos; + return true; +} + + +static bool +resolve_engine_list_item(THD *thd, plugin_ref *list, uint32 *idx, + const char *pos, const char *pos_end, + bool error_on_unknown_engine, bool temp_copy) +{ + LEX_CSTRING item_str; + plugin_ref ref; + uint32_t i; + THD *thd_or_null = (temp_copy ? thd : NULL); + + item_str.str= pos; + item_str.length= pos_end-pos; + ref= ha_resolve_by_name(thd_or_null, &item_str, false); + if (!ref) + { + if (error_on_unknown_engine) + { + ErrConvString err(pos, pos_end-pos, system_charset_info); + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr()); + return true; + } + return false; + } + /* Ignore duplicates, like --plugin-load does. */ + for (i= 0; i < *idx; ++i) + { + if (plugin_hton(list[i]) == plugin_hton(ref)) + { + if (!temp_copy) + plugin_unlock(NULL, ref); + return false; + } + } + list[*idx]= ref; + ++*idx; + return false; +} + + +/* + Helper for class Sys_var_pluginlist. + Resolve a comma-separated list of storage engine names to a null-terminated + array of plugin_ref. + + If TEMP_COPY is true, a THD must be given as well. In this case, the + allocated memory and locked plugins are registered in the THD and will + be freed / unlocked automatically. If TEMP_COPY is true, THD can be + passed as NULL, and resources must be freed explicitly later with + free_engine_list(). +*/ +plugin_ref * +resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len, + bool error_on_unknown_engine, bool temp_copy) +{ + uint32 count, idx; + const char *pos, *item_start, *item_end; + const char *str_arg_end= str_arg + str_arg_len; + plugin_ref *res; + + count= 0; + pos= str_arg; + for (;;) + { + if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end)) + break; + ++count; + } + + if (temp_copy) + res= (plugin_ref *)thd->calloc((count+1)*sizeof(*res)); + else + res= (plugin_ref *)my_malloc((count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME)); + if (!res) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res))); + goto err; + } + + idx= 0; + pos= str_arg; + for (;;) + { + if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end)) + break; + DBUG_ASSERT(idx < count); + if (idx >= count) + break; + if (resolve_engine_list_item(thd, res, &idx, item_start, item_end, + error_on_unknown_engine, temp_copy)) + goto err; + } + + return res; + +err: + if (!temp_copy) + free_engine_list(res); + return NULL; +} + + +void +free_engine_list(plugin_ref *list) +{ + plugin_ref *p; + + if (!list) + return; + for (p= list; *p; ++p) + plugin_unlock(NULL, *p); + my_free(list); +} + + +plugin_ref * +copy_engine_list(plugin_ref *list) +{ + plugin_ref *p; + uint32 count, i; + + for (p= list, count= 0; *p; ++p, ++count) + ; + p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0)); + if (!p) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p))); + return NULL; + } + for (i= 0; i < count; ++i) + p[i]= my_plugin_lock(NULL, list[i]); + p[i] = NULL; + return p; +} + + +/* + Create a temporary copy of an engine list. The memory will be freed + (and the plugins unlocked) automatically, on the passed THD. +*/ +plugin_ref * +temp_copy_engine_list(THD *thd, plugin_ref *list) +{ + plugin_ref *p; + uint32 count, i; + + for (p= list, count= 0; *p; ++p, ++count) + ; + p= (plugin_ref *)thd->alloc((count+1)*sizeof(*p)); + if (!p) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p))); + return NULL; + } + for (i= 0; i < count; ++i) + p[i]= my_plugin_lock(thd, list[i]); + p[i] = NULL; + return p; +} + + +char * +pretty_print_engine_list(THD *thd, plugin_ref *list) +{ + plugin_ref *p; + size_t size; + char *buf, *pos; + + if (!list || !*list) + return thd->strmake("", 0); + + size= 0; + for (p= list; *p; ++p) + size+= plugin_name(*p)->length + 1; + buf= static_cast<char *>(thd->alloc(size)); + if (!buf) + return NULL; + pos= buf; + for (p= list; *p; ++p) + { + LEX_CSTRING *name; + size_t remain; + + remain= buf + size - pos; + DBUG_ASSERT(remain > 0); + if (remain <= 1) + break; + if (pos != buf) + { + pos= strmake(pos, ",", remain-1); + --remain; + } + name= plugin_name(*p); + pos= strmake(pos, name->str, MY_MIN(name->length, remain-1)); + } + *pos= '\0'; + return buf; +} diff --git a/sql/set_var.h b/sql/set_var.h index 17d1ff93ebc..d0143e1e524 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -158,6 +158,7 @@ public: case GET_BOOL: case GET_SET: case GET_FLAGSET: + case GET_BIT: return type != STRING_RESULT && type != INT_RESULT; case GET_DOUBLE: return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT; @@ -286,6 +287,7 @@ public: longlong longlong_value; ///< for signed integer double double_value; ///< for Sys_var_double plugin_ref plugin; ///< for Sys_var_plugin + plugin_ref *plugins; ///< for Sys_var_pluginlist Time_zone *time_zone; ///< for Sys_var_tz LEX_STRING string_value; ///< for Sys_var_charptr and others const void *ptr; ///< for Sys_var_struct @@ -424,6 +426,12 @@ int sys_var_init(); uint sys_var_elements(); int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags); void sys_var_end(void); +plugin_ref *resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len, + bool error_on_unknown_engine, bool temp_copy); +void free_engine_list(plugin_ref *list); +plugin_ref *copy_engine_list(plugin_ref *list); +plugin_ref *temp_copy_engine_list(THD *thd, plugin_ref *list); +char *pretty_print_engine_list(THD *thd, plugin_ref *list); #endif diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 7812cbd951d..349e8f2c533 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7785,6 +7785,30 @@ ER_UNKNOWN_SEQUENCES 42S02 ER_UNKNOWN_VIEW 42S02 eng "Unknown VIEW: '%-.300s'" ER_WRONG_INSERT_INTO_SEQUENCE - eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a squence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead." + eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a sequence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead." ER_SP_STACK_TRACE eng "At line %u in %s" +ER_COMPRESSED_COLUMN_USED_AS_KEY + eng "Compressed column '%-.192s' can't be used in key specification" +ER_UNKNOWN_COMPRESSION_METHOD + eng "Unknown compression method: %s" +ER_WRONG_NUMBER_OF_VALUES_IN_TVC + eng "The used table value constructor has a different number of values" +ER_FIELD_REFERENCE_IN_TVC + eng "Field reference '%-.192s' can't be used in table value constructor" +ER_WRONG_TYPE_FOR_PERCENTILE_FUNC + eng "Numeric datatype is required for %s function" +ER_ARGUMENT_NOT_CONSTANT + eng "Argument to the %s function is not a constant for a partition" +ER_ARGUMENT_OUT_OF_RANGE + eng "Argument to the %s function does not belong to the range [0,1]" +ER_WRONG_TYPE_OF_ARGUMENT + eng "%s function only accepts arguments that can be converted to numerical types" +ER_NOT_AGGREGATE_FUNCTION + eng "Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW)" +ER_INVALID_AGGREGATE_FUNCTION + eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function" +ER_INVALID_VALUE_TO_LIMIT + eng "Limit only accepts integer values" +ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT + eng "Invisible column %`s must have a default value" diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 68e801d5885..0452e181e22 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" +#include "mariadb.h" #include <signal.h> //#include "sys_vars.h" @@ -163,7 +163,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) "where mysqld died. If you see no messages after this, something went\n" "terribly wrong...\n"); my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, - (ulong)my_thread_stack_size); + (ulong)my_thread_stack_size, 0); } if (thd) { diff --git a/sql/slave.cc b/sql/slave.cc index c686553fdd5..90ad8bbdef1 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -25,7 +25,7 @@ replication slave. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "slave.h" #include "sql_parse.h" // execute_init_command @@ -43,7 +43,6 @@ #include <ssl_compat.h> #include "unireg.h" #include <mysys_err.h> -#include "rpl_handler.h" #include <signal.h> #include <mysql.h> #include <myisam.h> @@ -60,6 +59,8 @@ #include "rpl_tblmap.h" #include "debug_sync.h" #include "rpl_parallel.h" +#include "sql_show.h" +#include "semisync_slave.h" #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -71,6 +72,9 @@ bool use_slave_mask = 0; MY_BITMAP slave_error_mask; char slave_skip_error_names[SHOW_VAR_FUNC_BUFF_SIZE]; +uint *slave_transaction_retry_errors; +uint slave_transaction_retry_error_length= 0; +char slave_transaction_retry_error_names[SHOW_VAR_FUNC_BUFF_SIZE]; char* slave_load_tmpdir = 0; Master_info *active_mi= 0; @@ -155,7 +159,8 @@ static bool wait_for_relay_log_space(Relay_log_info* rli); static bool io_slave_killed(Master_info* mi); static bool sql_slave_killed(rpl_group_info *rgi); static int init_slave_thread(THD*, Master_info *, SLAVE_THD_TYPE); -static void print_slave_skip_errors(void); +static void make_slave_skip_errors_printable(void); +static void make_slave_transaction_retry_errors_printable(void); static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi); static int safe_reconnect(THD*, MYSQL*, Master_info*, bool); static int connect_to_master(THD*, MYSQL*, Master_info*, bool, bool); @@ -279,15 +284,180 @@ static void init_slave_psi_keys(void) #endif /* HAVE_PSI_INTERFACE */ +/* + Note: This definition needs to be kept in sync with the one in + mysql_system_tables.sql which is used by mysql_create_db. +*/ +static const char gtid_pos_table_definition1[]= + "CREATE TABLE "; +static const char gtid_pos_table_definition2[]= + " (domain_id INT UNSIGNED NOT NULL, " + "sub_id BIGINT UNSIGNED NOT NULL, " + "server_id INT UNSIGNED NOT NULL, " + "seq_no BIGINT UNSIGNED NOT NULL, " + "PRIMARY KEY (domain_id, sub_id)) CHARSET=latin1 " + "COMMENT='Replication slave GTID position' " + "ENGINE="; + +/* + Build a query string + CREATE TABLE mysql.gtid_slave_pos_<engine> ... ENGINE=<engine> +*/ +static bool +build_gtid_pos_create_query(THD *thd, String *query, + LEX_CSTRING *table_name, + LEX_CSTRING *engine_name) +{ + bool err= false; + err|= query->append(gtid_pos_table_definition1); + err|= append_identifier(thd, query, table_name->str, table_name->length); + err|= query->append(gtid_pos_table_definition2); + err|= append_identifier(thd, query, engine_name->str, engine_name->length); + return err; +} + + +static int +gtid_pos_table_creation(THD *thd, plugin_ref engine, LEX_CSTRING *table_name) +{ + int err; + StringBuffer<sizeof(gtid_pos_table_definition1) + + sizeof(gtid_pos_table_definition1) + + 2*FN_REFLEN> query; + + if (build_gtid_pos_create_query(thd, &query, table_name, plugin_name(engine))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return 1; + } + + thd->set_db("mysql", 5); + thd->clear_error(); + ulonglong thd_saved_option= thd->variables.option_bits; + /* This query shuold not be binlogged. */ + thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG; + thd->set_query_and_id(query.c_ptr(), query.length(), thd->charset(), + next_query_id()); + Parser_state parser_state; + err= parser_state.init(thd, thd->query(), thd->query_length()); + if (err) + goto end; + mysql_parse(thd, thd->query(), thd->query_length(), &parser_state, + FALSE, FALSE); + if (thd->is_error()) + err= 1; +end: + thd->variables.option_bits= thd_saved_option; + thd->reset_query(); + return err; +} + + +static void +handle_gtid_pos_auto_create_request(THD *thd, void *hton) +{ + int err; + plugin_ref engine= NULL, *auto_engines; + rpl_slave_state::gtid_pos_table *entry; + StringBuffer<FN_REFLEN> loc_table_name; + LEX_CSTRING table_name; + + /* + Check that the plugin is still in @@gtid_pos_auto_engines, and lock + it. + */ + mysql_mutex_lock(&LOCK_global_system_variables); + engine= NULL; + for (auto_engines= opt_gtid_pos_auto_plugins; + auto_engines && *auto_engines; + ++auto_engines) + { + if (plugin_hton(*auto_engines) == hton) + { + engine= my_plugin_lock(NULL, *auto_engines); + break; + } + } + mysql_mutex_unlock(&LOCK_global_system_variables); + if (!engine) + { + /* The engine is gone from @@gtid_pos_auto_engines, so no action. */ + goto end; + } + + /* Find the entry for the table to auto-create. */ + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (entry) + { + if (entry->table_hton == hton && + entry->state == rpl_slave_state::GTID_POS_CREATE_REQUESTED) + break; + entry= entry->next; + } + if (entry) + { + entry->state = rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS; + err= loc_table_name.append(entry->table_name.str, entry->table_name.length); + } + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (!entry) + goto end; + if (err) + { + sql_print_error("Out of memory while trying to auto-create GTID position table"); + goto end; + } + table_name.str= loc_table_name.c_ptr_safe(); + table_name.length= loc_table_name.length(); + + err= gtid_pos_table_creation(thd, engine, &table_name); + if (err) + { + sql_print_error("Error auto-creating GTID position table `mysql.%s`: %s Error_code: %d", + table_name.str, thd->get_stmt_da()->message(), + thd->get_stmt_da()->sql_errno()); + thd->clear_error(); + goto end; + } + + /* Now enable the entry for the auto-created table. */ + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (entry) + { + if (entry->table_hton == hton && + entry->state == rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS) + { + entry->state= rpl_slave_state::GTID_POS_AVAILABLE; + break; + } + entry= entry->next; + } + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + +end: + if (engine) + plugin_unlock(NULL, engine); +} + + static bool slave_background_thread_running; static bool slave_background_thread_stop; static bool slave_background_thread_gtid_loaded; -struct slave_background_kill_t { +static struct slave_background_kill_t { slave_background_kill_t *next; THD *to_kill; } *slave_background_kill_list; +static struct slave_background_gtid_pos_create_t { + slave_background_gtid_pos_create_t *next; + void *hton; +} *slave_background_gtid_pos_create_list; + pthread_handler_t handle_slave_background(void *arg __attribute__((unused))) @@ -321,6 +491,7 @@ handle_slave_background(void *arg __attribute__((unused))) do { slave_background_kill_t *kill_list; + slave_background_gtid_pos_create_t *create_list; thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background, &stage_slave_background_wait_request, @@ -329,12 +500,14 @@ handle_slave_background(void *arg __attribute__((unused))) { stop= abort_loop || thd->killed || slave_background_thread_stop; kill_list= slave_background_kill_list; - if (stop || kill_list) + create_list= slave_background_gtid_pos_create_list; + if (stop || kill_list || create_list) break; mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); } slave_background_kill_list= NULL; + slave_background_gtid_pos_create_list= NULL; thd->EXIT_COND(&old_stage); while (kill_list) @@ -351,6 +524,16 @@ handle_slave_background(void *arg __attribute__((unused))) mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready); my_free(p); } + + while (create_list) + { + slave_background_gtid_pos_create_t *next= create_list->next; + void *hton= create_list->hton; + handle_gtid_pos_auto_create_request(thd, hton); + my_free(create_list); + create_list= next; + } + mysql_mutex_lock(&LOCK_slave_background); } while (!stop); @@ -390,6 +573,41 @@ slave_background_kill_request(THD *to_kill) /* + This function must only be called from a slave SQL thread (or worker thread), + to ensure that the table_entry will not go away before we can lock the + LOCK_slave_state. +*/ +void +slave_background_gtid_pos_create_request( + rpl_slave_state::gtid_pos_table *table_entry) +{ + slave_background_gtid_pos_create_t *p; + + if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE) + return; + p= (slave_background_gtid_pos_create_t *)my_malloc(sizeof(*p), MYF(MY_WME)); + if (!p) + return; + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE) + { + my_free(p); + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + return; + } + table_entry->state= rpl_slave_state::GTID_POS_CREATE_REQUESTED; + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + + p->hton= table_entry->table_hton; + mysql_mutex_lock(&LOCK_slave_background); + p->next= slave_background_gtid_pos_create_list; + slave_background_gtid_pos_create_list= p; + mysql_cond_signal(&COND_slave_background); + mysql_mutex_unlock(&LOCK_slave_background); +} + + +/* Start the slave background thread. This thread is currently used for two purposes: @@ -419,7 +637,6 @@ start_slave_background_thread() sql_print_error("Failed to create thread while initialising slave"); return 1; } - mysql_mutex_lock(&LOCK_slave_background); while (!slave_background_thread_gtid_loaded) mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); @@ -491,15 +708,6 @@ int init_slave() } /* - If --slave-skip-errors=... was not used, the string value for the - system variable has not been set up yet. Do it now. - */ - if (!use_slave_mask) - { - print_slave_skip_errors(); - } - - /* If master_host is not specified, try to read it from the master_info file. If master_host is specified, create the master_info file if it doesn't exists. @@ -596,12 +804,12 @@ int init_recovery(Master_info* mi, const char** errmsg) DBUG_RETURN(0); } - + /** Convert slave skip errors bitmap into a printable string. */ -static void print_slave_skip_errors(void) +static void make_slave_skip_errors_printable(void) { /* To be safe, we want 10 characters of room in the buffer for a number @@ -610,7 +818,7 @@ static void print_slave_skip_errors(void) plus a NUL terminator. That is a max 6 digit number. */ const size_t MIN_ROOM= 10; - DBUG_ENTER("print_slave_skip_errors"); + DBUG_ENTER("make_slave_skip_errors_printable"); DBUG_ASSERT(sizeof(slave_skip_error_names) > MIN_ROOM); DBUG_ASSERT(MAX_SLAVE_ERROR <= 999999); // 6 digits @@ -632,14 +840,14 @@ static void print_slave_skip_errors(void) else { char *buff= slave_skip_error_names; - char *bend= buff + sizeof(slave_skip_error_names); + char *bend= buff + sizeof(slave_skip_error_names) - MIN_ROOM; int errnum; for (errnum= 0; errnum < MAX_SLAVE_ERROR; errnum++) { if (bitmap_is_set(&slave_error_mask, errnum)) { - if (buff + MIN_ROOM >= bend) + if (buff >= bend) break; /* purecov: tested */ buff= int10_to_str(errnum, buff, 10); *buff++= ','; @@ -669,24 +877,24 @@ static void print_slave_skip_errors(void) Called from get_options() in mysqld.cc on start-up */ -void init_slave_skip_errors(const char* arg) +bool init_slave_skip_errors(const char* arg) { const char *p; DBUG_ENTER("init_slave_skip_errors"); + if (!arg || !*arg) // No errors defined + goto end; + if (my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0)) - { - fprintf(stderr, "Badly out of memory, please check your system status\n"); - exit(1); - } - use_slave_mask = 1; + DBUG_RETURN(1); + + use_slave_mask= 1; for (;my_isspace(system_charset_info,*arg);++arg) /* empty */; if (!my_strnncoll(system_charset_info,(uchar*)arg,4,(const uchar*)"all",4)) { bitmap_set_all(&slave_error_mask); - print_slave_skip_errors(); - DBUG_VOID_RETURN; + goto end; } for (p= arg ; *p; ) { @@ -698,11 +906,109 @@ void init_slave_skip_errors(const char* arg) while (!my_isdigit(system_charset_info,*p) && *p) p++; } - /* Convert slave skip errors bitmap into a printable string. */ - print_slave_skip_errors(); + +end: + make_slave_skip_errors_printable(); + DBUG_RETURN(0); +} + +/** + Make printable version if slave_transaction_retry_errors + This is never empty as at least ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT + will be there +*/ + +static void make_slave_transaction_retry_errors_printable(void) +{ + /* + To be safe, we want 10 characters of room in the buffer for a number + plus terminators. Also, we need some space for constant strings. + 10 characters must be sufficient for a number plus {',' | '...'} + plus a NUL terminator. That is a max 6 digit number. + */ + const size_t MIN_ROOM= 10; + char *buff= slave_transaction_retry_error_names; + char *bend= buff + sizeof(slave_transaction_retry_error_names) - MIN_ROOM; + uint i; + DBUG_ENTER("make_slave_transaction_retry_errors_printable"); + DBUG_ASSERT(sizeof(slave_transaction_retry_error_names) > MIN_ROOM); + + /* Make @@slave_transaction_retry_errors show a human-readable value */ + opt_slave_transaction_retry_errors= slave_transaction_retry_error_names; + + for (i= 0; i < slave_transaction_retry_error_length && buff < bend; i++) + { + buff= int10_to_str(slave_transaction_retry_errors[i], buff, 10); + *buff++= ','; + } + if (buff != slave_transaction_retry_error_names) + buff--; // Remove last ',' + if (i < slave_transaction_retry_error_length) + { + /* Couldn't show all errors */ + buff= strmov(buff, "..."); /* purecov: tested */ + } + *buff=0; + DBUG_PRINT("exit", ("error_names: '%s'", + slave_transaction_retry_error_names)); DBUG_VOID_RETURN; } + +bool init_slave_transaction_retry_errors(const char* arg) +{ + const char *p; + long err_code; + uint i; + DBUG_ENTER("init_slave_transaction_retry_errors"); + + /* Handle empty strings */ + if (!arg) + arg= ""; + + slave_transaction_retry_error_length= 2; + for (;my_isspace(system_charset_info,*arg);++arg) + /* empty */; + for (p= arg; *p; ) + { + if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code))) + break; + slave_transaction_retry_error_length++; + while (!my_isdigit(system_charset_info,*p) && *p) + p++; + } + + if (!(slave_transaction_retry_errors= + (uint *) my_once_alloc(sizeof(int) * + slave_transaction_retry_error_length, + MYF(MY_WME)))) + DBUG_RETURN(1); + + /* + Temporary error codes: + currently, InnoDB deadlock detected by InnoDB or lock + wait timeout (innodb_lock_wait_timeout exceeded + */ + slave_transaction_retry_errors[0]= ER_LOCK_DEADLOCK; + slave_transaction_retry_errors[1]= ER_LOCK_WAIT_TIMEOUT; + + /* Add user codes after this */ + for (p= arg, i= 2; *p; ) + { + if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code))) + break; + if (err_code > 0 && err_code < ER_ERROR_LAST) + slave_transaction_retry_errors[i++]= (uint) err_code; + while (!my_isdigit(system_charset_info,*p) && *p) + p++; + } + slave_transaction_retry_error_length= i; + + make_slave_transaction_retry_errors_printable(); + DBUG_RETURN(0); +} + + int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) { DBUG_ENTER("terminate_slave_threads"); @@ -771,7 +1077,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) mysql_mutex_unlock(log_lock); } - DBUG_RETURN(retval); + DBUG_RETURN(retval); } @@ -3162,6 +3468,13 @@ void set_slave_thread_options(THD* thd) options&= ~OPTION_BIN_LOG; thd->variables.option_bits= options; thd->variables.completion_type= 0; + + /* For easier test in LOGGER::log_command */ + if (thd->variables.log_disabled_statements & LOG_DISABLE_SLAVE) + thd->variables.option_bits|= OPTION_LOG_OFF; + + thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements & + LOG_SLOW_DISABLE_SLAVE); DBUG_VOID_RETURN; } @@ -3208,8 +3521,7 @@ static int init_slave_thread(THD* thd, Master_info *mi, thd->security_ctx->skip_grants(); thd->slave_thread= 1; thd->connection_name= mi->connection_name; - thd->variables.sql_log_slow= opt_log_slow_slave_statements; - thd->variables.log_slow_filter= global_system_variables.log_slow_filter; + thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE); set_slave_thread_options(thd); if (thd_type == SLAVE_THD_SQL) @@ -3274,11 +3586,9 @@ static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi, if (opt_log_slave_updates && opt_replicate_annotate_row_events) binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT; - if (RUN_HOOK(binlog_relay_io, - before_request_transmit, - (thd, mi, binlog_flags))) + if (repl_semisync_slave.request_transmit(mi)) DBUG_RETURN(1); - + // TODO if big log files: Change next to int8store() int4store(buf, (ulong) mi->master_log_pos); int2store(buf + 4, binlog_flags); @@ -3376,14 +3686,20 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings, DBUG_RETURN(len - 1); } -/* + +/** Check if the current error is of temporary nature of not. Some errors are temporary in nature, such as ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT. + + @retval 0 if fatal error + @retval 1 temporary error, do retry */ + int has_temporary_error(THD *thd) { + uint current_errno; DBUG_ENTER("has_temporary_error"); DBUG_EXECUTE_IF("all_errors_are_temporary_errors", @@ -3401,14 +3717,12 @@ has_temporary_error(THD *thd) if (!thd->is_error()) DBUG_RETURN(0); - /* - Temporary error codes: - currently, InnoDB deadlock detected by InnoDB or lock - wait timeout (innodb_lock_wait_timeout exceeded - */ - if (thd->get_stmt_da()->sql_errno() == ER_LOCK_DEADLOCK || - thd->get_stmt_da()->sql_errno() == ER_LOCK_WAIT_TIMEOUT) - DBUG_RETURN(1); + current_errno= thd->get_stmt_da()->sql_errno(); + for (uint i= 0; i < slave_transaction_retry_error_length; i++) + { + if (current_errno == slave_transaction_retry_errors[i]) + DBUG_RETURN(1); + } DBUG_RETURN(0); } @@ -4059,8 +4373,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, exec_res= 0; serial_rgi->cleanup_context(thd, 1); /* chance for concurrent connection to get more locks */ - slave_sleep(thd, MY_MIN(serial_rgi->trans_retries, + slave_sleep(thd, MY_MAX(MY_MIN(serial_rgi->trans_retries, MAX_SLAVE_RETRY_PAUSE), + slave_trans_retry_interval), sql_slave_killed, serial_rgi); serial_rgi->trans_retries++; mysql_mutex_lock(&rli->data_lock); // because of SHOW STATUS @@ -4145,7 +4460,7 @@ static bool check_io_slave_killed(Master_info *mi, const char *info) @param[in] mysql MySQL connection. @param[in] mi Master connection information. @param[in,out] retry_count Number of attempts to reconnect. - @param[in] suppress_warnings TRUE when a normal net read timeout + @param[in] suppress_warnings TRUE when a normal net read timeout has caused to reconnecting. @param[in] messages Messages to print/log, see reconnect_messages[] array. @@ -4298,7 +4613,8 @@ pthread_handler_t handle_slave_io(void *arg) } - if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi))) + if (DBUG_EVALUATE_IF("failed_slave_start", 1, 0) + || repl_semisync_slave.slave_start(mi)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4488,9 +4804,10 @@ Stopping slave I/O thread due to out-of-memory error from master"); retry_count=0; // ok event, reset retry counter THD_STAGE_INFO(thd, stage_queueing_master_event_to_the_relay_log); event_buf= (const char*)mysql->net.read_pos + 1; - if (RUN_HOOK(binlog_relay_io, after_read_event, - (thd, mi,(const char*)mysql->net.read_pos + 1, - event_len, &event_buf, &event_len))) + mi->semi_ack= 0; + if (repl_semisync_slave. + slave_read_sync_header((const char*)mysql->net.read_pos + 1, event_len, + &(mi->semi_ack), &event_buf, &event_len)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4539,9 +4856,6 @@ Stopping slave I/O thread due to out-of-memory error from master"); tokenamount -= network_read_len; } - /* XXX: 'synced' should be updated by queue_event to indicate - whether event has been synced to disk */ - bool synced= 0; if (queue_event(mi, event_buf, event_len)) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL, @@ -4550,8 +4864,8 @@ Stopping slave I/O thread due to out-of-memory error from master"); goto err; } - if (RUN_HOOK(binlog_relay_io, after_queue_event, - (thd, mi, event_buf, event_len, synced))) + if (rpl_semi_sync_slave_status && (mi->semi_ack & SEMI_SYNC_NEED_ACK) && + repl_semisync_slave.slave_reply(mi)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4560,7 +4874,16 @@ Stopping slave I/O thread due to out-of-memory error from master"); } if (mi->using_gtid == Master_info::USE_GTID_NO && - flush_master_info(mi, TRUE, TRUE)) + /* + If rpl_semi_sync_slave_delay_master is enabled, we will flush + master info only when ack is needed. This may lead to at least one + group transaction delay but affords better performance improvement. + */ + (!repl_semisync_slave.get_slave_enabled() || + (!(mi->semi_ack & SEMI_SYNC_SLAVE_DELAY_SYNC) || + (mi->semi_ack & (SEMI_SYNC_NEED_ACK)))) && + (DBUG_EVALUATE_IF("failed_flush_master_info", 1, 0) || + flush_master_info(mi, TRUE, TRUE))) { sql_print_error("Failed to flush master info file"); goto err; @@ -4614,7 +4937,7 @@ err: IO_RPL_LOG_NAME, mi->master_log_pos, tmp.c_ptr_safe()); } - RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi)); + repl_semisync_slave.slave_stop(mi); thd->reset_query(); thd->reset_db(NULL, 0); if (mysql) @@ -4648,9 +4971,7 @@ err_during_init: // TODO: make rpl_status part of Master_info change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); - mysql_mutex_lock(&LOCK_thread_count); - thd->unlink(); - mysql_mutex_unlock(&LOCK_thread_count); + thd->assert_not_linked(); delete thd; thread_safe_decrement32(&service_thread_count); signal_thd_deleted(); @@ -5063,6 +5384,14 @@ pthread_handler_t handle_slave_sql(void *arg) if (mi->using_gtid != Master_info::USE_GTID_NO || opt_gtid_strict_mode) goto err; } + /* Re-load the set of mysql.gtid_slave_posXXX tables available. */ + if (find_gtid_slave_pos_tables(thd)) + { + rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL, + "Error processing replication GTID position tables: %s", + thd->get_stmt_da()->message()); + goto err; + } /* execute init_slave variable */ if (opt_init_slave.length) @@ -5321,11 +5650,7 @@ err_during_init: rpl_parallel_resize_pool_if_no_slaves(); - /* TODO: Check if this lock is needed */ - mysql_mutex_lock(&LOCK_thread_count); delete serial_rgi; - mysql_mutex_unlock(&LOCK_thread_count); - delete thd; thread_safe_decrement32(&service_thread_count); signal_thd_deleted(); @@ -5934,7 +6259,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) mysql_mutex_unlock(log_lock); goto err; } - rli->relay_log.signal_update(); + rli->relay_log.signal_relay_log_update(); mysql_mutex_unlock(log_lock); mi->gtid_reconnect_event_skip_count= 0; @@ -6479,7 +6804,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) if (got_gtid_event) rli->ign_gtids.update(&event_gtid); } - rli->relay_log.signal_update(); // the slave SQL thread needs to re-check + // the slave SQL thread needs to re-check + rli->relay_log.signal_relay_log_update(); DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored", (ulong) mi->master_log_pos, uint4korr(buf + SERVER_ID_OFFSET))); } @@ -6989,7 +7315,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) MYSQL_BIN_LOG::open() will write the buffered description event. */ old_pos= rli->event_relay_log_pos; - if ((ev= Log_event::read_log_event(cur_log,0, + if ((ev= Log_event::read_log_event(cur_log, rli->relay_log.description_event_for_exec, opt_slave_sql_verify_checksum))) diff --git a/sql/slave.h b/sql/slave.h index aec5f0a1d02..649d55b45b9 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -48,6 +48,7 @@ #include "my_list.h" #include "rpl_filter.h" #include "rpl_tblmap.h" +#include "rpl_gtid.h" #define SLAVE_NET_TIMEOUT 60 @@ -131,6 +132,9 @@ extern ulong master_retry_count; extern MY_BITMAP slave_error_mask; extern char slave_skip_error_names[]; extern bool use_slave_mask; +extern char slave_transaction_retry_error_names[]; +extern uint *slave_transaction_retry_errors; +extern uint slave_transaction_retry_error_length; extern char *slave_load_tmpdir; extern char *master_info_file; extern MYSQL_PLUGIN_IMPORT char *relay_log_info_file; @@ -138,6 +142,7 @@ extern char *opt_relay_logname, *opt_relaylog_index_name; extern my_bool opt_skip_slave_start, opt_reckless_slave; extern my_bool opt_log_slave_updates; extern char *opt_slave_skip_errors; +extern char *opt_slave_transaction_retry_errors; extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; extern ulonglong opt_read_binlog_speed_limit; @@ -183,7 +188,8 @@ extern const char *relay_log_basename; int init_slave(); int init_recovery(Master_info* mi, const char** errmsg); -void init_slave_skip_errors(const char* arg); +bool init_slave_skip_errors(const char* arg); +bool init_slave_transaction_retry_errors(const char* arg); int register_slave_on_master(MYSQL* mysql); int terminate_slave_threads(Master_info* mi, int thread_mask, bool skip_lock = 0); @@ -268,6 +274,8 @@ void slave_output_error_info(rpl_group_info *rgi, THD *thd); pthread_handler_t handle_slave_sql(void *arg); bool net_request_file(NET* net, const char* fname); void slave_background_kill_request(THD *to_kill); +void slave_background_gtid_pos_create_request + (rpl_slave_state::gtid_pos_table *table_entry); extern bool volatile abort_loop; extern Master_info *active_mi; /* active_mi for multi-master */ diff --git a/sql/sp.cc b/sql/sp.cc index 12d303ed85a..843c40cf2b6 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sp.h" @@ -201,6 +201,11 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = { C_STRING_WITH_LEN("body_utf8") }, { C_STRING_WITH_LEN("longblob") }, { NULL, 0 } + }, + { + { C_STRING_WITH_LEN("aggregate") }, + { C_STRING_WITH_LEN("enum('NONE','GROUP')") }, + { NULL, 0 } } }; @@ -583,6 +588,22 @@ bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table) return true; suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID; + if (table->field[MYSQL_PROC_FIELD_AGGREGATE]->val_str_nopad(thd->mem_root, + &str)) + return true; + + switch (str.str[0]) { + case 'N': + agg_type= NOT_AGGREGATE; + break; + case 'G': + agg_type= GROUP_AGGREGATE; + break; + default: + agg_type= DEFAULT_AGGREGATE; + } + + if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root, &comment)) return true; @@ -1183,6 +1204,13 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name, system_charset_info); + if (sp->agg_type() != DEFAULT_AGGREGATE) + { + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_AGGREGATE]-> + store((longlong)sp->agg_type(),TRUE); + } + store_failed= store_failed || table->field[MYSQL_PROC_MYSQL_TYPE]-> store((longlong) type(), true); @@ -1494,6 +1522,9 @@ Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name, if (chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment, system_charset_info); + if (chistics->agg_type != DEFAULT_AGGREGATE) + table->field[MYSQL_PROC_FIELD_AGGREGATE]-> + store((longlong)chistics->agg_type, TRUE); if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) && ret != HA_ERR_RECORD_IS_THE_SAME) ret= SP_WRITE_ROW_FAILED; @@ -1559,7 +1590,7 @@ bool lock_db_routines(THD *thd, const char *db) uchar keybuf[MAX_KEY_LENGTH]; DBUG_ENTER("lock_db_routines"); - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); /* mysql.proc will be re-opened during deletion, so we can ignore @@ -2238,11 +2269,12 @@ Sp_handler::show_create_sp(THD *thd, String *buf, sql_mode_t sql_mode) const { sql_mode_t old_sql_mode= thd->variables.sql_mode; + size_t agglen= (chistics.agg_type == GROUP_AGGREGATE)? 10 : 0; /* Make some room to begin with */ if (buf->alloc(100 + db.length + 1 + name.length + params.length + returns.length + - chistics.comment.length + 10 /* length of " DEFINER= "*/ + - USER_HOST_BUFF_SIZE)) + chistics.comment.length + 10 /* length of " DEFINER= "*/ + + agglen + USER_HOST_BUFF_SIZE)) return true; thd->variables.sql_mode= sql_mode; @@ -2250,6 +2282,8 @@ Sp_handler::show_create_sp(THD *thd, String *buf, if (ddl_options.or_replace()) buf->append(STRING_WITH_LEN("OR REPLACE ")); append_definer(thd, buf, &definer.user, &definer.host); + if (chistics.agg_type == GROUP_AGGREGATE) + buf->append(STRING_WITH_LEN("AGGREGATE ")); buf->append(type_lex_cstring()); buf->append(STRING_WITH_LEN(" ")); if (ddl_options.if_not_exists()) @@ -370,6 +370,7 @@ enum MYSQL_PROC_FIELD_COLLATION_CONNECTION, MYSQL_PROC_FIELD_DB_COLLATION, MYSQL_PROC_FIELD_BODY_UTF8, + MYSQL_PROC_FIELD_AGGREGATE, MYSQL_PROC_FIELD_COUNT }; diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 70ba5084914..342673bf619 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #ifdef USE_PRAGMA_IMPLEMENTATION diff --git a/sql/sp_cache.h b/sql/sp_cache.h index 51886a61ee9..a045ff5d3c5 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -21,8 +21,6 @@ #pragma interface /* gcc class implementation */ #endif -#include "my_global.h" /* ulong */ - /* Stored procedures/functions cache. This is used as follows: * Each thread has its own cache. diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3d5d886f15a..0b72c76b05b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_prepare.h" @@ -999,6 +999,12 @@ sp_head::execute(THD *thd, bool merge_da_on_success) bool err_status= FALSE; uint ip= 0; sql_mode_t save_sql_mode; + + // TODO(cvicentiu) See if you can drop this bit. This is used to resume + // execution from where we left off. + if (m_chistics.agg_type == GROUP_AGGREGATE) + ip= thd->spcont->instr_ptr; + bool save_abort_on_warning; Query_arena *old_arena; /* per-instruction arena */ @@ -1176,6 +1182,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) #if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); #endif + thd->spcont->quit_func= TRUE; break; } @@ -1245,7 +1252,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success) /* Reset sp_rcontext::end_partial_result_set flag. */ ctx->end_partial_result_set= FALSE; - } while (!err_status && !thd->killed && !thd->is_fatal_error); + } while (!err_status && !thd->killed && !thd->is_fatal_error && + !thd->spcont->pause_state); #if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); @@ -1261,9 +1269,14 @@ sp_head::execute(THD *thd, bool merge_da_on_success) thd->restore_active_arena(&execute_arena, &backup_arena); - thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error + /* Only pop cursors when we're done with group aggregate running. */ + if (m_chistics.agg_type != GROUP_AGGREGATE || + (m_chistics.agg_type == GROUP_AGGREGATE && thd->spcont->quit_func)) + thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error /* Restore all saved */ + if (m_chistics.agg_type == GROUP_AGGREGATE) + thd->spcont->instr_ptr= ip; thd->server_status= (thd->server_status & ~status_backup_mask) | old_server_status; old_packet.swap(thd->packet); DBUG_ASSERT(thd->change_list.is_empty()); @@ -1675,18 +1688,16 @@ err_with_cleanup: bool sp_head::execute_function(THD *thd, Item **argp, uint argcount, - Field *return_value_fld) + Field *return_value_fld, sp_rcontext **func_ctx, + Query_arena *call_arena) { ulonglong UNINIT_VAR(binlog_save_options); bool need_binlog_call= FALSE; uint arg_no; sp_rcontext *octx = thd->spcont; - sp_rcontext *nctx = NULL; char buf[STRING_BUFFER_USUAL_SIZE]; String binlog_buf(buf, sizeof(buf), &my_charset_bin); bool err_status= FALSE; - MEM_ROOT call_mem_root; - Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP); Query_arena backup_arena; DBUG_ENTER("sp_head::execute_function"); DBUG_PRINT("info", ("function %s", m_name.str)); @@ -1719,23 +1730,25 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, TODO: we should create sp_rcontext once per command and reuse it on subsequent executions of a function/trigger. */ - init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); - thd->set_n_backup_active_arena(&call_arena, &backup_arena); - - if (!(nctx= rcontext_create(thd, return_value_fld, argp, argcount))) + if (!(*func_ctx)) { - thd->restore_active_arena(&call_arena, &backup_arena); - err_status= TRUE; - goto err_with_cleanup; - } + thd->set_n_backup_active_arena(call_arena, &backup_arena); - /* - We have to switch temporarily back to callers arena/memroot. - Function arguments belong to the caller and so the may reference - memory which they will allocate during calculation long after - this function call will be finished (e.g. in Item::cleanup()). - */ - thd->restore_active_arena(&call_arena, &backup_arena); + if (!(*func_ctx= rcontext_create(thd, return_value_fld, argp, argcount))) + { + thd->restore_active_arena(call_arena, &backup_arena); + err_status= TRUE; + goto err_with_cleanup; + } + + /* + We have to switch temporarily back to callers arena/memroot. + Function arguments belong to the caller and so the may reference + memory which they will allocate during calculation long after + this function call will be finished (e.g. in Item::cleanup()). + */ + thd->restore_active_arena(call_arena, &backup_arena); + } /* Pass arguments. */ for (arg_no= 0; arg_no < argcount; arg_no++) @@ -1743,7 +1756,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, /* Arguments must be fixed in Item_func_sp::fix_fields */ DBUG_ASSERT(argp[arg_no]->fixed); - if ((err_status= nctx->set_variable(thd, arg_no, &(argp[arg_no])))) + if ((err_status= (*func_ctx)->set_variable(thd, arg_no, &(argp[arg_no])))) goto err_with_cleanup; } @@ -1775,7 +1788,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (arg_no) binlog_buf.append(','); - Item *item= nctx->get_item(arg_no); + Item *item= (*func_ctx)->get_item(arg_no); str_value= item->type_handler()->print_item_value(thd, item, &str_value_holder); if (str_value) @@ -1785,7 +1798,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } binlog_buf.append(')'); } - thd->spcont= nctx; + thd->spcont= *func_ctx; #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx; @@ -1826,11 +1839,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, sp_rcontext and allocate all these objects (and sp_rcontext itself) on it directly rather than juggle with arenas. */ - thd->set_n_backup_active_arena(&call_arena, &backup_arena); + thd->set_n_backup_active_arena(call_arena, &backup_arena); err_status= execute(thd, TRUE); - thd->restore_active_arena(&call_arena, &backup_arena); + thd->restore_active_arena(call_arena, &backup_arena); if (need_binlog_call) { @@ -1856,11 +1869,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } } - if (!err_status) + if (!err_status && thd->spcont->quit_func) { /* We need result only in function but not in trigger */ - if (!nctx->is_return_value_set()) + if (!(*func_ctx)->is_return_value_set()) { my_error(ER_SP_NORETURNEND, MYF(0), m_name.str); err_status= TRUE; @@ -1872,9 +1885,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, #endif err_with_cleanup: - delete nctx; - call_arena.free_items(); - free_root(&call_mem_root, MYF(0)); thd->spcont= octx; /* @@ -2046,13 +2056,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length, m_name.str)); } + save_enable_slow_log= thd->enable_slow_log; - if (!(m_flags & LOG_SLOW_STATEMENTS) && save_enable_slow_log) + + /* + Disable slow log if: + - Slow logging is enabled (no change needed) + - This is a normal SP (not event log) + - If we have not explicitely disabled logging of SP + */ + if (save_enable_slow_log && + ((!(m_flags & LOG_SLOW_STATEMENTS) && + (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SP)))) { DBUG_PRINT("info", ("Disabling slow log for the execution")); thd->enable_slow_log= FALSE; } - if (!(m_flags & LOG_GENERAL_LOG) && !(thd->variables.option_bits & OPTION_LOG_OFF)) + + /* + Disable general log if: + - If general log is enabled (no change needed) + - This is a normal SP (not event log) + - If we have not explicitely disabled logging of SP + */ + if (!(thd->variables.option_bits & OPTION_LOG_OFF) && + (!(m_flags & LOG_GENERAL_LOG) && + (thd->variables.log_disabled_statements & LOG_DISABLE_SP))) { DBUG_PRINT("info", ("Disabling general log for the execution")); save_log_general= true; @@ -2075,6 +2104,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (save_log_general) thd->variables.option_bits &= ~OPTION_LOG_OFF; thd->enable_slow_log= save_enable_slow_log; + /* In the case when we weren't able to employ reuse mechanism for OUT/INOUT paranmeters, we should reallocate memory. This @@ -2329,9 +2359,7 @@ sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) */ continue; } - if (my_strcasecmp(system_charset_info, - bp->lab->name.str, - lab->name.str) == 0) + if (lex_string_cmp(system_charset_info, &bp->lab->name, &lab->name) == 0) { if (bp->instr_type == GOTO) { @@ -2466,7 +2494,6 @@ sp_head::set_chistics(const st_sp_chistics &chistics) m_chistics.comment.length); } - void sp_head::set_info(longlong created, longlong modified, const st_sp_chistics &chistics, sql_mode_t sql_mode) @@ -3242,14 +3269,29 @@ int sp_instr_stmt::execute(THD *thd, uint *nextp) { int res; + bool save_enable_slow_log; + const CSET_STRING query_backup= thd->query_string; + QUERY_START_TIME_INFO time_info; + Sub_statement_state backup_state; DBUG_ENTER("sp_instr_stmt::execute"); DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command())); - const CSET_STRING query_backup= thd->query_string; #if defined(ENABLED_PROFILING) /* This s-p instr is profilable and will be captured. */ thd->profiling.set_query_source(m_query.str, m_query.length); #endif + + if ((save_enable_slow_log= thd->enable_slow_log)) + { + /* + Save start time info for the CALL statement and overwrite it with the + current time for log_slow_statement() to log the individual query timing. + */ + thd->get_time(&time_info); + thd->set_time(); + } + thd->store_slow_query_state(&backup_state); + if (!(res= alloc_query(thd, m_query.str, m_query.length)) && !(res=subst_spvars(thd, this, &m_query))) { @@ -3262,6 +3304,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) if (query_cache_send_result_to_client(thd, thd->query(), thd->query_length()) <= 0) { + thd->reset_slow_query_state(); res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this); bool log_slow= !res && thd->enable_slow_log; @@ -3281,6 +3324,15 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) if (log_slow) log_slow_statement(thd); + + /* + Restore enable_slow_log, that can be changed by a admin or call + command + */ + thd->enable_slow_log= save_enable_slow_log; + + /* Add the number of rows to thd for the 'call' statistics */ + thd->add_slow_query_state(&backup_state); } else { @@ -3301,6 +3353,10 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) thd->get_stmt_da()->reset_diagnostics_area(); } } + /* Restore the original query start time */ + if (thd->enable_slow_log) + thd->set_time(&time_info); + DBUG_RETURN(res || thd->is_error()); } @@ -3403,7 +3459,7 @@ sp_instr_set::print(String *str) str->qs_append(STRING_WITH_LEN("set ")); if (var) { - str->qs_append(var->name.str, var->name.length); + str->qs_append(&var->name); str->qs_append('@'); } str->qs_append(m_offset); @@ -3449,9 +3505,9 @@ sp_instr_set_row_field::print(String *str) if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("set ")); - str->qs_append(var->name.str, var->name.length); + str->qs_append(&var->name); str->qs_append('.'); - str->qs_append(def->field_name.str, def->field_name.length); + str->qs_append(&def->field_name); str->qs_append('@'); str->qs_append(m_offset); str->qs_append('['); @@ -3507,13 +3563,13 @@ sp_instr_set_row_field_by_name::print(String *str) if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("set ")); - str->qs_append(var->name.str, var->name.length); + str->qs_append(&var->name); str->qs_append('.'); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append('@'); str->qs_append(m_offset); str->qs_append("[\"",2); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append("\"]",2); str->qs_append(' '); m_value->print(str, enum_query_type(QT_ORDINARY | @@ -4166,12 +4222,41 @@ sp_instr_cfetch::print(String *str) if (str->reserve(pv->name.length+SP_INSTR_UINT_MAXLEN+2)) return; str->qs_append(' '); - str->qs_append(pv->name.str, pv->name.length); + str->qs_append(&pv->name); str->qs_append('@'); str->qs_append(pv->offset); } } +int +sp_instr_agg_cfetch::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_cfetch::execute"); + int res= 0; + if (!thd->spcont->instr_ptr) + { + *nextp= m_ip+1; + thd->spcont->instr_ptr= m_ip + 1; + } + else if (!thd->spcont->pause_state) + thd->spcont->pause_state= TRUE; + else + { + thd->spcont->pause_state= FALSE; + if (thd->server_status == SERVER_STATUS_LAST_ROW_SENT) + { + my_message(ER_SP_FETCH_NO_DATA, + ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0)); + res= -1; + thd->spcont->quit_func= TRUE; + } + else + *nextp= m_ip + 1; + } + DBUG_RETURN(res); +} + + /* sp_instr_cursor_copy_struct class functions @@ -4568,7 +4653,8 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->init_one_table_for_prelocking(key_buff, stab->db_length, key_buff + stab->db_length + 1, stab->table_name_length, key_buff + stab->db_length + stab->table_name_length + 2, - stab->lock_type, true, belong_to_view, stab->trg_event_map, + stab->lock_type, TABLE_LIST::PRELOCK_ROUTINE, belong_to_view, + stab->trg_event_map, query_tables_last_ptr); tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST)); diff --git a/sql/sp_head.h b/sql/sp_head.h index f8a819bbe94..7e477544958 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -27,7 +27,6 @@ are dependencies on include order for set_var.h and item.h. This will be resolved later. */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_class.h" // THD, set_var.h: THD #include "set_var.h" // Item #include "sp_pcontext.h" // sp_pcontext @@ -165,7 +164,10 @@ public: /* Marks routines that have column type references: DECLARE a t1.a%TYPE; */ - HAS_COLUMN_TYPE_REFS= 8192 + HAS_COLUMN_TYPE_REFS= 8192, + /* Set if has FETCH GROUP NEXT ROW instr. Used to ensure that only + functions with AGGREGATE keyword use the instr. */ + HAS_AGGREGATE_INSTR= 16384 }; const Sp_handler *m_handler; @@ -198,6 +200,7 @@ public: enum_sp_suid_behaviour suid() const { return m_chistics.suid; } bool detistic() const { return m_chistics.detistic; } enum_sp_data_access daccess() const { return m_chistics.daccess; } + enum_sp_aggregate_type agg_type() const { return m_chistics.agg_type; } /** Is this routine being executed? */ @@ -344,7 +347,8 @@ public: GRANT_INFO *grant_info); bool - execute_function(THD *thd, Item **args, uint argcount, Field *return_fld); + execute_function(THD *thd, Item **args, uint argcount, Field *return_fld, + sp_rcontext **nctx, Query_arena *call_arena); bool execute_procedure(THD *thd, List<Item> *args); @@ -720,6 +724,10 @@ public: const LEX_CSTRING &table); void set_chistics(const st_sp_chistics &chistics); + inline void set_chistics_agg_type(enum enum_sp_aggregate_type type) + { + m_chistics.agg_type= type; + } void set_info(longlong created, longlong modified, const st_sp_chistics &chistics, sql_mode_t sql_mode); @@ -1822,6 +1830,32 @@ private: }; // class sp_instr_cfetch : public sp_instr +/* +This class is created for the special fetch instruction +FETCH GROUP NEXT ROW, used in the user-defined aggregate +functions +*/ + +class sp_instr_agg_cfetch : public sp_instr +{ + sp_instr_agg_cfetch(const sp_instr_cfetch &); /**< Prevent use of these */ + void operator=(sp_instr_cfetch &); + +public: + + sp_instr_agg_cfetch(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx){} + + virtual ~sp_instr_agg_cfetch() + {} + + virtual int execute(THD *thd, uint *nextp); + + virtual void print(String *str){}; +}; // class sp_instr_agg_cfetch : public sp_instr + + + class sp_instr_error : public sp_instr { diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index d98f8005945..1e2081e3479 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #ifdef USE_PRAGMA_IMPLEMENTATION @@ -132,8 +132,8 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope) bool cmp_labels(sp_label *a, sp_label *b) { - return (my_strcasecmp(system_charset_info, a->name.str, b->name.str) == 0 - && a->type == b->type); + return (lex_string_cmp(system_charset_info, &a->name, &b->name) == 0 && + a->type == b->type); } sp_pcontext *sp_pcontext::pop_context() @@ -304,7 +304,7 @@ sp_label *sp_pcontext::find_goto_label(const LEX_CSTRING *name, bool recusive) while ((lab= li++)) { - if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0) + if (lex_string_cmp(system_charset_info, name, &lab->name) == 0) return lab; } @@ -341,7 +341,7 @@ sp_label *sp_pcontext::find_label(const LEX_CSTRING *name) while ((lab= li++)) { - if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0) + if (lex_string_cmp(system_charset_info, name, &lab->name) == 0) return lab; } @@ -416,6 +416,7 @@ static sp_condition_value // Errors cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN, "24000"), cond_dup_val_on_index(ER_DUP_ENTRY, "23000"), + cond_dup_val_on_index2(ER_DUP_ENTRY_WITH_KEY_NAME, "23000"), cond_too_many_rows(ER_TOO_MANY_ROWS, "42000"); @@ -426,6 +427,7 @@ static sp_condition sp_predefined_conditions[]= // Errors sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor), sp_condition(C_STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index), + sp_condition(C_STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index2), sp_condition(C_STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows) }; diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index d82fffc69b0..740941937e8 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #ifdef USE_PRAGMA_IMPLEMENTATION @@ -40,9 +40,8 @@ sp_rcontext::sp_rcontext(const sp_head *owner, Field *return_value_fld, bool in_sub_stmt) :end_partial_result_set(false), -#ifndef DBUG_OFF + pause_state(false), quit_func(false), instr_ptr(0), m_sp(owner), -#endif m_root_parsing_ctx(root_parsing_ctx), m_var_table(NULL), m_return_value_fld(return_value_fld), diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 26c06512417..0999271ebde 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -178,12 +178,14 @@ public: /// (if one is found). Otherwise the client will hang due to a violation /// of the client/server protocol. bool end_partial_result_set; + bool pause_state; + bool quit_func; + uint instr_ptr; -#ifndef DBUG_OFF /// The stored program for which this runtime context is created. Used for /// checking if correct runtime context is used for variable handling. + /// Also used by slow log. const sp_head *m_sp; -#endif ///////////////////////////////////////////////////////////////////////// // SP-variables. diff --git a/sql/spatial.cc b/sql/spatial.cc index 2aca528dd15..2a07ef2ecbe 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "spatial.h" #include "gstream.h" // Gis_read_stream diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 35a9261cdb4..85df471a3c0 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -25,7 +25,7 @@ in the relevant fields. Empty strings comes last. */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS #include "sql_base.h" // close_mysql_tables @@ -1594,12 +1594,10 @@ static const char *fix_plugin_ptr(const char *name) */ static bool fix_user_plugin_ptr(ACL_USER *user) { - if (my_strcasecmp(system_charset_info, user->plugin.str, - native_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &native_password_plugin_name) == 0) user->plugin= native_password_plugin_name; else - if (my_strcasecmp(system_charset_info, user->plugin.str, - old_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &old_password_plugin_name) == 0) user->plugin= old_password_plugin_name; else return true; @@ -1639,12 +1637,10 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) DBUG_ASSERT(user->plugin.length || !user->auth.length); DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length))); - if (my_strcasecmp(system_charset_info, user->plugin.str, - native_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &native_password_plugin_name) == 0) check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; else - if (my_strcasecmp(system_charset_info, user->plugin.str, - old_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &old_password_plugin_name) == 0) check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; else if (user->plugin.length) @@ -1832,7 +1828,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { if (host_table.init_read_record(&read_record_info, thd)) DBUG_RETURN(true); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_HOST host; update_hostname(&host.host, get_field(&acl_memroot, host_table.host())); @@ -1936,7 +1932,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) } allow_all_hosts=0; - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_USER user; bool is_role= FALSE; @@ -2148,7 +2144,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) const Db_table& db_table= tables.db_table(); if (db_table.init_read_record(&read_record_info, thd)) DBUG_RETURN(TRUE); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_DB db; char *db_name; @@ -2215,7 +2211,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { if (proxies_priv_table.init_read_record(&read_record_info, thd)) DBUG_RETURN(TRUE); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_PROXY_USER proxy; proxy.init(proxies_priv_table, &acl_memroot); @@ -2244,7 +2240,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) MEM_ROOT temp_root; init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host())); char *username= safe_str(get_field(&temp_root, roles_mapping_table.user())); @@ -3180,7 +3176,7 @@ static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr) { DBUG_ASSERT(!found); delete_dynamic_element(array, i); - IF_DBUG(found= true, break); + IF_DBUG_ASSERT(found= true, break); } } DBUG_ASSERT(found); @@ -7602,10 +7598,18 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; ulong orig_want_access= original_want_access; - if (t_ref->sequence) + /* + If sequence is used as part of NEXT VALUE, PREVIUS VALUE or SELECT, + we need to modify the requested access rights depending on how the + sequence is used. + */ + if (t_ref->sequence & + (orig_want_access & + (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL))) { - /* We want to have either SELECT or INSERT rights to sequences depending - on how they are accessed + /* + We want to have either SELECT or INSERT rights to sequences depending + on how they are accessed */ orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ? INSERT_ACL : SELECT_ACL); @@ -8404,18 +8408,18 @@ static void add_user_parameters(String *result, ACL_USER* acl_user, { DBUG_ASSERT(acl_user->salt_len); result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); - result->append(acl_user->auth_string.str, acl_user->auth_string.length); + result->append(&acl_user->auth_string); result->append('\''); } } else { result->append(STRING_WITH_LEN(" IDENTIFIED VIA ")); - result->append(acl_user->plugin.str, acl_user->plugin.length); + result->append(&acl_user->plugin); if (acl_user->auth_string.length) { result->append(STRING_WITH_LEN(" USING '")); - result->append(acl_user->auth_string.str, acl_user->auth_string.length); + result->append(&acl_user->auth_string); result->append('\''); } } @@ -11233,7 +11237,7 @@ applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr) if (!is_role) { if (data->user->default_rolename.length && - !strcmp(data->user->default_rolename.str, role->user.str)) + !lex_string_eq(&data->user->default_rolename, &role->user)) table->field[3]->store(STRING_WITH_LEN("YES"), cs); else table->field[3]->store(STRING_WITH_LEN("NO"), cs); @@ -12845,8 +12849,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, restarted and a server auth plugin will read the data that the client has just send. Cache them to return in the next server_mpvio_read_packet(). */ - if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, - plugin_name(mpvio->plugin)->str) != 0) + if (lex_string_eq(&mpvio->acl_user->plugin, + plugin_name(mpvio->plugin)) != 0) { mpvio->cached_client_reply.pkt= passwd; mpvio->cached_client_reply.pkt_len= passwd_len; @@ -13276,8 +13280,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) { DBUG_ASSERT(mpvio.acl_user); DBUG_ASSERT(command == COM_CHANGE_USER || - my_strcasecmp(system_charset_info, auth_plugin_name->str, - mpvio.acl_user->plugin.str)); + lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin)); auth_plugin_name= &mpvio.acl_user->plugin; res= do_auth_once(thd, auth_plugin_name, &mpvio); } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index cc6e16a19e5..e299844a71a 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -17,7 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "violite.h" /* SSL_type */ #include "sql_class.h" /* LEX_COLUMN */ diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index c1c053670af..4ed8234aa64 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -14,7 +14,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 "sql_class.h" // THD and my_global.h +#include "mariadb.h" +#include "sql_class.h" // THD #include "keycaches.h" // get_key_cache #include "sql_base.h" // Open_table_context #include "lock.h" // MYSQL_OPEN_* @@ -447,10 +448,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, int compl_result_code; bool need_repair_or_alter= 0; wait_for_commit* suspended_wfc; - DBUG_ENTER("mysql_admin_table"); DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options)); + thd->prepare_logs_for_admin_command(); + field_list.push_back(item= new (thd->mem_root) Item_empty_string(thd, "Table", NAME_CHAR_LEN * 2), thd->mem_root); @@ -808,7 +810,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Here we close and reopen table in read mode because operation of collecting statistics is long and it will be better do not block the table completely. - InnoDB/XtraDB will allow read/write and MyISAM read/insert. + InnoDB will allow read/write and MyISAM read/insert. */ trans_commit_stmt(thd); trans_commit(thd); @@ -1302,7 +1304,6 @@ bool Sql_cmd_analyze_table::execute(THD *thd) FALSE, UINT_MAX, FALSE)) goto error; WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); - thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "analyze", lock_type, 1, 0, 0, 0, &handler::ha_analyze, 0); @@ -1333,7 +1334,6 @@ bool Sql_cmd_check_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL, first_table, TRUE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check", lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0, @@ -1358,7 +1358,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd) FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); - thd->enable_slow_log= opt_log_slow_admin_statements; + res= (specialflag & SPECIAL_NO_NEW_FUNC) ? mysql_recreate_table(thd, first_table, true) : mysql_admin_table(thd, first_table, &m_lex->check_opt, @@ -1390,7 +1390,8 @@ bool Sql_cmd_repair_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->enable_slow_log&= !MY_TEST(thd->variables.log_slow_disabled_statements & + LOG_SLOW_DISABLE_ADMIN); WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair", TL_WRITE, 1, diff --git a/sql/sql_alloc.h b/sql/sql_alloc.h new file mode 100644 index 00000000000..4db12964f0a --- /dev/null +++ b/sql/sql_alloc.h @@ -0,0 +1,56 @@ +#ifndef SQL_ALLOC_INCLUDED +#define SQL_ALLOC_INCLUDED +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2017, MariaDB AB + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include <my_sys.h> /* alloc_root, MEM_ROOT, TRASH */ + +THD *thd_get_current_thd(); + +/* mysql standard class memory allocator */ + +class Sql_alloc +{ +public: + static void *operator new(size_t size) throw () + { + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(thd_get_current_thd(), uint(size)); + } + static void *operator new[](size_t size) throw () + { + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(thd_get_current_thd(), uint(size)); + } + static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } + static void *operator new(size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } + static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); } + static void operator delete(void *ptr, MEM_ROOT *mem_root) + { /* never called */ } + static void operator delete[](void *ptr, MEM_ROOT *mem_root) + { /* never called */ } + static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); } +#ifdef HAVE_valgrind + bool dummy_for_valgrind; + inline Sql_alloc() :dummy_for_valgrind(0) {} +#else + inline Sql_alloc() {} +#endif + inline ~Sql_alloc() {} +}; +#endif /* SQL_ALLOC_INCLUDED */ diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 0194f26d217..3d1e4c0fa65 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_parse.h" // check_access #include "sql_table.h" // mysql_alter_table, // mysql_exchange_partition @@ -89,7 +90,7 @@ Alter_table_ctx::Alter_table_ctx() new_db(NULL), new_name(NULL), new_alias(NULL), fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_table(NULL) -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS , tmp_table(false) #endif { @@ -109,7 +110,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, new_db(new_db_arg), new_name(new_name_arg), fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_table(NULL) -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS , tmp_table(false) #endif { @@ -186,7 +187,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, this case. This fact is enforced with assert. */ build_tmptable_filename(thd, tmp_path, sizeof(tmp_path)); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS tmp_table= true; #endif } @@ -309,7 +310,7 @@ bool Sql_cmd_alter_table::execute(THD *thd) "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->prepare_logs_for_admin_command(); #ifdef WITH_WSREP if ((!thd->is_current_stmt_binlog_format_row() || @@ -354,7 +355,7 @@ bool Sql_cmd_discard_import_tablespace::execute(THD *thd) if (check_grant(thd, ALTER_ACL, table_list, false, UINT_MAX, false)) return true; - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->prepare_logs_for_admin_command(); /* Check if we attempt to alter mysql.slow_log or diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 5c898a5407c..25e377be8de 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -331,7 +331,7 @@ private: char new_path[FN_REFLEN + 1]; char tmp_path[FN_REFLEN + 1]; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS /** Indicates that we are altering temporary table. Used only in asserts. */ bool tmp_table; #endif diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index d6af0410ad2..351ddb452d8 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -29,7 +29,7 @@ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "procedure.h" #include "sql_analyse.h" @@ -298,9 +298,10 @@ bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num) } // get_ev_num_info -void free_string(String *s) +int free_string(String *s) { s->free(); + return 0; } @@ -374,7 +375,7 @@ void field_str::add() if (!tree_insert(&tree, (void*) &s, 0, tree.custom_arg)) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } else { @@ -382,7 +383,7 @@ void field_str::add() if ((treemem += length) > pc->max_treemem) { room_in_tree = 0; // Remove tree, too big tree - delete_tree(&tree); + delete_tree(&tree, 0); } } } @@ -441,7 +442,7 @@ void field_real::add() if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -450,7 +451,7 @@ void field_real::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } @@ -507,7 +508,7 @@ void field_decimal::add() if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -516,7 +517,7 @@ void field_decimal::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } @@ -574,7 +575,7 @@ void field_longlong::add() if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -583,7 +584,7 @@ void field_longlong::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } @@ -630,7 +631,7 @@ void field_ulonglong::add() if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -639,7 +640,7 @@ void field_ulonglong::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 820877f2a69..6704de4ed6d 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -68,7 +68,7 @@ int compare_ulonglong2(void* cmp_arg __attribute__((unused)), int compare_decimal2(int* len, const char *s, const char *t); Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list); -void free_string(String*); +int free_string(String*); class analyse; class field_info :public Sql_alloc @@ -86,7 +86,7 @@ public: nulls(0), min_length(0), max_length(0), room_in_tree(1), found(0),item(a), pc(b) {}; - virtual ~field_info() { delete_tree(&tree); } + virtual ~field_info() { delete_tree(&tree, 0); } virtual void add() = 0; virtual void get_opt_type(String*, ha_rows) = 0; virtual String *get_min_arg(String *) = 0; diff --git a/sql/sql_analyze_stmt.cc b/sql/sql_analyze_stmt.cc index ac3797aae60..2c09772bd66 100644 --- a/sql/sql_analyze_stmt.cc +++ b/sql/sql_analyze_stmt.cc @@ -18,7 +18,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "my_json_writer.h" diff --git a/sql/sql_array.h b/sql/sql_array.h index bbaa653b177..341ea9a651b 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -258,4 +258,6 @@ public: } }; +typedef Bounds_checked_array<Item*> Ref_ptr_array; + #endif /* SQL_ARRAY_INCLUDED */ diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index 8134adca13f..7c8b355b8d0 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_audit.h" diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 96f9d0caece..7a11f5aa113 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -18,8 +18,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> - #include <mysql/plugin_audit.h> #include "sql_class.h" @@ -155,6 +153,7 @@ static inline void mysql_audit_general(THD *thd, uint event_subtype, int error_code, const char *msg) { + DBUG_ENTER("mysql_audit_general"); if (mysql_audit_general_enabled()) { char user_buff[MAX_USER_HOST_SIZE]; @@ -195,6 +194,7 @@ void mysql_audit_general(THD *thd, uint event_subtype, mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, &event); } + DBUG_VOID_RETURN; } static inline diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 87ef4e0c03d..a441b0ef6a6 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -17,7 +17,7 @@ /* Basic functions needed by many modules */ -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" // setup_table_map #include "sql_priv.h" #include "unireg.h" @@ -878,6 +878,12 @@ void close_thread_table(THD *thd, TABLE **table_ptr) table->file->update_global_index_stats(); } + /* + This look is needed to allow THD::notify_shared_lock() to + traverse the thd->open_tables list without having to worry that + some of the tables are removed from under it + */ + mysql_mutex_lock(&thd->LOCK_thd_data); *table_ptr=table->next; mysql_mutex_unlock(&thd->LOCK_thd_data); @@ -1096,14 +1102,32 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, table= table->find_table_for_update(); - if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM) + if (table->table && + table->table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) { TABLE_LIST *child; dup= NULL; /* Check duplicates of all merge children. */ - for (child= table->next_global; child && child->parent_l == table; + for (child= table->next_global; child; child= child->next_global) { + if (child->table && + child->table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) + continue; + + /* + Ensure that the child has one parent that is the table that is + updated. + */ + TABLE_LIST *tmp_parent= child; + while ((tmp_parent= tmp_parent->parent_l)) + { + if (tmp_parent == table) + break; + } + if (!tmp_parent) + break; + if ((dup= find_dup_table(thd, child, child->next_global, check_alias))) break; } @@ -1112,6 +1136,8 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, dup= find_dup_table(thd, table, table_list, check_alias); return dup; } + + /* Issue correct error message in case we found 2 duplicate tables which prevent some update operation @@ -1136,10 +1162,10 @@ void update_non_unique_table_error(TABLE_LIST *update, update->view == duplicate->view || update->view_name.length != duplicate->view_name.length || update->view_db.length != duplicate->view_db.length || - my_strcasecmp(table_alias_charset, - update->view_name.str, duplicate->view_name.str) != 0 || - my_strcasecmp(table_alias_charset, - update->view_db.str, duplicate->view_db.str) != 0) + lex_string_cmp(table_alias_charset, + &update->view_name, &duplicate->view_name) != 0 || + lex_string_cmp(table_alias_charset, + &update->view_db, &duplicate->view_db) != 0) { /* it is not the same view repeated (but it can be parts of the same copy @@ -3502,7 +3528,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, */ if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) + (tables->lock_type >= TL_WRITE_ALLOW_WRITE || thd->lex->default_used)) { bool need_prelocking= FALSE; TABLE_LIST **save_query_tables_last= lex->query_tables_last; @@ -4089,7 +4115,7 @@ restart: continue; /* Schema tables may not have a TABLE object here. */ - if (tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM) + if (tbl->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) { /* MERGE tables need to access parent and child TABLE_LISTs. */ DBUG_ASSERT(tbl->pos_in_table_list == tables); @@ -4208,7 +4234,7 @@ static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, for (; tl; tl= tl->next_global ) { if (tl->lock_type >= lock_type && - tl->prelocking_placeholder == TABLE_LIST::FK && + tl->prelocking_placeholder == TABLE_LIST::PRELOCK_FK && strcmp(tl->db, db->str) == 0 && strcmp(tl->table_name, table->str) == 0) return true; @@ -4217,6 +4243,55 @@ static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, } +static bool internal_table_exists(TABLE_LIST *global_list, + const char *table_name) +{ + do + { + if (global_list->table_name == table_name) + return 1; + } while ((global_list= global_list->next_global)); + return 0; +} + + +static bool +add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *tables) +{ + TABLE_LIST *global_table_list= prelocking_ctx->query_tables; + + do + { + /* + Skip table if already in the list. Can happen with prepared statements + */ + if (tables->next_local && + internal_table_exists(global_table_list, tables->table_name)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + if (!tl) + return TRUE; + tl->init_one_table_for_prelocking(tables->db, + strlen(tables->db), + tables->table_name, + strlen(tables->table_name), + NULL, tables->lock_type, + TABLE_LIST::PRELOCK_NONE, + 0, 0, + &prelocking_ctx->query_tables_last); + /* + Store link to the new table_list that will be used by open so that + Item_func_nextval() can find it + */ + tables->next_local= tl; + } while ((tables= tables->next_global)); + return FALSE; +} + + + /** Defines how prelocking algorithm for DML statements should handle table list elements: @@ -4243,21 +4318,23 @@ bool DML_prelocking_strategy:: handle_table(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *table_list, bool *need_prelocking) { + TABLE *table= table_list->table; /* We rely on a caller to check that table is going to be changed. */ - DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE); + DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE || + thd->lex->default_used); if (table_list->trg_event_map) { - if (table_list->table->triggers) + if (table->triggers) { *need_prelocking= TRUE; - if (table_list->table->triggers-> + if (table->triggers-> add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list)) return TRUE; } - if (table_list->table->file->referenced_by_foreign_key()) + if (table->file->referenced_by_foreign_key()) { List <FOREIGN_KEY_INFO> fk_list; List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); @@ -4266,7 +4343,7 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, arena= thd->activate_stmt_arena_if_needed(&backup); - table_list->table->file->get_parent_foreign_key_list(thd, &fk_list); + table->file->get_parent_foreign_key_list(thd, &fk_list); if (thd->is_error()) { if (arena) @@ -4295,21 +4372,96 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, continue; TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); - tl->init_one_table_for_prelocking(fk->foreign_db->str, fk->foreign_db->length, - fk->foreign_table->str, fk->foreign_table->length, - NULL, lock_type, false, table_list->belong_to_view, - op, &prelocking_ctx->query_tables_last); + tl->init_one_table_for_prelocking(fk->foreign_db->str, + fk->foreign_db->length, + fk->foreign_table->str, + fk->foreign_table->length, + NULL, lock_type, + TABLE_LIST::PRELOCK_FK, + table_list->belong_to_view, op, + &prelocking_ctx->query_tables_last); } if (arena) thd->restore_active_arena(arena, &backup); } } + /* Open any tables used by DEFAULT (like sequence tables) */ + if (table->internal_tables && + ((sql_command_flags[thd->lex->sql_command] & CF_INSERTS_DATA) || + thd->lex->default_used)) + { + Query_arena *arena, backup; + bool error; + arena= thd->activate_stmt_arena_if_needed(&backup); + error= add_internal_tables(thd, prelocking_ctx, + table->internal_tables); + if (arena) + thd->restore_active_arena(arena, &backup); + if (error) + { + *need_prelocking= TRUE; + return TRUE; + } + } return FALSE; } /** + Open all tables used by DEFAULT functions. + + This is different from normal open_and_lock_tables() as we may + already have other tables opened and locked and we have to merge the + new table with the old ones. +*/ + +bool open_and_lock_internal_tables(TABLE *table, bool lock_table) +{ + THD *thd= table->in_use; + TABLE_LIST *tl; + MYSQL_LOCK *save_lock,*new_lock; + DBUG_ENTER("open_internal_tables"); + + /* remove pointer to old select_lex which is already destroyed */ + for (tl= table->internal_tables ; tl ; tl= tl->next_global) + tl->select_lex= 0; + + uint counter; + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST *tmp= table->internal_tables; + DML_prelocking_strategy prelocking_strategy; + + if (open_tables(thd, thd->lex->create_info, &tmp, &counter, 0, + &prelocking_strategy)) + goto err; + + if (lock_table) + { + save_lock= thd->lock; + thd->lock= 0; + if (lock_tables(thd, table->internal_tables, counter, + MYSQL_LOCK_USE_MALLOC)) + goto err; + + if (!(new_lock= mysql_lock_merge(save_lock, thd->lock))) + { + thd->lock= save_lock; + mysql_unlock_tables(thd, save_lock, 1); + /* We don't have to close tables as caller will do that */ + goto err; + } + thd->lock= new_lock; + } + DBUG_RETURN(0); + +err: + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(1); +} + + +/** Defines how prelocking algorithm for DML statements should handle view - all view routines should be added to the prelocking set. @@ -4460,7 +4612,7 @@ static bool check_lock_and_start_stmt(THD *thd, Prelocking placeholder is not set for TABLE_LIST that are directly used by TOP level statement. */ - DBUG_ASSERT(table_list->prelocking_placeholder == false); + DBUG_ASSERT(table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_NONE); /* TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only @@ -4473,7 +4625,6 @@ static bool check_lock_and_start_stmt(THD *thd, Last argument routine_modifies_data for read_lock_type_for_table() is ignored, as prelocking placeholder will never be set here. */ - DBUG_ASSERT(table_list->prelocking_placeholder == false); if (table_list->lock_type == TL_WRITE_DEFAULT) lock_type= thd->update_lock_default; else if (table_list->lock_type == TL_READ_DEFAULT) @@ -4481,8 +4632,8 @@ static bool check_lock_and_start_stmt(THD *thd, else lock_type= table_list->lock_type; - if ((int) lock_type > (int) TL_WRITE_ALLOW_WRITE && - (int) table_list->table->reginfo.lock_type <= (int) TL_WRITE_ALLOW_WRITE) + if ((int) lock_type >= (int) TL_WRITE_ALLOW_WRITE && + (int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_WRITE) { my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->table->alias.c_ptr()); @@ -4636,7 +4787,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, */ DBUG_ASSERT(table_list->table); table= table_list->table; - if (table->file->ht->db_type == DB_TYPE_MRG_MYISAM) + if (table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) { /* A MERGE table must not come here. */ /* purecov: begin tested */ @@ -5502,6 +5653,10 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, if (field_ptr && *field_ptr) { + if ((*field_ptr)->field_visibility == COMPLETELY_INVISIBLE && + DBUG_EVALUATE_IF("test_completely_invisible", 0, 1)) + DBUG_RETURN((Field*)0); + *cached_field_index_ptr= (uint)(field_ptr - table->field); field= *field_ptr; } @@ -6130,8 +6285,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, item is not fix_field()'ed yet. */ if (item_field->field_name.str && item_field->table_name && - !my_strcasecmp(system_charset_info, item_field->field_name.str, - field_name->str) && + !lex_string_cmp(system_charset_info, &item_field->field_name, + field_name) && !my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && (!db_name || (item_field->db_name && @@ -6160,11 +6315,11 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, } else { - int fname_cmp= my_strcasecmp(system_charset_info, - item_field->field_name.str, - field_name->str); - if (!my_strcasecmp(system_charset_info, - item_field->name.str,field_name->str)) + bool fname_cmp= lex_string_cmp(system_charset_info, + &item_field->field_name, + field_name); + if (!lex_string_cmp(system_charset_info, + &item_field->name, field_name)) { /* If table name was not given we should scan through aliases @@ -6210,7 +6365,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, { if (is_ref_by_name && find->name.str && item->name.str && find->name.length == item->name.length && - !my_strcasecmp(system_charset_info,item->name.str, find->name.str)) + !lex_string_cmp(system_charset_info, &item->name, &find->name)) { found= li.ref(); *counter= i; @@ -6427,8 +6582,8 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, here. These columns must be checked only on unqualified reference by name (e.g. in SELECT list). */ - if (!my_strcasecmp(system_charset_info, field_name_1->str, - cur_field_name_2->str)) + if (!lex_string_cmp(system_charset_info, field_name_1, + cur_field_name_2)) { DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); if (cur_nj_col_2->is_common || @@ -7581,6 +7736,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, for (; !field_iterator.end_of_fields(); field_iterator.next()) { + /* + field() is always NULL for views (see, e.g. Field_iterator_view or + Field_iterator_natural_join). + But view fields can never be invisible. + */ + if ((field= field_iterator.field()) && + field->field_visibility != NOT_INVISIBLE) + continue; Item *item; if (!(item= field_iterator.create_item(thd))) @@ -8187,7 +8350,6 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, ? table->next_number_field->field_index : ~0U; DBUG_ENTER("fill_record"); - if (!*ptr) { /* No fields to update, quite strange!*/ @@ -8210,7 +8372,10 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, /* Ensure that all fields are from the same table */ DBUG_ASSERT(field->table == table); - value=v++; + if (field->field_visibility != NOT_INVISIBLE) + continue; + else + value=v++; if (field->field_index == autoinc_index) table->auto_increment_field_not_null= TRUE; if (field->vcol_info) @@ -8372,84 +8537,6 @@ my_bool mysql_rm_tmp_tables(void) unireg support functions *****************************************************************************/ -/** - A callback to the server internals that is used to address - special cases of the locking protocol. - Invoked when acquiring an exclusive lock, for each thread that - has a conflicting shared metadata lock. - - This function: - - aborts waiting of the thread on a data lock, to make it notice - the pending exclusive lock and back off. - - if the thread is an INSERT DELAYED thread, sends it a KILL - signal to terminate it. - - @note This function does not wait for the thread to give away its - locks. Waiting is done outside for all threads at once. - - @param thd Current thread context - @param in_use The thread to wake up - @param needs_thr_lock_abort Indicates that to wake up thread - this call needs to abort its waiting - on table-level lock. - - @retval TRUE if the thread was woken up - @retval FALSE otherwise. - - @note It is one of two places where border between MDL and the - rest of the server is broken. -*/ - -bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, - bool needs_thr_lock_abort) -{ - bool signalled= FALSE; - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - !in_use->killed) - { - in_use->set_killed(KILL_SYSTEM_THREAD); - mysql_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - mysql_mutex_lock(in_use->mysys_var->current_mutex); - mysql_cond_broadcast(in_use->mysys_var->current_cond); - mysql_mutex_unlock(in_use->mysys_var->current_mutex); - } - mysql_mutex_unlock(&in_use->mysys_var->mutex); - signalled= TRUE; - } - - if (needs_thr_lock_abort) - { - mysql_mutex_lock(&in_use->LOCK_thd_data); - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* - Check for TABLE::needs_reopen() is needed since in some places we call - handler::close() for table instance (and set TABLE::db_stat to 0) - and do not remove such instances from the THD::open_tables - for some time, during which other thread can see those instances - (e.g. see partitioning code). - */ - if (!thd_table->needs_reopen()) - { - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - if (thd && WSREP(thd) && wsrep_thd_is_BF(thd, true)) - { - WSREP_DEBUG("remove_table_from_cache: %llu", - (unsigned long long) thd->real_id); - wsrep_abort_thd((void *)thd, (void *)in_use, FALSE); - } - } - } - mysql_mutex_unlock(&in_use->LOCK_thd_data); - } - return signalled; -} - - int setup_ftfuncs(SELECT_LEX *select_lex) { List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)), @@ -8481,7 +8568,8 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) DBUG_PRINT("info",("Performing FULLTEXT search")); while ((ifm=li++)) - ifm->init_search(thd, no_order); + if (ifm->init_search(thd, no_order)) + return 1; } return 0; } @@ -8670,7 +8758,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG); /* Make sure all columns get assigned to a default value */ table->use_all_columns(); - DBUG_ASSERT(table->no_replicate); + DBUG_ASSERT(table->s->no_replicate); } else thd->restore_backup_open_tables_state(backup); diff --git a/sql/sql_base.h b/sql/sql_base.h index bb33af66590..bf140cb5d17 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -100,6 +100,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, */ #define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000 #define MYSQL_LOCK_NOT_TEMPORARY 0x2000 +#define MYSQL_LOCK_USE_MALLOC 0x4000 /** Only check THD::killed if waits happen (e.g. wait on MDL, wait on table flush, wait on thr_lock.c locks) while opening and locking table. @@ -257,6 +258,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, uint dt_phases); bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables, bool can_deadlock); +bool open_and_lock_internal_tables(TABLE *table, bool lock); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index f58bcf2e8fe..aa1a1cd6941 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_binlog.h" #include "sql_parse.h" diff --git a/sql/sql_bootstrap.cc b/sql/sql_bootstrap.cc index 4ef9885b34d..ce7d7a9fc93 100644 --- a/sql/sql_bootstrap.cc +++ b/sql/sql_bootstrap.cc @@ -14,7 +14,7 @@ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> +#include "mariadb.h" #include <ctype.h> #include <string.h> #include "sql_bootstrap.h" diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index dc5b2295b20..766aa8099ba 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -328,7 +328,7 @@ TODO list: (This could be done with almost no speed penalty) */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_basic_types.h" #include "sql_cache.h" @@ -345,7 +345,6 @@ TODO list: #include "../storage/myisammrg/ha_myisammrg.h" #include "../storage/myisammrg/myrg_def.h" #include "probes_mysql.h" -#include "log_slow.h" #include "transaction.h" #include "strfunc.h" @@ -962,7 +961,7 @@ inline void Query_cache_query::unlock_reading() void Query_cache_query::init_n_lock() { DBUG_ENTER("Query_cache_query::init_n_lock"); - res=0; wri = 0; len = 0; ready= 0; + res=0; wri = 0; len = 0; ready= 0; hit_count = 0; mysql_rwlock_init(key_rwlock_query_cache_query_lock, &lock); lock_writing(); DBUG_PRINT("qcache", ("inited & locked query for block %p", @@ -2143,6 +2142,7 @@ lookup: } move_to_query_list_end(query_block); hits++; + query->increment_hits(); unlock(); /* @@ -2341,7 +2341,7 @@ void Query_cache::invalidate(THD *thd, const char *db) if (is_disabled()) DBUG_VOID_RETURN; - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); bool restart= FALSE; /* @@ -4049,41 +4049,35 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, tables_used->view_name.str, tables_used->view_db.str)); *tables_type|= HA_CACHE_TBL_NONTRANSACT; + continue; } - else + if (tables_used->derived) { - if (tables_used->derived) - { - DBUG_PRINT("qcache", ("table: %s", tables_used->alias)); - table_count--; - DBUG_PRINT("qcache", ("derived table skipped")); - continue; - } - DBUG_PRINT("qcache", ("table: %s db: %s type: %u", - tables_used->table->s->table_name.str, - tables_used->table->s->db.str, - tables_used->table->s->db_type()->db_type)); - *tables_type|= tables_used->table->file->table_cache_type(); + DBUG_PRINT("qcache", ("table: %s", tables_used->alias)); + table_count--; + DBUG_PRINT("qcache", ("derived table skipped")); + continue; + } - /* - table_alias_charset used here because it depends of - lower_case_table_names variable - */ - table_count+= tables_used->table->file-> - count_query_cache_dependant_tables(tables_type); - - if (tables_used->table->s->tmp_table != NO_TMP_TABLE || - (*tables_type & HA_CACHE_TBL_NOCACHE) || - (tables_used->db_length == 5 && - my_strnncoll(table_alias_charset, - (uchar*)tables_used->table->s->table_cache_key.str, 6, - (uchar*)"mysql",6) == 0)) - { - DBUG_PRINT("qcache", - ("select not cacheable: temporary, system or " - "other non-cacheable table(s)")); - DBUG_RETURN(0); - } + DBUG_PRINT("qcache", ("table: %s db: %s type: %u", + tables_used->table->s->table_name.str, + tables_used->table->s->db.str, + tables_used->table->s->db_type()->db_type)); + *tables_type|= tables_used->table->file->table_cache_type(); + + /* + table_alias_charset used here because it depends of + lower_case_table_names variable + */ + table_count+= tables_used->table->file-> + count_query_cache_dependant_tables(tables_type); + + if (tables_used->table->s->not_usable_by_query_cache) + { + DBUG_PRINT("qcache", + ("select not cacheable: temporary, system or " + "other non-cacheable table(s)")); + DBUG_RETURN(0); } } DBUG_RETURN(table_count); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 07a80f778b8..6fc083ee331 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -159,6 +159,7 @@ struct Query_cache_query unsigned int last_pkt_nr; uint8 tbls_type; uint8 ready; + ulonglong hit_count; Query_cache_query() {} /* Remove gcc warning */ inline void init_n_lock(); @@ -184,6 +185,8 @@ struct Query_cache_query */ inline void set_results_ready() { ready= 1; } inline bool is_results_ready() { return ready; } + inline void increment_hits() { hit_count++; } + inline ulonglong hits() { return hit_count; } void lock_writing(); void lock_reading(); bool try_lock_writing(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index dc68d01f978..9d83e467c82 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -28,7 +28,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" #include "sql_cache.h" // query_cache_abort @@ -121,8 +121,8 @@ extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry) bool Key_part_spec::operator==(const Key_part_spec& other) const { return length == other.length && - !my_strcasecmp(system_charset_info, field_name.str, - other.field_name.str); + !lex_string_cmp(system_charset_info, &field_name, + &other.field_name); } /** @@ -138,7 +138,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root) columns(rhs.columns, mem_root), name(rhs.name), option_list(rhs.option_list), - generated(rhs.generated) + generated(rhs.generated), invisible(false) { list_copy_and_replace_each_value(columns, mem_root); } @@ -249,9 +249,9 @@ bool Foreign_key::validate(List<Create_field> &table_fields) { it.rewind(); while ((sql_field= it++) && - my_strcasecmp(system_charset_info, - column->field_name.str, - sql_field->field_name.str)) {} + lex_string_cmp(system_charset_info, + &column->field_name, + &sql_field->field_name)) {} if (!sql_field) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); @@ -706,7 +706,7 @@ extern "C" void thd_kill_timeout(THD* thd) } -THD::THD(my_thread_id id, bool is_wsrep_applier) +THD::THD(my_thread_id id, bool is_wsrep_applier, bool skip_global_sys_var_lock) :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION, /* statement id */ 0), rli_fake(0), rgi_fake(0), rgi_slave(NULL), @@ -834,7 +834,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) enable_slow_log= 0; durability_property= HA_REGULAR_DURABILITY; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS dbug_sentry=THD_SENTRY_MAGIC; #endif mysql_audit_init_thd(this); @@ -893,7 +893,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) /* Call to init() below requires fully initialized Open_tables_state. */ reset_open_tables_state(this); - init(); + init(skip_global_sys_var_lock); #if defined(ENABLED_PROFILING) profiling.set_thd(this); #endif @@ -1264,10 +1264,11 @@ const Type_handler *THD::type_handler_for_date() const Init common variables that has to be reset on start and on change_user */ -void THD::init(void) +void THD::init(bool skip_lock) { DBUG_ENTER("thd::init"); - mysql_mutex_lock(&LOCK_global_system_variables); + if (!skip_lock) + mysql_mutex_lock(&LOCK_global_system_variables); plugin_thdvar_init(this); /* plugin_thd_var_init() sets variables= global_system_variables, which @@ -1280,8 +1281,8 @@ void THD::init(void) ::strmake(default_master_connection_buff, global_system_variables.default_master_connection.str, variables.default_master_connection.length); - - mysql_mutex_unlock(&LOCK_global_system_variables); + if (!skip_lock) + mysql_mutex_unlock(&LOCK_global_system_variables); user_time.val= start_time= start_time_sec_part= 0; @@ -1646,7 +1647,7 @@ THD::~THD() mysql_mutex_destroy(&LOCK_wakeup_ready); mysql_mutex_destroy(&LOCK_thd_data); mysql_mutex_destroy(&LOCK_thd_kill); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS dbug_sentry= THD_SENTRY_GONE; #endif #ifndef EMBEDDED_LIBRARY @@ -1727,6 +1728,9 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) to_var->binlog_bytes_written+= from_var->binlog_bytes_written; to_var->cpu_time+= from_var->cpu_time; to_var->busy_time+= from_var->busy_time; + to_var->table_open_cache_hits+= from_var->table_open_cache_hits; + to_var->table_open_cache_misses+= from_var->table_open_cache_misses; + to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows; /* Update global_memory_used. We have to do this with atomic_add as the @@ -1778,6 +1782,12 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, dec_var->binlog_bytes_written; to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time; to_var->busy_time+= from_var->busy_time - dec_var->busy_time; + to_var->table_open_cache_hits+= from_var->table_open_cache_hits - + dec_var->table_open_cache_hits; + to_var->table_open_cache_misses+= from_var->table_open_cache_misses - + dec_var->table_open_cache_misses; + to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows - + dec_var->table_open_cache_overflows; /* We don't need to accumulate memory_used as these are not reset or used by @@ -2063,6 +2073,23 @@ int THD::killed_errno() } +void THD::reset_killed() +{ + /* + Resetting killed has to be done under a mutex to ensure + its not done during an awake() call. + */ + DBUG_ENTER("reset_killed"); + if (killed != NOT_KILLED) + { + mysql_mutex_lock(&LOCK_thd_kill); + killed= NOT_KILLED; + killed_err= 0; + mysql_mutex_unlock(&LOCK_thd_kill); + } + DBUG_VOID_RETURN; +} + /* Remember the location of thread info, the structure needed for the structure for the net buffer @@ -3773,7 +3800,7 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup) backup->set_query_arena(this); set_query_arena(set); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS backup->is_backup_arena= TRUE; #endif DBUG_VOID_RETURN; @@ -3792,7 +3819,7 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup) DBUG_ASSERT(backup->is_backup_arena); set->set_query_arena(this); set_query_arena(backup); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS backup->is_backup_arena= FALSE; #endif DBUG_VOID_RETURN; @@ -4174,7 +4201,8 @@ my_bool thd_net_is_killed() void thd_increment_bytes_received(void *thd, ulong length) { - ((THD*) thd)->status_var.bytes_received+= length; + if (thd != NULL) // MDEV-13073 Ack collector having NULL + ((THD*) thd)->status_var.bytes_received+= length; } @@ -4188,6 +4216,12 @@ void THD::set_status_var_init() { bzero((char*) &status_var, offsetof(STATUS_VAR, last_cleared_system_status_var)); + /* + Session status for Threads_running is always 1. It can only be queried + by thread itself via INFORMATION_SCHEMA.SESSION_STATUS or SHOW [SESSION] + STATUS. And at this point thread is guaranteed to be running. + */ + status_var.threads_running= 1; } @@ -4661,7 +4695,7 @@ TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len, { if (t->s->db.length == db_len && t->s->table_name.length == table_len && !strcmp(t->s->db.str, db) && !strcmp(t->s->table_name.str, table) && - t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::FK) + t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_FK) return t; } return NULL; @@ -4686,7 +4720,6 @@ void destroy_thd(MYSQL_THD thd) thd->add_status_to_global(); unlink_not_visible_thd(thd); delete thd; - dec_thread_running(); } void reset_thd(MYSQL_THD thd) @@ -4753,13 +4786,6 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd) return(thd->slave_thread); } -/* Returns true for a worker thread in parallel replication. */ -extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd) -{ - return thd->rgi_slave && thd->rgi_slave->is_parallel_exec; -} - - /* Returns high resolution timestamp for the start of the current query. */ extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd) @@ -4797,7 +4823,7 @@ thd_need_wait_reports(const MYSQL_THD thd) } /* - Used by storage engines (currently TokuDB and InnoDB/XtraDB) to report that + Used by storage engines (currently TokuDB and InnoDB) to report that one transaction THD is about to go to wait for a transactional lock held by another transactions OTHER_THD. @@ -4819,7 +4845,7 @@ thd_need_wait_reports(const MYSQL_THD thd) transaction, and later re-try it, to resolve the deadlock. This call need only receive reports about waits for locks that will remain - until the holding transaction commits. InnoDB/XtraDB auto-increment locks, + until the holding transaction commits. InnoDB auto-increment locks, for example, are released earlier, and so need not be reported. (Such false positives are not harmful, but could lead to unnecessary kill and retry, so best avoided). @@ -4872,7 +4898,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd) } /* - This function is called from InnoDB/XtraDB to check if the commit order of + This function is called from InnoDB to check if the commit order of two transactions has already been decided by the upper layer. This happens in parallel replication, where the commit order is forced to be the same on the slave as it was originally on the master. @@ -4902,7 +4928,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd) If this function returns true, normal locking should be done as required by the binlogging and transaction isolation level in effect. But if it returns - false, the correct order will be enforced anyway, and InnoDB/XtraDB can + false, the correct order will be enforced anyway, and InnoDB can avoid taking the gap lock, preventing the lock conflict. Calling this function is just an optimisation to avoid unnecessary @@ -5181,10 +5207,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->count_cuted_fields= count_cuted_fields; backup->in_sub_stmt= in_sub_stmt; backup->enable_slow_log= enable_slow_log; - backup->query_plan_flags= query_plan_flags; backup->limit_found_rows= limit_found_rows; - backup->examined_row_count= m_examined_row_count; - backup->sent_row_count= m_sent_row_count; backup->cuted_fields= cuted_fields; backup->client_capabilities= client_capabilities; backup->savepoints= transaction.savepoints; @@ -5192,6 +5215,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, first_successful_insert_id_in_prev_stmt; backup->first_successful_insert_id_in_cur_stmt= first_successful_insert_id_in_cur_stmt; + store_slow_query_state(backup); if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) && !is_current_stmt_binlog_format_row()) @@ -5207,14 +5231,12 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; - m_examined_row_count= 0; - m_sent_row_count= 0; cuted_fields= 0; transaction.savepoints= 0; first_successful_insert_id_in_cur_stmt= 0; + reset_slow_query_state(); } - void THD::restore_sub_statement_state(Sub_statement_state *backup) { DBUG_ENTER("THD::restore_sub_statement_state"); @@ -5249,7 +5271,6 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) variables.option_bits= backup->option_bits; in_sub_stmt= backup->in_sub_stmt; enable_slow_log= backup->enable_slow_log; - query_plan_flags= backup->query_plan_flags; first_successful_insert_id_in_prev_stmt= backup->first_successful_insert_id_in_prev_stmt; first_successful_insert_id_in_cur_stmt= @@ -5257,6 +5278,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) limit_found_rows= backup->limit_found_rows; set_sent_row_count(backup->sent_row_count); client_capabilities= backup->client_capabilities; + + /* Restore statistic needed for slow log */ + add_slow_query_state(backup); + /* If we've left sub-statement mode, reset the fatal error flag. Otherwise keep the current value, to propagate it up the sub-statement @@ -5281,6 +5306,56 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) DBUG_VOID_RETURN; } +/* + Store slow query state at start of a stored procedure statment +*/ + +void THD::store_slow_query_state(Sub_statement_state *backup) +{ + backup->affected_rows= affected_rows; + backup->bytes_sent_old= bytes_sent_old; + backup->examined_row_count= m_examined_row_count; + backup->query_plan_flags= query_plan_flags; + backup->query_plan_fsort_passes= query_plan_fsort_passes; + backup->sent_row_count= m_sent_row_count; + backup->tmp_tables_disk_used= tmp_tables_disk_used; + backup->tmp_tables_size= tmp_tables_size; + backup->tmp_tables_used= tmp_tables_used; +} + +/* Reset variables related to slow query log */ + +void THD::reset_slow_query_state() +{ + affected_rows= 0; + bytes_sent_old= status_var.bytes_sent; + m_examined_row_count= 0; + m_sent_row_count= 0; + query_plan_flags= QPLAN_INIT; + query_plan_fsort_passes= 0; + tmp_tables_disk_used= 0; + tmp_tables_size= 0; + tmp_tables_used= 0; +} + +/* + Add back the stored values to the current counters to be able to get + right status for 'call procedure_name' +*/ + +void THD::add_slow_query_state(Sub_statement_state *backup) +{ + affected_rows+= backup->affected_rows; + bytes_sent_old= backup->bytes_sent_old; + m_examined_row_count+= backup->examined_row_count; + m_sent_row_count+= backup->sent_row_count; + query_plan_flags|= backup->query_plan_flags; + query_plan_fsort_passes+= backup->query_plan_fsort_passes; + tmp_tables_disk_used+= backup->tmp_tables_disk_used; + tmp_tables_size+= backup->tmp_tables_size; + tmp_tables_used+= backup->tmp_tables_used; +} + void THD::set_statement(Statement *stmt) { @@ -5315,6 +5390,8 @@ void THD::inc_examined_row_count(ha_rows count) void THD::inc_status_created_tmp_disk_tables() { + tmp_tables_disk_used++; + query_plan_flags|= QPLAN_TMP_DISK; status_var_increment(status_var.created_tmp_disk_tables_); #ifdef HAVE_PSI_STATEMENT_INTERFACE PSI_STATEMENT_CALL(inc_statement_created_tmp_disk_tables)(m_statement_psi, 1); @@ -5323,6 +5400,8 @@ void THD::inc_status_created_tmp_disk_tables() void THD::inc_status_created_tmp_tables() { + tmp_tables_used++; + query_plan_flags|= QPLAN_TMP_TABLE; status_var_increment(status_var.created_tmp_tables_); #ifdef HAVE_PSI_STATEMENT_INTERFACE PSI_STATEMENT_CALL(inc_statement_created_tmp_tables)(m_statement_psi, 1); @@ -5979,7 +6058,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx", table->table_name, flags)); - if (table->table->no_replicate) + if (table->table->s->no_replicate) { /* The statement uses a table that is not replicated. @@ -6009,7 +6088,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) replicated_tables_count++; if (table->lock_type <= TL_READ_NO_INSERT && - table->prelocking_placeholder != TABLE_LIST::FK) + table->prelocking_placeholder != TABLE_LIST::PRELOCK_FK) has_read_tables= true; else if (table->table->found_next_number_field && (table->lock_type >= TL_WRITE_ALLOW_WRITE)) @@ -6493,7 +6572,7 @@ CPP_UNNAMED_NS_START { DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr)); DBUG_ASSERT(m_ptr[s] != 0); - DBUG_ASSERT(m_alloc_checked == TRUE); + DBUG_SLOW_ASSERT(m_alloc_checked == TRUE); return m_ptr[s]; } diff --git a/sql/sql_class.h b/sql/sql_class.h index d733aea1756..8747d658aea 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -20,7 +20,6 @@ /* Classes in mysql */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "dur_prop.h" #include <waiting_threads.h> #include "sql_const.h" @@ -38,6 +37,7 @@ #include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, THR_LOCK_INFO */ #include "thr_timer.h" #include "thr_malloc.h" +#include "log_slow.h" /* LOG_SLOW_DISABLE_... */ #include "sql_digest_stream.h" // sql_digest_state @@ -304,22 +304,25 @@ public: LEX_CSTRING name; engine_option_value *option_list; bool generated; + bool invisible; Key(enum Keytype type_par, const LEX_CSTRING *name_arg, ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options) :DDL_options(ddl_options), type(type_par), key_create_info(default_key_create_info), - name(*name_arg), option_list(NULL), generated(generated_arg) + name(*name_arg), option_list(NULL), generated(generated_arg), + invisible(false) { key_create_info.algorithm= algorithm_arg; - } + } Key(enum Keytype type_par, const LEX_CSTRING *name_arg, KEY_CREATE_INFO *key_info_arg, bool generated_arg, List<Key_part_spec> *cols, engine_option_value *create_opt, DDL_options_st ddl_options) :DDL_options(ddl_options), type(type_par), key_create_info(*key_info_arg), columns(*cols), - name(*name_arg), option_list(create_opt), generated(generated_arg) + name(*name_arg), option_list(create_opt), generated(generated_arg), + invisible(false) {} Key(const Key &rhs, MEM_ROOT *mem_root); virtual ~Key() {} @@ -370,8 +373,9 @@ public: typedef struct st_mysql_lock { TABLE **table; - uint table_count,lock_count; THR_LOCK_DATA **locks; + uint table_count,lock_count; + uint flags; } MYSQL_LOCK; @@ -537,6 +541,8 @@ typedef struct system_variables ulonglong join_buff_space_limit; ulonglong log_slow_filter; ulonglong log_slow_verbosity; + ulonglong log_slow_disabled_statements; + ulonglong log_disabled_statements; ulonglong bulk_insert_buff_size; ulonglong join_buff_size; ulonglong sortbuff_size; @@ -555,6 +561,7 @@ typedef struct system_variables ha_rows max_join_size; ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; + ulong column_compression_zlib_strategy; ulong lock_wait_timeout; ulong join_cache_level; ulong max_allowed_packet; @@ -642,6 +649,7 @@ typedef struct system_variables my_bool sql_log_bin_off; my_bool binlog_annotate_row_events; my_bool binlog_direct_non_trans_update; + my_bool column_compression_zlib_wrap; plugin_ref table_plugin; plugin_ref tmp_table_plugin; @@ -691,6 +699,13 @@ typedef struct system_variables my_bool session_track_state_change; ulong threadpool_priority; + + uint idle_transaction_timeout; + uint idle_readonly_transaction_timeout; + uint idle_write_transaction_timeout; + uint column_compression_threshold; + uint column_compression_zlib_level; + uint in_subquery_conversion_threshold; } SV; /** @@ -701,6 +716,8 @@ typedef struct system_variables typedef struct system_status_var { + ulong column_compressions; + ulong column_decompressions; ulong com_stat[(uint) SQLCOM_END]; ulong com_create_tmp_table; ulong com_drop_tmp_table; @@ -778,6 +795,7 @@ typedef struct system_status_var ulong feature_dynamic_columns; /* +1 when creating a dynamic column */ ulong feature_fulltext; /* +1 when MATCH is used */ ulong feature_gis; /* +1 opening a table with GIS features */ + ulong feature_invisible_columns; /* +1 opening a table with invisible column */ ulong feature_locale; /* +1 when LOCALE is set */ ulong feature_subquery; /* +1 when subqueries are used */ ulong feature_timezone; /* +1 when XPATH is used */ @@ -810,8 +828,12 @@ typedef struct system_status_var ulonglong rows_sent; ulonglong rows_tmp_read; ulonglong binlog_bytes_written; + ulonglong table_open_cache_hits; + ulonglong table_open_cache_misses; + ulonglong table_open_cache_overflows; double last_query_cost; double cpu_time, busy_time; + uint32_t threads_running; /* Don't initialize */ /* Memory used for thread local storage */ int64 max_local_memory_used; @@ -892,7 +914,7 @@ void free_tmp_table(THD *thd, TABLE *entry); /* The following macro is to make init of Query_arena simpler */ -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS #define INIT_ARENA_DBUG_INFO is_backup_arena= 0; is_reprepared= FALSE; #else #define INIT_ARENA_DBUG_INFO @@ -907,7 +929,7 @@ public: */ Item *free_list; MEM_ROOT *mem_root; // Pointer to current memroot -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool is_backup_arena; /* True if this arena is used for backup. */ bool is_reprepared; #endif @@ -1514,19 +1536,25 @@ public: class Sub_statement_state { public: + Discrete_interval auto_inc_interval_for_cur_row; + Discrete_intervals_list auto_inc_intervals_forced; + SAVEPOINT *savepoints; ulonglong option_bits; ulonglong first_successful_insert_id_in_prev_stmt; ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row; - Discrete_interval auto_inc_interval_for_cur_row; - Discrete_intervals_list auto_inc_intervals_forced; ulonglong limit_found_rows; - ha_rows cuted_fields, sent_row_count, examined_row_count; + ulonglong tmp_tables_size; ulonglong client_capabilities; + ulonglong cuted_fields, sent_row_count, examined_row_count; + ulonglong affected_rows; + ulonglong bytes_sent_old; + ulong tmp_tables_used; + ulong tmp_tables_disk_used; + ulong query_plan_fsort_passes; ulong query_plan_flags; uint in_sub_stmt; /* 0, SUB_STMT_TRIGGER or SUB_STMT_FUNCTION */ bool enable_slow_log; bool last_insert_id_used; - SAVEPOINT *savepoints; enum enum_check_fields count_cuted_fields; }; @@ -1542,7 +1570,8 @@ enum enum_thread_type SYSTEM_THREAD_EVENT_WORKER= 16, SYSTEM_THREAD_BINLOG_BACKGROUND= 32, SYSTEM_THREAD_SLAVE_BACKGROUND= 64, - SYSTEM_THREAD_GENERIC= 128 + SYSTEM_THREAD_GENERIC= 128, + SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND= 256 }; inline char const * @@ -1558,6 +1587,7 @@ show_system_thread(enum_thread_type thread) RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER); RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER); RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_BACKGROUND); + RETURN_NAME_AS_STRING(SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND); default: sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread); return buf; @@ -2017,13 +2047,18 @@ struct wait_for_commit void reinit(); }; +/* + Structure to store the start time for a query +*/ -extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); +typedef struct +{ + my_time_t start_time; + ulong start_time_sec_part; + ulonglong start_utime, utime_after_lock; +} QUERY_START_TIME_INFO; -class THD; -#ifndef DBUG_OFF -void dbug_serve_apcs(THD *thd, int n_calls); -#endif +extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); /** @class THD @@ -2229,7 +2264,8 @@ public: /* Needed by MariaDB semi sync replication */ Trans_binlog_info *semisync_info; - + /* If this is a semisync slave connection. */ + bool semi_sync_slave; ulonglong client_capabilities; /* What the client supports */ ulong max_client_packet_length; @@ -2242,7 +2278,7 @@ public: HASH ull_hash; /* Hash of used seqeunces (for PREVIOUS value) */ HASH sequences; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS uint dbug_sentry; // watch out for memory corruption #endif struct st_my_thread_var *mysys_var; @@ -2723,6 +2759,14 @@ public: { m_row_count_func= row_count_func; } + inline void set_affected_rows(longlong row_count_func) + { + /* + We have to add to affected_rows (used by slow log), as otherwise + information for 'call' will be wrong + */ + affected_rows+= (row_count_func >= 0 ? row_count_func : 0); + } ha_rows cuted_fields; @@ -2752,6 +2796,9 @@ public: ha_rows get_examined_row_count() const { return m_examined_row_count; } + ulonglong get_affected_rows() const + { return affected_rows; } + void set_sent_row_count(ha_rows count); void set_examined_row_count(ha_rows count); @@ -2829,8 +2876,16 @@ public: /* Statement id is thread-wide. This counter is used to generate ids */ ulong statement_id_counter; ulong rand_saved_seed1, rand_saved_seed2; + + /* The following variables are used when printing to slow log */ ulong query_plan_flags; ulong query_plan_fsort_passes; + ulong tmp_tables_used; + ulong tmp_tables_disk_used; + ulonglong tmp_tables_size; + ulonglong bytes_sent_old; + ulonglong affected_rows; /* Number of changed rows */ + pthread_t real_id; /* For debugging */ my_thread_id thread_id, thread_dbug_id; uint32 os_thread_id; @@ -2920,7 +2975,6 @@ public: uint8 failed_com_change_user; bool slave_thread; bool extra_port; /* If extra connection */ - bool no_errors; /** @@ -2964,7 +3018,7 @@ public: */ bool charset_is_system_charset, charset_is_collation_connection; bool charset_is_character_set_filesystem; - bool enable_slow_log; /* enable slow log for current statement */ + bool enable_slow_log; /* Enable slow log for current statement */ bool abort_on_warning; bool got_warning; /* Set on call to push_warning() */ /* set during loop of derived table processing */ @@ -2996,6 +3050,7 @@ public: the query. 0 if no error on the master. */ int slave_expected_error; + enum_sql_command last_sql_command; // Last sql_command exceuted in mysql_execute_command() sp_rcontext *spcont; // SP runtime context sp_cache *sp_proc_cache; @@ -3096,11 +3151,20 @@ public: /* Debug Sync facility. See debug_sync.cc. */ struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ - THD(my_thread_id id, bool is_wsrep_applier= false); + /** + @param id thread identifier + @param is_wsrep_applier thread type + @param skip_lock instruct whether @c LOCK_global_system_variables + is already locked, to not acquire it then. + */ + THD(my_thread_id id, bool is_wsrep_applier= false, bool skip_lock= false); ~THD(); - - void init(void); + /** + @param skip_lock instruct whether @c LOCK_global_system_variables + is already locked, to not acquire it then. + */ + void init(bool skip_lock= false); /* Initialize memory roots necessary for query processing and (!) pre-allocate memory for it. We can't do that in THD constructor because @@ -3294,6 +3358,20 @@ public: MYSQL_SET_STATEMENT_LOCK_TIME(m_statement_psi, (utime_after_lock - start_utime)); } + void get_time(QUERY_START_TIME_INFO *time_info) + { + time_info->start_time= start_time; + time_info->start_time_sec_part= start_time_sec_part; + time_info->start_utime= start_utime; + time_info->utime_after_lock= utime_after_lock; + } + void set_time(QUERY_START_TIME_INFO *time_info) + { + start_time= time_info->start_time; + start_time_sec_part= time_info->start_time_sec_part; + start_utime= time_info->start_utime; + utime_after_lock= time_info->utime_after_lock; + } ulonglong current_utime() { return microsecond_interval_timer(); } /* Tell SHOW PROCESSLIST to show time from this point */ @@ -3312,7 +3390,7 @@ public: void update_server_status() { set_time_for_next_stage(); - if (utime_after_query > utime_after_lock + variables.long_query_time) + if (utime_after_query >= utime_after_lock + variables.long_query_time) server_status|= SERVER_QUERY_WAS_SLOW; } inline ulonglong found_rows(void) @@ -3420,17 +3498,31 @@ public: LEX_STRING *make_lex_string(const char* str, uint length) { LEX_STRING *lex_str; - if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING)))) + char *tmp; + if (!(lex_str= (LEX_STRING *) alloc_root(mem_root, sizeof(LEX_STRING) + + length+1))) return 0; - return make_lex_string(lex_str, str, length); + tmp= (char*) (lex_str+1); + lex_str->str= tmp; + memcpy(tmp, str, length); + tmp[length]= 0; + lex_str->length= length; + return lex_str; } LEX_CSTRING *make_clex_string(const char* str, uint length) { LEX_CSTRING *lex_str; - if (!(lex_str= (LEX_CSTRING *)alloc_root(mem_root, sizeof(LEX_CSTRING)))) + char *tmp; + if (!(lex_str= (LEX_CSTRING *)alloc_root(mem_root, sizeof(LEX_CSTRING) + + length+1))) return 0; - return make_lex_string(lex_str, str, length); + tmp= (char*) (lex_str+1); + lex_str->str= tmp; + memcpy(tmp, str, length); + tmp[length]= 0; + lex_str->length= length; + return lex_str; } // Allocate LEX_STRING for character set conversion @@ -3704,20 +3796,7 @@ public: } } int killed_errno(); - inline void reset_killed() - { - /* - Resetting killed has to be done under a mutex to ensure - its not done during an awake() call. - */ - if (killed != NOT_KILLED) - { - mysql_mutex_lock(&LOCK_thd_kill); - killed= NOT_KILLED; - killed_err= 0; - mysql_mutex_unlock(&LOCK_thd_kill); - } - } + void reset_killed(); inline void reset_kill_query() { if (killed < KILL_CONNECTION) @@ -3747,6 +3826,9 @@ public: void restore_backup_open_tables_state(Open_tables_backup *backup); void reset_sub_statement_state(Sub_statement_state *backup, uint new_state); void restore_sub_statement_state(Sub_statement_state *backup); + void store_slow_query_state(Sub_statement_state *backup); + void reset_slow_query_state(); + void add_slow_query_state(Sub_statement_state *backup); void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); void restore_active_arena(Query_arena *set, Query_arena *backup); @@ -4325,7 +4407,8 @@ public: const char *path, const char *db, const char *table_name, - bool open_in_engine); + bool open_in_engine, + bool open_internal_tables); TABLE *find_temporary_table(const char *db, const char *table_name); TABLE *find_temporary_table(const TABLE_LIST *tl); @@ -4501,6 +4584,29 @@ public: mysql_mutex_unlock(&LOCK_thread_count); } + + uint get_net_wait_timeout() + { + if (in_active_multi_stmt_transaction()) + { + if (transaction.all.is_trx_read_write()) + { + if (variables.idle_write_transaction_timeout > 0) + return variables.idle_write_transaction_timeout; + } + else + { + if (variables.idle_readonly_transaction_timeout > 0) + return variables.idle_readonly_transaction_timeout; + } + + if (variables.idle_transaction_timeout > 0) + return variables.idle_transaction_timeout; + } + + return variables.net_wait_timeout; + } + /** Switch to a sublex, to parse a substatement or an expression. */ @@ -4524,6 +4630,13 @@ public: See also sp_head::merge_lex(). */ bool restore_from_local_lex_to_old_lex(LEX *oldlex); + + inline void prepare_logs_for_admin_command() + { + enable_slow_log&= !MY_TEST(variables.log_slow_disabled_statements & + LOG_SLOW_DISABLE_ADMIN); + query_plan_flags|= QPLAN_ADMIN; + } }; inline void add_to_active_threads(THD *thd) @@ -4550,11 +4663,12 @@ inline void unlink_not_visible_thd(THD *thd) /** A short cut for thd->get_stmt_da()->set_ok_status(). */ inline void -my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0, +my_ok(THD *thd, ulonglong affected_rows_arg= 0, ulonglong id= 0, const char *message= NULL) { - thd->set_row_count_func(affected_rows); - thd->get_stmt_da()->set_ok_status(affected_rows, id, message); + thd->set_row_count_func(affected_rows_arg); + thd->set_affected_rows(affected_rows_arg); + thd->get_stmt_da()->set_ok_status(affected_rows_arg, id, message); } @@ -5393,8 +5507,6 @@ public: }; - - /* Optimizer and executor structure for the materialized semi-join info. This structure contains @@ -6113,7 +6225,7 @@ public: (int) m_db.length, (m_db.length ? m_db.str : ""), dot, ".", (int) m_name.length, m_name.str); - DBUG_ASSERT(ok_for_lower_case_names(m_db.str)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str)); return false; } }; @@ -6133,6 +6245,87 @@ public: } }; +/* Functions to compare if two lex strings are equal */ +inline bool lex_string_cmp(CHARSET_INFO *charset, + const LEX_CSTRING *a, + const LEX_CSTRING *b) +{ + return my_strcasecmp(charset, a->str, b->str); +} + +/* + Compare if two LEX_CSTRING are equal. Assumption is that + character set is ASCII (like for plugin names) +*/ +inline bool lex_string_eq(const LEX_CSTRING *a, + const LEX_CSTRING *b) +{ + if (a->length != b->length) + return 1; /* Different */ + return strcasecmp(a->str, b->str) != 0; +} + +class Type_holder: public Sql_alloc, + public Item_args, + public Type_handler_hybrid_field_type, + public Type_all_attributes, + public Type_geometry_attributes +{ + TYPELIB *m_typelib; + bool m_maybe_null; +public: + Type_holder() + :m_typelib(NULL), + m_maybe_null(false) + { } + + void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; } + bool get_maybe_null() const { return m_maybe_null; } + + uint decimal_precision() const + { + /* + Type_holder is not used directly to create fields, so + its virtual decimal_precision() is never called. + We should eventually extend create_result_table() to accept + an array of Type_holders directly, without having to allocate + Item_type_holder's and put them into List<Item>. + */ + DBUG_ASSERT(0); + return 0; + } + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } + uint uint_geometry_type() const + { + return Type_geometry_attributes::get_geometry_type(); + } + void set_typelib(TYPELIB *typelib) + { + m_typelib= typelib; + } + TYPELIB *get_typelib() const + { + return m_typelib; + } + + bool aggregate_attributes(THD *thd) + { + for (uint i= 0; i < arg_count; i++) + m_maybe_null|= args[i]->maybe_null; + return + type_handler()->Item_hybrid_func_fix_attributes(thd, + "UNION", this, this, + args, arg_count); + } +}; + + +#ifndef DBUG_OFF +void dbug_serve_apcs(THD *thd, int n_calls); +#endif #endif /* MYSQL_SERVER */ diff --git a/sql/sql_client.cc b/sql/sql_client.cc index efac01f9894..0e17360915c 100644 --- a/sql/sql_client.cc +++ b/sql/sql_client.cc @@ -18,7 +18,7 @@ This files defines some MySQL C API functions that are server specific */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // system_variables diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 0b6a79bae89..1e3994dea66 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -20,7 +20,7 @@ Functions to autenticate and handle reqests for a connection */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifndef __WIN__ #include <netdb.h> // getservbyname, servent @@ -37,6 +37,7 @@ #include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL #include "sql_callback.h" #include "wsrep_mysqld.h" +#include "proxy_protocol.h" HASH global_user_stats, global_client_stats, global_table_stats; HASH global_index_stats; @@ -44,6 +45,7 @@ HASH global_index_stats; extern mysql_mutex_t LOCK_global_user_client_stats; extern mysql_mutex_t LOCK_global_table_stats; extern mysql_mutex_t LOCK_global_index_stats; +extern vio_keepalive_opts opt_vio_keepalive; /* Get structure for logging connection data for the current user @@ -817,6 +819,113 @@ bool init_new_connection_handler_thread() return 0; } +/** + Set client address during authentication. + + Initializes THD::main_security_ctx and THD::peer_port. + Optionally does ip to hostname translation. + + @param thd current THD handle + @param addr peer address (can be NULL, if 'ip' is set) + @param ip peer address as string (can be NULL if 'addr' is set) + @param port peer port + @param check_proxy_networks if true, and host is in + 'proxy_protocol_networks' list, skip + "host not privileged" check + @param[out] host_errors - number of connect + errors for this host + + @retval 0 ok, 1 error +*/ +int thd_set_peer_addr(THD *thd, + sockaddr_storage *addr, + const char *ip, + uint port, + bool check_proxy_networks, + uint *host_errors) +{ + *host_errors= 0; + + thd->peer_port= port; + + char ip_string[128]; + if (!ip) + { + void *addr_data; + if (addr->ss_family == AF_UNIX) + { + /* local connection */ + my_free((void *)thd->main_security_ctx.ip); + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host = my_localhost; + thd->main_security_ctx.ip= 0; + return 0; + } + else if (addr->ss_family == AF_INET) + addr_data= &((struct sockaddr_in *)addr)->sin_addr; + else + addr_data= &((struct sockaddr_in6 *)addr)->sin6_addr; + if (!inet_ntop(addr->ss_family,addr_data, ip_string, sizeof(ip_string))) + { + DBUG_ASSERT(0); + return 1; + } + ip= ip_string; + } + + my_free((void *)thd->main_security_ctx.ip); + if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME)))) + { + /* + No error accounting per IP in host_cache, + this is treated as a global server OOM error. + TODO: remove the need for my_strdup. + */ + statistic_increment(aborted_connects, &LOCK_status); + statistic_increment(connection_errors_internal, &LOCK_status); + return 1; /* The error is set by my_strdup(). */ + } + thd->main_security_ctx.host_or_ip = thd->main_security_ctx.ip; + if (!(specialflag & SPECIAL_NO_RESOLVE)) + { + int rc; + + rc = ip_to_hostname(addr, + thd->main_security_ctx.ip, + &thd->main_security_ctx.host, + host_errors); + + /* Cut very long hostnames to avoid possible overflows */ + if (thd->main_security_ctx.host) + { + if (thd->main_security_ctx.host != my_localhost) + ((char*)thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host), + HOSTNAME_LENGTH)] = 0; + thd->main_security_ctx.host_or_ip = thd->main_security_ctx.host; + } + + if (rc == RC_BLOCKED_HOST) + { + /* HOST_CACHE stats updated by ip_to_hostname(). */ + my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip); + return 1; + } + } + DBUG_PRINT("info", ("Host: %s ip: %s", + (thd->main_security_ctx.host ? + thd->main_security_ctx.host : "unknown host"), + (thd->main_security_ctx.ip ? + thd->main_security_ctx.ip : "unknown ip"))); + if ((!check_proxy_networks || !is_proxy_protocol_allowed((struct sockaddr *) addr)) + && acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) + { + /* HOST_CACHE stats updated by acl_check_host(). */ + my_error(ER_HOST_NOT_PRIVILEGED, MYF(0), + thd->main_security_ctx.host_or_ip); + return 1; + } + return 0; +} + /* Perform handshake, authorize client and update thd ACL variables. @@ -846,8 +955,9 @@ static int check_connection(THD *thd) { my_bool peer_rc; char ip[NI_MAXHOST]; + uint16 peer_port; - peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST); + peer_rc= vio_peer_addr(net->vio, ip, &peer_port, NI_MAXHOST); /* =========================================================================== @@ -922,55 +1032,10 @@ static int check_connection(THD *thd) my_error(ER_BAD_HOST_ERROR, MYF(0)); return 1; } - if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME)))) - { - /* - No error accounting per IP in host_cache, - this is treated as a global server OOM error. - TODO: remove the need for my_strdup. - */ - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - return 1; /* The error is set by my_strdup(). */ - } - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; - if (!(specialflag & SPECIAL_NO_RESOLVE)) - { - int rc; - - rc= ip_to_hostname(&net->vio->remote, - thd->main_security_ctx.ip, - &thd->main_security_ctx.host, - &connect_errors); - - /* Cut very long hostnames to avoid possible overflows */ - if (thd->main_security_ctx.host) - { - if (thd->main_security_ctx.host != my_localhost) - ((char*) thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host), - HOSTNAME_LENGTH)]= 0; - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; - } - - if (rc == RC_BLOCKED_HOST) - { - /* HOST_CACHE stats updated by ip_to_hostname(). */ - my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - } - DBUG_PRINT("info",("Host: %s ip: %s", - (thd->main_security_ctx.host ? - thd->main_security_ctx.host : "unknown host"), - (thd->main_security_ctx.ip ? - thd->main_security_ctx.ip : "unknown ip"))); - if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) - { - /* HOST_CACHE stats updated by acl_check_host(). */ - my_error(ER_HOST_NOT_PRIVILEGED, MYF(0), - thd->main_security_ctx.host_or_ip); + + if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port, + true, &connect_errors)) return 1; - } } else /* Hostname given means that the connection was on a socket */ { @@ -981,7 +1046,8 @@ static int check_connection(THD *thd) bzero((char*) &net->vio->remote, sizeof(net->vio->remote)); } vio_keepalive(net->vio, TRUE); - + vio_set_keepalive_options(net->vio, &opt_vio_keepalive); + if (thd->packet.alloc(thd->variables.net_buffer_length)) { /* diff --git a/sql/sql_connect.h b/sql/sql_connect.h index 364be401944..67950061da8 100644 --- a/sql/sql_connect.h +++ b/sql/sql_connect.h @@ -16,7 +16,7 @@ #ifndef SQL_CONNECT_INCLUDED #define SQL_CONNECT_INCLUDED -#include "my_sys.h" /* pthread_handler_t */ +#include <my_sys.h> /* pthread_handler_t */ #include "mysql_com.h" /* enum_server_command */ #include "structs.h" #include <mysql/psi/mysql_socket.h> @@ -85,6 +85,10 @@ bool thd_init_client_charset(THD *thd, uint cs_number); bool setup_connection_thread_globals(THD *thd); bool thd_prepare_connection(THD *thd); bool thd_is_connection_alive(THD *thd); +int thd_set_peer_addr(THD *thd, sockaddr_storage *addr, + const char *ip, uint port, + bool check_proxy_networks, + uint *host_errors); bool login_connection(THD *thd); void prepare_new_connection_state(THD* thd); diff --git a/sql/sql_const.h b/sql/sql_const.h index 007b7faeebb..65742235bee 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -231,6 +231,7 @@ */ #define HEAP_TEMPTABLE_LOOKUP_COST 0.05 #define DISK_TEMPTABLE_LOOKUP_COST 1.0 +#define SORT_INDEX_CMP_COST 0.02 #define MY_CHARSET_BIN_MB_MAXLEN 1 diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 2460a16551d..19cd780e9c3 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_crypt.h" #include "password.h" diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h index 3df554e9d31..e61776713b6 100644 --- a/sql/sql_crypt.h +++ b/sql/sql_crypt.h @@ -21,7 +21,7 @@ #pragma interface /* gcc class implementation */ #endif -#include "sql_list.h" /* Sql_alloc */ +#include "sql_alloc.h" /* Sql_alloc */ #include "my_rnd.h" /* rand_struct */ class SQL_CRYPT :public Sql_alloc diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index d2298619f63..7da088cdfd5 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -1,3 +1,20 @@ +/* + Copyright (c) 2016, 2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mariadb.h" #include "sql_class.h" #include "sql_lex.h" #include "sql_cte.h" @@ -112,8 +129,8 @@ bool With_clause::check_dependencies() elem != with_elem; elem= elem->next) { - if (my_strcasecmp(system_charset_info, with_elem->query_name->str, - elem->query_name->str) == 0) + if (lex_string_cmp(system_charset_info, with_elem->query_name, + elem->query_name) == 0) { my_error(ER_DUP_QUERY_NAME, MYF(0), with_elem->query_name->str); return true; diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 85676df9d39..036f0335c10 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -1,3 +1,19 @@ +/* + Copyright (c) 2016, 2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + #ifndef SQL_CTE_INCLUDED #define SQL_CTE_INCLUDED #include "sql_list.h" diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 40eb7046108..864b64a9f28 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -17,7 +17,7 @@ #pragma implementation /* gcc class implementation */ #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_cursor.h" diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 7860fa6d550..f568db51b9c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -18,7 +18,7 @@ /* create and drop of databases */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_db.h" @@ -1725,8 +1725,7 @@ bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) DBUG_PRINT("info",("Examining: %s", file->name)); /* skiping non-FRM files */ - if (my_strcasecmp(files_charset_info, - (extension= fn_rext(file->name)), reg_ext)) + if (!(extension= (char*) fn_frm_ext(file->name))) continue; /* A frm file found, add the table info rename list */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e7a0168fb48..984a0799b54 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -21,7 +21,7 @@ Multi-table deletes were introduced by Monty and Sinisa */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_delete.h" @@ -61,6 +61,8 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t Explain_query *query= thd->lex->explain; Explain_delete *explain= new (mem_root) Explain_delete(mem_root, thd->lex->analyze_stmt); + if (!explain) + return 0; if (deleting_all_rows) { @@ -71,8 +73,9 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t else { explain->deleting_all_rows= false; - Update_plan::save_explain_data_intern(mem_root, explain, - thd->lex->analyze_stmt); + if (Update_plan::save_explain_data_intern(mem_root, explain, + thd->lex->analyze_stmt)) + return 0; } query->add_upd_del_plan(explain); @@ -86,13 +89,16 @@ Update_plan::save_explain_update_data(MEM_ROOT *mem_root, THD *thd) Explain_query *query= thd->lex->explain; Explain_update* explain= new (mem_root) Explain_update(mem_root, thd->lex->analyze_stmt); - save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt); + if (!explain) + return 0; + if (save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt)) + return 0; query->add_upd_del_plan(explain); return explain; } -void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, +bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *explain, bool is_analyze) { @@ -105,13 +111,13 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, if (impossible_where) { explain->impossible_where= true; - return; + return 0; } if (no_partitions) { explain->no_partitions= true; - return; + return 0; } if (is_analyze) @@ -162,7 +168,8 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, explain->where_cond= select? select->cond: NULL; if (using_filesort) - explain->filesort_tracker= new (mem_root) Filesort_tracker(is_analyze); + if (!(explain->filesort_tracker= new (mem_root) Filesort_tracker(is_analyze))) + return 1; explain->using_io_buffer= using_io_buffer; append_possible_keys(mem_root, explain->possible_keys, table, @@ -211,6 +218,7 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, if (!(unit->item && unit->item->eliminated)) explain->add_child(unit->first_select()->select_number); } + return 0; } @@ -242,7 +250,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_I_List<ORDER> *order_list, ha_rows limit, ulonglong options, select_result *result) { - bool will_batch; + bool will_batch= FALSE; int error, loc_error; TABLE *table; SQL_SELECT *select=0; @@ -254,11 +262,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool return_error= 0; ha_rows deleted= 0; bool reverse= FALSE; + bool has_triggers; ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); SELECT_LEX *select_lex= &thd->lex->select_lex; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; + bool binlog_is_row; bool with_select= !select_lex->item_list.is_empty(); Explain_delete *explain; Delete_plan query_plan(thd->mem_root); @@ -363,9 +373,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - We should not be binlogging this statement in row-based, and - there should be no delete triggers associated with the table. */ + + has_triggers= (table->triggers && + table->triggers->has_delete_triggers()); if (!with_select && !using_limit && const_cond_result && (!thd->is_current_stmt_binlog_format_row() && - !(table->triggers && table->triggers->has_delete_triggers()))) + !has_triggers)) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -385,7 +398,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_type= THD::STMT_QUERY_TYPE; error= -1; deleted= maybe_deleted; - query_plan.save_explain_delete_data(thd->mem_root, thd); + if (!query_plan.save_explain_delete_data(thd->mem_root, thd)) + error= 1; goto cleanup; } if (error != HA_ERR_WRONG_COMMAND) @@ -503,7 +517,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto produce_explain_and_leave; - explain= query_plan.save_explain_delete_data(thd->mem_root, thd); + if (!(explain= query_plan.save_explain_delete_data(thd->mem_root, thd))) + goto got_error; ANALYZE_START_TRACKING(&explain->command_tracker); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", @@ -512,14 +527,59 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!(select && select->quick)) status_var_increment(thd->status_var.delete_scan_count); - if (query_plan.using_filesort) + binlog_is_row= thd->is_current_stmt_binlog_format_row(); + DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE")); + + /* + We can use direct delete (delete that is done silently in the handler) + if none of the following conditions are true: + - There are triggers + - There is binary logging + - There is a virtual not stored column in the WHERE clause + - ORDER BY or LIMIT + - As this requires the rows to be deleted in a specific order + - Note that Spider can handle ORDER BY and LIMIT in a cluster with + one data node. These conditions are therefore checked in + direct_delete_rows_init(). + + Direct delete does not require a WHERE clause + + Later we also ensure that we are only using one table (no sub queries) + */ + + if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) && + !has_triggers && !binlog_is_row && !with_select) { + table->mark_columns_needed_for_delete(); + if (!table->check_virtual_columns_marked_for_read()) + { + DBUG_PRINT("info", ("Trying direct delete")); + if (select && select->cond && + (select->cond->used_tables() == table->map)) + { + DBUG_ASSERT(!table->file->pushed_cond); + if (!table->file->cond_push(select->cond)) + table->file->pushed_cond= select->cond; + } + if (!table->file->direct_delete_rows_init()) + { + /* Direct deleting is supported */ + DBUG_PRINT("info", ("Using direct delete")); + THD_STAGE_INFO(thd, stage_updating); + if (!(error= table->file->ha_direct_delete_rows(&deleted))) + error= -1; + goto terminate_delete; + } + } + } + if (query_plan.using_filesort) + { { Filesort fsort(order, HA_POS_ERROR, true, select); DBUG_ASSERT(query_plan.index == MAX_KEY); - Filesort_tracker *fs_tracker= + Filesort_tracker *fs_tracker= thd->lex->explain->get_upd_del_plan()->filesort_tracker; if (!(file_sort= filesort(thd, table, &fsort, fs_tracker))) @@ -555,17 +615,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (error) goto got_error; - init_ftfuncs(thd, select_lex, 1); - - if (table->prepare_triggers_for_delete_stmt_or_event()) - { - will_batch= FALSE; - } - else - will_batch= !table->file->start_bulk_delete(); + if (init_ftfuncs(thd, select_lex, 1)) + goto got_error; table->mark_columns_needed_for_delete(); + if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_DELETE) && + !table->prepare_triggers_for_delete_stmt_or_event()) + will_batch= !table->file->start_bulk_delete(); + if (with_select) { if (result->send_result_set_metadata(select_lex->item_list, @@ -589,7 +647,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, MEM_STRIP_BUF_SIZE); THD_STAGE_INFO(thd, stage_searching_rows_for_update); - while (!(error=info.read_record(&info)) && !thd->killed && + while (!(error=info.read_record()) && !thd->killed && ! thd->is_error()) { if (record_should_be_deleted(thd, table, select, explain)) @@ -615,7 +673,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } THD_STAGE_INFO(thd, stage_updating); - while (!(error=info.read_record(&info)) && !thd->killed && + while (!(error=info.read_record()) && !thd->killed && ! thd->is_error()) { if (delete_while_scanning) @@ -672,6 +730,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else break; } + terminate_delete: killed_status= thd->killed; if (killed_status != NOT_KILLED || thd->is_error()) @@ -758,6 +817,8 @@ cleanup: } delete file_sort; free_underlaid_joins(thd, select_lex); + if (table->file->pushed_cond) + table->file->cond_pop(); DBUG_RETURN(error >= 0 || thd->is_error()); /* Special exits */ @@ -766,7 +827,8 @@ produce_explain_and_leave: We come here for various "degenerate" query plans: impossible WHERE, no-partitions-used, impossible-range, etc. */ - query_plan.save_explain_delete_data(thd->mem_root, thd); + if (!(query_plan.save_explain_delete_data(thd->mem_root, thd))) + goto got_error; send_nothing_and_leave: /* @@ -778,7 +840,8 @@ send_nothing_and_leave: delete select; delete file_sort; free_underlaid_joins(thd, select_lex); - //table->set_keyread(false); + if (table->file->pushed_cond) + table->file->cond_pop(); DBUG_ASSERT(!return_error || thd->is_error() || thd->killed); DBUG_RETURN((return_error || thd->is_error() || thd->killed) ? 1 : 0); @@ -1066,7 +1129,7 @@ multi_delete::initialize_tables(JOIN *join) MEM_STRIP_BUF_SIZE); } init_ftfuncs(thd, thd->lex->current_select, 1); - DBUG_RETURN(thd->is_fatal_error != 0); + DBUG_RETURN(thd->is_fatal_error); } @@ -1288,7 +1351,7 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, */ info.ignore_not_found_rows= 1; bool will_batch= !table->file->start_bulk_delete(); - while (!(local_error= info.read_record(&info)) && !thd->killed) + while (!(local_error= info.read_record()) && !thd->killed) { if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 3ed1417e7d0..f9801d2bf36 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -22,7 +22,7 @@ */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_derived.h" @@ -1301,7 +1301,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) continue; extracted_cond_copy= !sl->next_select() ? extracted_cond : - extracted_cond->build_clone(thd, thd->mem_root); + extracted_cond->build_clone(thd); if (!extracted_cond_copy) continue; @@ -1331,7 +1331,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) */ extracted_cond_copy= !sl->next_select() ? extracted_cond : - extracted_cond->build_clone(thd, thd->mem_root); + extracted_cond->build_clone(thd); if (!extracted_cond_copy) continue; diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc index 1b41f7dadfc..2f88d62fbbb 100644 --- a/sql/sql_digest.cc +++ b/sql/sql_digest.cc @@ -18,7 +18,7 @@ This code needs extra visibility in the lexer structures */ -#include "my_global.h" +#include "mariadb.h" #include "my_md5.h" #include "unireg.h" @@ -31,11 +31,6 @@ #include "sql_get_diagnostics.h" -#ifdef NEVER -#include "my_sys.h" -#include "sql_signal.h" -#endif - /* Generated code */ #include "sql_yacc.h" #define LEX_TOKEN_WITH_DEFINITION diff --git a/sql/sql_do.cc b/sql/sql_do.cc index da7dfe0c137..f556dc282ed 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -16,7 +16,7 @@ /* Execute DO statement */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "transaction.h" #include "unireg.h" diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 144cf9d4a30..b1c7481bb8c 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -41,7 +41,7 @@ This file contains the implementation of error and warnings related ***********************************************************************/ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_error.h" diff --git a/sql/sql_error.h b/sql/sql_error.h index 263c5843a4a..f8b8adc805a 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -17,12 +17,12 @@ #ifndef SQL_ERROR_H #define SQL_ERROR_H -#include "sql_list.h" /* Sql_alloc, MEM_ROOT */ -#include "m_string.h" /* LEX_STRING */ -#include "sql_string.h" /* String */ -#include "sql_plist.h" /* I_P_List */ -#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */ -#include "my_time.h" /* MYSQL_TIME */ +#include "sql_list.h" /* Sql_alloc, MEM_ROOT, list */ +#include "m_string.h" /* LEX_STRING */ +#include "sql_string.h" /* String */ +#include "sql_plist.h" /* I_P_List */ +#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */ +#include "my_time.h" /* MYSQL_TIME */ #include "decimal.h" class THD; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index c82948e52ae..ad80303e1b3 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -18,7 +18,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "my_json_writer.h" @@ -1149,33 +1149,37 @@ void Explain_table_access::fill_key_len_str(String *key_len_str) const } -void Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg) +bool Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg) { - set_pseudo_key(mem_root, key->name); + if (set_pseudo_key(mem_root, key->name.str)) + return 1; + key_len= key_len_arg; uint len= 0; for (uint i= 0; i < key->usable_key_parts; i++) { - key_parts_list.append_str(mem_root, - key->key_part[i].field->field_name.str); + if (!key_parts_list.append_str(mem_root, + key->key_part[i].field->field_name.str)) + return 1; len += key->key_part[i].store_length; if (len >= key_len_arg) break; } + return 0; } -void Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg) +bool Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg) { if (key_name_arg) { - size_t name_len= strlen(key_name_arg); - if ((key_name= (char*)alloc_root(root, name_len+1))) - memcpy(key_name, key_name_arg, name_len+1); + if (!(key_name= strdup_root(root, key_name_arg))) + return 1; } else key_name= NULL; key_len= ~(uint) 0; + return 0; } @@ -1815,9 +1819,9 @@ const char * extra_tag_text[]= "Using join buffer", // special handling - "const row not found", - "unique row not found", - "Impossible ON condition" + "Const row not found", + "Unique row not found", + "Impossible ON condition", }; @@ -2456,7 +2460,11 @@ int Explain_range_checked_fer::append_possible_keys_stat(MEM_ROOT *alloc, for (j= 0; j < table->s->keys; j++) { if (possible_keys.is_set(j)) - keys_stat_names[j]= key_set.append_str(alloc, table->key_info[j].name); + { + if (!(keys_stat_names[j]= key_set.append_str(alloc, + table->key_info[j].name.str))) + return 1; + } else keys_stat_names[j]= NULL; } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index fb524263a8e..7fb50a6cb04 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -590,8 +590,8 @@ public: key_name= NULL; key_len= (uint)-1; } - void set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); - void set_pseudo_key(MEM_ROOT *root, const char *key_name); + bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); + bool set_pseudo_key(MEM_ROOT *root, const char *key_name); inline const char *get_key_name() const { return key_name; } inline uint get_key_len() const { return key_len; } diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc index c79783bf561..00f5bfd792f 100644 --- a/sql/sql_expression_cache.cc +++ b/sql/sql_expression_cache.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" #include "sql_select.h" #include "sql_expression_cache.h" diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc index d21fa7f4752..e7ab6cc3c75 100644 --- a/sql/sql_get_diagnostics.cc +++ b/sql/sql_get_diagnostics.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ +#include "mariadb.h" #include "sql_list.h" // Sql_alloc, List, List_iterator #include "sql_cmd.h" // Sql_cmd #include "sql_class.h" // Diagnostics_area diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index babb09a1267..eecfd5ad0b6 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -52,7 +52,7 @@ cursor points at the first record). */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_handler.h" #include "sql_base.h" // close_thread_tables @@ -139,6 +139,48 @@ static void mysql_ha_hash_free(SQL_HANDLER *table) delete table; } +static void mysql_ha_close_childs(THD *thd, TABLE_LIST *current_table_list, + TABLE_LIST **next_global) +{ + TABLE_LIST *table_list; + DBUG_ENTER("mysql_ha_close_childs"); + DBUG_PRINT("info",("current_table_list: %p", current_table_list)); + DBUG_PRINT("info",("next_global: %p", *next_global)); + for (table_list = *next_global; table_list; table_list = *next_global) + { + *next_global = table_list->next_global; + DBUG_PRINT("info",("table_name: %s.%s", table_list->table->s->db.str, + table_list->table->s->table_name.str)); + DBUG_PRINT("info",("parent_l: %p", table_list->parent_l)); + if (table_list->parent_l == current_table_list) + { + DBUG_PRINT("info",("found child")); + TABLE *table = table_list->table; + if (table) + { + table->open_by_handler= 0; + if (!table->s->tmp_table) + { + (void) close_thread_table(thd, &table); + thd->mdl_context.release_lock(table_list->mdl_request.ticket); + } + else + { + thd->mark_tmp_table_as_free_for_reuse(table); + } + } + mysql_ha_close_childs(thd, table_list, next_global); + } + else + { + /* the end of child tables */ + *next_global = table_list; + break; + } + } + DBUG_VOID_RETURN; +} + /** Close a HANDLER table. @@ -155,11 +197,16 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) { THD *thd= handler->thd; TABLE *table= handler->table; + TABLE_LIST *current_table_list= NULL, *next_global; /* check if table was already closed */ if (!table) return; + if ((next_global= table->file->get_next_global_for_child())) + current_table_list= next_global->parent_l; + + table->open_by_handler= 0; if (!table->s->tmp_table) { /* Non temporary table. */ @@ -170,15 +217,17 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) } table->file->ha_index_or_rnd_end(); - table->open_by_handler= 0; close_thread_table(thd, &table); + if (current_table_list) + mysql_ha_close_childs(thd, current_table_list, &next_global); thd->mdl_context.release_lock(handler->mdl_request.ticket); } else { /* Must be a temporary table */ table->file->ha_index_or_rnd_end(); - table->open_by_handler= 0; + if (current_table_list) + mysql_ha_close_childs(thd, current_table_list, &next_global); thd->mark_tmp_table_as_free_for_reuse(table); } my_free(handler->lock); @@ -309,15 +358,24 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) goto err; } - if (tables->mdl_request.ticket && - thd->mdl_context.has_lock(mdl_savepoint, tables->mdl_request.ticket)) + DBUG_PRINT("info",("clone_tickets start")); + for (TABLE_LIST *table_list= tables; table_list; + table_list= table_list->next_global) + { + DBUG_PRINT("info",("table_list %s.%s", table_list->table->s->db.str, + table_list->table->s->table_name.str)); + if (table_list->mdl_request.ticket && + thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket)) { + DBUG_PRINT("info",("clone_tickets")); /* The ticket returned is within a savepoint. Make a copy. */ - error= thd->mdl_context.clone_ticket(&tables->mdl_request); - tables->table->mdl_ticket= tables->mdl_request.ticket; + error= thd->mdl_context.clone_ticket(&table_list->mdl_request); + table_list->table->mdl_ticket= table_list->mdl_request.ticket; if (error) goto err; } + } + DBUG_PRINT("info",("clone_tickets end")); if (! reopen) { @@ -378,26 +436,37 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) /* Restore the state. */ thd->set_open_tables(backup_open_tables); + DBUG_PRINT("info",("set_lock_duration start")); if (sql_handler->mdl_request.ticket) { thd->mdl_context.set_lock_duration(sql_handler->mdl_request.ticket, MDL_EXPLICIT); thd->mdl_context.set_needs_thr_lock_abort(TRUE); } + for (TABLE_LIST *table_list= tables->next_global; table_list; + table_list= table_list->next_global) + { + DBUG_PRINT("info",("table_list %s.%s", table_list->table->s->db.str, + table_list->table->s->table_name.str)); + if (table_list->mdl_request.ticket) + { + thd->mdl_context.set_lock_duration(table_list->mdl_request.ticket, + MDL_EXPLICIT); + thd->mdl_context.set_needs_thr_lock_abort(TRUE); + } + } + DBUG_PRINT("info",("set_lock_duration end")); /* - Assert that the above check prevents opening of views and merge tables. - For temporary tables, TABLE::next can be set even if only one table - was opened for HANDLER as it is used to link them together. - */ - DBUG_ASSERT(sql_handler->table->next == NULL || - sql_handler->table->s->tmp_table); - /* If it's a temp table, don't reset table->query_id as the table is being used by this handler. For non-temp tables we use this flag in asserts. */ - table->open_by_handler= 1; + for (TABLE_LIST *table_list= tables; table_list; + table_list= table_list->next_global) + { + table_list->table->open_by_handler= 1; + } /* Safety, cleanup the pointer to satisfy MDL assertions. */ tables->mdl_request.ticket= NULL; @@ -571,7 +640,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, if (handler->keyno < 0 || my_strcasecmp(&my_charset_latin1, keyname, - table->s->key_info[handler->keyno].name)) + table->s->key_info[handler->keyno].name.str)) { if ((handler->keyno= find_type(keyname, &table->s->keynames, FIND_TYPE_NO_PREFIX) - 1) < 0) @@ -707,8 +776,14 @@ retry: { int lock_error; - if (handler->lock->lock_count > 0) - handler->lock->locks[0]->type= handler->lock->locks[0]->org_type; + THR_LOCK_DATA **pos,**end; + for (pos= handler->lock->locks, + end= handler->lock->locks + handler->lock->lock_count; + pos < end; + pos++) + { + pos[0]->type= pos[0]->org_type; + } /* save open_tables state */ TABLE* backup_open_tables= thd->open_tables; diff --git a/sql/sql_help.cc b/sql/sql_help.cc index bd11397e2aa..4ccaa6a055f 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_help.h" @@ -202,7 +202,7 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields, FALSE)) DBUG_RETURN(0); - while (!read_record_info.read_record(&read_record_info)) + while (!read_record_info.read_record()) { if (!select->cond->val_int()) // Doesn't match like continue; @@ -246,7 +246,7 @@ int search_keyword(THD *thd, TABLE *keywords, FALSE)) DBUG_RETURN(0); - while (!read_record_info.read_record(&read_record_info) && count<2) + while (!read_record_info.read_record() && count<2) { if (!select->cond->val_int()) // Dosn't match like continue; @@ -380,7 +380,7 @@ int search_categories(THD *thd, TABLE *categories, if (init_read_record(&read_record_info, thd, categories, select, NULL, 1, 0, FALSE)) DBUG_RETURN(0); - while (!read_record_info.read_record(&read_record_info)) + while (!read_record_info.read_record()) { if (select && !select->cond->val_int()) continue; @@ -418,7 +418,7 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname, FALSE)) DBUG_VOID_RETURN; - while (!read_record_info.read_record(&read_record_info)) + while (!read_record_info.read_record()) { if (!select->cond->val_int()) continue; diff --git a/sql/sql_hset.h b/sql/sql_hset.h index 4dfddf898f0..dfaf17cf55c 100644 --- a/sql/sql_hset.h +++ b/sql/sql_hset.h @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "my_global.h" #include "hash.h" diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 07b03baaf27..0cf121fdd59 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -56,7 +56,7 @@ */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_insert.h" #include "sql_update.h" // compare_record @@ -222,7 +222,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(-1); } - if (values.elements != table->s->fields) + if (values.elements != table->s->visible_fields) { my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); DBUG_RETURN(-1); @@ -980,7 +980,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, No field list, all fields are set explicitly: INSERT INTO t1 VALUES (values) */ - if (thd->lex->used_tables) // Column used in values() + if (thd->lex->used_tables || // Column used in values() + table->s->visible_fields != table->s->fields) restore_record(table,s->default_values); // Get empty record else { @@ -2423,8 +2424,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) The thread could be killed with an error message if di->handle_inserts() or di->open_and_lock_table() fails. The thread could be killed without an error message if - killed using mysql_notify_thread_having_shared_lock() or - kill_delayed_threads_for_table(). + killed using kill_delayed_threads_for_table(). */ if (!thd.is_error()) my_message(ER_QUERY_INTERRUPTED, ER_THD(&thd, ER_QUERY_INTERRUPTED), diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index e6ef8a4be9f..6df92f13585 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -27,6 +27,7 @@ #pragma implementation // gcc: Class implementation #endif +#include "mariadb.h" #include "key.h" #include "sql_base.h" #include "sql_select.h" @@ -2570,10 +2571,11 @@ finish: BNLH, BKA or BKAH) to the data structure RETURN VALUE - none + 0 ok + 1 error */ -void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain) +bool JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain) { explain->incremental= MY_TEST(prev_cache); @@ -2595,6 +2597,7 @@ void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain) default: DBUG_ASSERT(0); } + return 0; } /** @@ -2607,7 +2610,7 @@ THD *JOIN_CACHE::thd() } -static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file) +static bool add_mrr_explain_info(String *str, uint mrr_mode, handler *file) { char mrr_str_buf[128]={0}; int len; @@ -2616,22 +2619,30 @@ static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file) if (len > 0) { if (str->length()) - str->append(STRING_WITH_LEN("; ")); - str->append(mrr_str_buf, len); + { + if (str->append(STRING_WITH_LEN("; "))) + return 1; + } + if (str->append(mrr_str_buf, len)) + return 1; } + return 0; } -void JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain) + +bool JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain) { - JOIN_CACHE::save_explain_data(explain); - add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); + if (JOIN_CACHE::save_explain_data(explain)) + return 1; + return add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); } -void JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain) +bool JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain) { - JOIN_CACHE::save_explain_data(explain); - add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); + if (JOIN_CACHE::save_explain_data(explain)) + return 1; + return add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); } @@ -3372,7 +3383,7 @@ int JOIN_TAB_SCAN::next() if (is_first_record) is_first_record= FALSE; else - err= info->read_record(info); + err= info->read_record(); if (!err) { @@ -3387,7 +3398,7 @@ int JOIN_TAB_SCAN::next() Move to the next record if the last retrieved record does not meet the condition pushed to the table join_tab. */ - err= info->read_record(info); + err= info->read_record(); if (!err) { join_tab->tracker->r_rows++; diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h index 4ae843ebfc2..12c278dae85 100644 --- a/sql/sql_join_cache.h +++ b/sql/sql_join_cache.h @@ -662,7 +662,7 @@ public: enum_nested_loop_state join_records(bool skip_last); /* Add a comment on the join algorithm employed by the join cache */ - virtual void save_explain_data(EXPLAIN_BKA_TYPE *explain); + virtual bool save_explain_data(EXPLAIN_BKA_TYPE *explain); THD *thd(); @@ -1325,6 +1325,10 @@ public: JOIN_CACHE_BKA(JOIN *j, JOIN_TAB *tab, uint flags, JOIN_CACHE *prev) :JOIN_CACHE(j, tab, prev), mrr_mode(flags) {} + JOIN_CACHE_BKA(JOIN_CACHE_BKA *bka) + :JOIN_CACHE(bka->join, bka->join_tab, bka->prev_cache), + mrr_mode(bka->mrr_mode) {} + uchar **get_curr_association_ptr() { return &curr_association; } /* Initialize the BKA cache */ @@ -1340,7 +1344,7 @@ public: /* Check index condition of the joined table for a record from BKA cache */ bool skip_index_tuple(range_id_t range_info); - void save_explain_data(EXPLAIN_BKA_TYPE *explain); + bool save_explain_data(EXPLAIN_BKA_TYPE *explain); }; @@ -1421,6 +1425,10 @@ public: JOIN_CACHE_BKAH(JOIN *j, JOIN_TAB *tab, uint flags, JOIN_CACHE *prev) :JOIN_CACHE_BNLH(j, tab, prev), mrr_mode(flags) {} + JOIN_CACHE_BKAH(JOIN_CACHE_BKAH *bkah) + :JOIN_CACHE_BNLH(bkah->join, bkah->join_tab, bkah->prev_cache), + mrr_mode(bkah->mrr_mode) {} + uchar **get_curr_association_ptr() { return &curr_matching_chain; } /* Initialize the BKAH cache */ @@ -1431,5 +1439,5 @@ public: /* Check index condition of the joined table for a record from BKAH cache */ bool skip_index_tuple(range_id_t range_info); - void save_explain_data(EXPLAIN_BKA_TYPE *explain); + bool save_explain_data(EXPLAIN_BKA_TYPE *explain); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index aed04842c90..c86bdb94bd9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -18,7 +18,7 @@ /* A lexical scanner on a temporary buffer with a yacc interface */ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // sql_lex.h: SQLCOM_END #include "sql_lex.h" @@ -196,8 +196,9 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) lex_start(thd); context->init(); if ((!(table_ident= new Table_ident(thd, + &table->s->db, &table->s->table_name, - &table->s->db, TRUE))) || + TRUE))) || (!(table_list= select_lex->add_table_to_list(thd, table_ident, NULL, @@ -681,7 +682,6 @@ void LEX::start(THD *thd_arg) context_stack.empty(); unit.init_query(); - unit.init_select(); select_lex.linkage= UNSPECIFIED_TYPE; /* 'parent_lex' is used in init_query() so it must be before it. */ select_lex.parent_lex= this; @@ -738,6 +738,7 @@ void LEX::start(THD *thd_arg) spcont= NULL; proc_list.first= 0; escape_used= FALSE; + default_used= FALSE; query_tables= 0; reset_query_tables_list(FALSE); expr_allows_subselect= TRUE; @@ -1308,7 +1309,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) { Lex_input_stream *lip= & thd->m_parser_state->m_lip; int token; - + if (lip->lookahead_token >= 0) { /* @@ -1351,6 +1352,24 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return WITH; } break; + case VALUES: + if (thd->lex->current_select->parsing_place == IN_UPDATE_ON_DUP_KEY || + thd->lex->current_select->parsing_place == IN_PART_FUNC) + return VALUE_SYM; + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + switch(token) { + case LESS_SYM: + return VALUES_LESS_SYM; + case IN_SYM: + return VALUES_IN_SYM; + default: + lip->lookahead_yylval= lip->yylval; + lip->yylval= NULL; + lip->lookahead_token= token; + return VALUES; + } + break; default: break; } @@ -2138,7 +2157,7 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, uint *prefix_length) st_select_lex structures initialisations */ -void st_select_lex_node::init_query() +void st_select_lex_node::init_query_common() { options= 0; sql_cache= SQL_CACHE_UNSPECIFIED; @@ -2147,13 +2166,9 @@ void st_select_lex_node::init_query() uncacheable= 0; } -void st_select_lex_node::init_select() -{ -} - void st_select_lex_unit::init_query() { - st_select_lex_node::init_query(); + init_query_common(); linkage= GLOBAL_OPTIONS_TYPE; select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; @@ -2179,7 +2194,7 @@ void st_select_lex_unit::init_query() void st_select_lex::init_query() { - st_select_lex_node::init_query(); + init_query_common(); table_list.empty(); top_join_list.empty(); join_list= &top_join_list; @@ -2230,11 +2245,12 @@ void st_select_lex::init_query() m_agg_func_used= false; window_specs.empty(); window_funcs.empty(); + tvc= 0; + in_tvc= false; } void st_select_lex::init_select() { - st_select_lex_node::init_select(); sj_nests.empty(); sj_subselects.empty(); group_list.empty(); @@ -2268,6 +2284,10 @@ void st_select_lex::init_select() with_dep= 0; join= 0; lock_type= TL_READ_DEFAULT; + tvc= 0; + in_funcs.empty(); + curr_tvc_name= 0; + in_tvc= false; } /* @@ -2579,26 +2599,6 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, return FALSE; } -bool st_select_lex_node::inc_in_sum_expr() { return 1; } -uint st_select_lex_node::get_in_sum_expr() { return 0; } -TABLE_LIST* st_select_lex_node::get_table_list() { return 0; } -List<Item>* st_select_lex_node::get_item_list() { return 0; } -TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table, - LEX_CSTRING *alias, - ulong table_join_options, - thr_lock_type flags, - enum_mdl_type mdl_type, - List<Index_hint> *hints, - List<String> *partition_names, - LEX_STRING *option) -{ - return 0; -} -ulong st_select_lex_node::get_table_join_options() -{ - return 0; -} - /* prohibit using LIMIT clause */ @@ -3025,7 +3025,7 @@ LEX::LEX() : explain(NULL), result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0), option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0), - is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) + default_used(0), is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) { init_dynamic_array2(&plugins, sizeof(plugin_ref), plugins_static_buffer, @@ -4209,6 +4209,22 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived, if (in_subq->emb_on_expr_nest == NO_JOIN_NEST) in_subq->emb_on_expr_nest= derived; } + + uint cnt= sizeof(expr_cache_may_be_used)/sizeof(bool); + for (uint i= 0; i < cnt; i++) + { + if (subq_select->expr_cache_may_be_used[i]) + expr_cache_may_be_used[i]= true; + } + + List_iterator_fast<Item_func_in> it(subq_select->in_funcs); + Item_func_in *in_func; + while ((in_func= it++)) + { + in_funcs.push_back(in_func, thd->mem_root); + if (in_func->emb_on_expr_nest == NO_JOIN_NEST) + in_func->emb_on_expr_nest= derived; + } } /* Walk through child's tables and adjust table map, tablenr, @@ -5655,7 +5671,7 @@ bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop) Item_splocal(thd, &src->name, src->offset, src->type_handler()); if (args[i] == NULL) return true; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS args[i]->m_sp= sphead; #endif } @@ -5789,7 +5805,7 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop) loop.m_index->type_handler()); if (splocal == NULL) return true; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS splocal->m_sp= sphead; #endif Item_int *inc= new (thd->mem_root) Item_int(thd, loop.m_direction); @@ -5975,8 +5991,8 @@ bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock, if (sp_block_finalize(thd, spblock, &splabel)) return true; if (end_label->str && - my_strcasecmp(system_charset_info, - end_label->str, splabel->name.str) != 0) + lex_string_cmp(system_charset_info, + end_label, &splabel->name) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), end_label->str); return true; @@ -6383,8 +6399,8 @@ bool LEX::sp_pop_loop_label(THD *thd, const LEX_CSTRING *label_name) sp_label *lab= spcont->pop_label(); sphead->backpatch(lab); if (label_name->str && - my_strcasecmp(system_charset_info, label_name->str, - lab->name.str) != 0) + lex_string_cmp(system_charset_info, label_name, + &lab->name) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), label_name->str); return true; @@ -6580,7 +6596,7 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd, pos.pos(), pos.length()))) return NULL; } -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS item->m_sp= sphead; #endif safe_to_cache_query=0; @@ -6748,7 +6764,7 @@ Item *LEX::create_item_limit(THD *thd, spv->offset, spv->type_handler(), pos.pos(), pos.length()))) return NULL; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS item->m_sp= sphead; #endif safe_to_cache_query= 0; @@ -6869,7 +6885,7 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name, pos.pos(), pos.length()); if (splocal == NULL) return NULL; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS splocal->m_sp= sphead; #endif safe_to_cache_query= 0; @@ -7204,7 +7220,7 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, if (no_top_clones) return cond; cond->clear_extraction_flag(); - return cond->build_clone(thd, thd->mem_root); + return cond->build_clone(thd); } if (cond->type() == Item::COND_ITEM) { @@ -7260,6 +7276,36 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, } +int set_statement_var_if_exists(THD *thd, const char *var_name, + size_t var_name_length, ulonglong value) +{ + sys_var *sysvar; + if (thd->lex->sql_command == SQLCOM_CREATE_VIEW) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "[NO]WAIT"); + return 1; + } + if (thd->lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "[NO]WAIT"); + return 1; + } + if ((sysvar= find_sys_var_ex(thd, var_name, var_name_length, true, false))) + { + Item *item= new (thd->mem_root) Item_uint(thd, value); + set_var *var= new (thd->mem_root) set_var(thd, OPT_SESSION, sysvar, + &null_clex_str, item); + + if (!item || !var || thd->lex->stmt_var_list.push_back(var, thd->mem_root)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return 1; + } + } + return 0; +} + + bool LEX::sp_add_cfetch(THD *thd, const LEX_CSTRING *name) { uint offset; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 9b50d28e58d..fac06233592 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -23,7 +23,6 @@ #include "violite.h" /* SSL_type */ #include "sql_trigger.h" -#include "item.h" /* From item_subselect.h: subselect_union_engine */ #include "thr_lock.h" /* thr_lock_type, TL_UNLOCK */ #include "mem_root_array.h" #include "sql_cmd.h" @@ -31,7 +30,8 @@ #include "sql_window.h" #include "sql_trigger.h" #include "sp.h" // enum stored_procedure_type - +#include "sql_tvc.h" +#include "item.h" /* YACC and LEX Definitions */ @@ -226,6 +226,13 @@ enum enum_sp_data_access SP_MODIFIES_SQL_DATA }; +enum enum_sp_aggregate_type +{ + DEFAULT_AGGREGATE= 0, + NOT_AGGREGATE, + GROUP_AGGREGATE +}; + const LEX_STRING sp_data_access_name[]= { { C_STRING_WITH_LEN("") }, @@ -551,6 +558,8 @@ protected: st_select_lex_node *next, **prev, /* neighbor list */ *master, *slave, /* vertical links */ *link_next, **link_prev; /* list of whole SELECT_LEX */ + + void init_query_common(); public: ulonglong options; @@ -588,11 +597,8 @@ public: linkage(UNSPECIFIED_TYPE) { } - virtual ~st_select_lex_node() {} inline st_select_lex_node* get_master() { return master; } - virtual void init_query(); - virtual void init_select(); void include_down(st_select_lex_node *upper); void add_slave(st_select_lex_node *slave_arg); void include_neighbour(st_select_lex_node *before); @@ -601,23 +607,6 @@ public: void exclude(); void exclude_from_tree(); - virtual st_select_lex* outer_select()= 0; - virtual st_select_lex* return_after_parsing()= 0; - - virtual bool inc_in_sum_expr(); - virtual uint get_in_sum_expr(); - virtual TABLE_LIST* get_table_list(); - virtual List<Item>* get_item_list(); - virtual ulong get_table_join_options(); - virtual TABLE_LIST *add_table_to_list(THD *thd, Table_ident *table, - LEX_CSTRING *alias, - ulong table_options, - thr_lock_type flags= TL_UNLOCK, - enum_mdl_type mdl_type= MDL_SHARED_READ, - List<Index_hint> *hints= 0, - List<String> *partition_names= 0, - LEX_STRING *option= 0); - virtual void set_lock_for_tables(thr_lock_type lock_type) {} void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; } void move_node(st_select_lex_node *where_to_move) { @@ -753,7 +742,7 @@ public: /* thread handler */ THD *thd; /* - SELECT_LEX for hidden SELECT in onion which process global + SELECT_LEX for hidden SELECT in union which process global ORDER BY and LIMIT */ st_select_lex *fake_select_lex; @@ -775,7 +764,7 @@ public: { return reinterpret_cast<st_select_lex*>(slave); } - void set_with_clause(With_clause *with_cl); + inline void set_with_clause(With_clause *with_cl); st_select_lex_unit* next_unit() { return reinterpret_cast<st_select_lex_unit*>(next); @@ -900,6 +889,17 @@ public: those converted to jtbm nests. The list is emptied when conversion is done. */ List<Item_in_subselect> sj_subselects; + /* + List of IN-predicates in this st_select_lex that + can be transformed into IN-subselect defined with TVC. + */ + List<Item_func_in> in_funcs; + /* + Number of current derived table made with TVC during the + transformation of IN-predicate into IN-subquery for this + st_select_lex. + */ + uint curr_tvc_name; /* Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column @@ -1036,6 +1036,9 @@ public: /* it is for correct printing SELECT options */ thr_lock_type lock_type; + + table_value_constr *tvc; + bool in_tvc; void init_query(); void init_select(); @@ -1213,7 +1216,7 @@ public: void set_non_agg_field_used(bool val) { m_non_agg_field_used= val; } void set_agg_func_used(bool val) { m_agg_func_used= val; } - void set_with_clause(With_clause *with_clause); + inline void set_with_clause(With_clause *with_clause); With_clause *get_with_clause() { return master_unit()->with_clause; @@ -1251,7 +1254,7 @@ public: ORDER *find_common_window_func_partition_fields(THD *thd); bool cond_pushdown_is_allowed() const - { return !olap && !explicit_limit; } + { return !olap && !explicit_limit && !tvc; } private: bool m_non_agg_field_used; @@ -1275,7 +1278,12 @@ typedef class st_select_lex SELECT_LEX; inline bool st_select_lex_unit::is_unit_op () { if (!first_select()->next_select()) - return 0; + { + if (first_select()->tvc) + return 1; + else + return 0; + } enum sub_select_type linkage= first_select()->next_select()->linkage; return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || @@ -1289,6 +1297,7 @@ struct st_sp_chistics enum enum_sp_suid_behaviour suid; bool detistic; enum enum_sp_data_access daccess; + enum enum_sp_aggregate_type agg_type; void init() { bzero(this, sizeof(*this)); } void set(const st_sp_chistics &other) { *this= other; } bool read_from_mysql_proc_row(THD *thd, TABLE *table); @@ -2588,7 +2597,7 @@ public: Explain_update* save_explain_update_data(MEM_ROOT *mem_root, THD *thd); protected: - void save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze); + bool save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze); public: virtual ~Update_plan() {} @@ -2928,6 +2937,7 @@ public: st_alter_tablespace *alter_tablespace_info; bool escape_used; + bool default_used; /* using default() function */ bool is_lex_started; /* If lex_start() did run. For debugging. */ /* @@ -3967,6 +3977,8 @@ extern bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name); void my_missing_function_error(const LEX_CSTRING &token, const char *name); bool is_keyword(const char *name, uint len); +int set_statement_var_if_exists(THD *thd, const char *var_name, + size_t var_name_length, ulonglong value); Virtual_column_info *add_virtual_expression(THD *thd, Item *expr); Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, diff --git a/sql/sql_list.cc b/sql/sql_list.cc index 2c1b3c47d55..e938d5515c9 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -18,6 +18,7 @@ #pragma implementation // gcc: Class implementation #endif +#include "mariadb.h" #include "sql_list.h" list_node end_of_list; diff --git a/sql/sql_list.h b/sql/sql_list.h index 111826495f7..c708ed6d2f9 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -19,48 +19,7 @@ #pragma interface /* gcc class implementation */ #endif -#include "my_sys.h" /* alloc_root, TRASH, MY_WME, - MY_FAE, MY_ALLOW_ZERO_PTR */ -#include "m_string.h" /* bfill */ - -THD *thd_get_current_thd(); - -/* mysql standard class memory allocator */ - -class Sql_alloc -{ -public: - static void *operator new(size_t size) throw () - { - DBUG_ASSERT(size < UINT_MAX32); - return thd_alloc(thd_get_current_thd(), (uint) size); - } - static void *operator new[](size_t size) throw () - { - DBUG_ASSERT(size < UINT_MAX32); - return thd_alloc(thd_get_current_thd(), (uint) size); - } - static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () - { return alloc_root(mem_root, size); } - static void *operator new(size_t size, MEM_ROOT *mem_root) throw () - { return alloc_root(mem_root, size); } - static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); } - static void operator delete(void *ptr, MEM_ROOT *mem_root) - { /* never called */ } - static void operator delete[](void *ptr, MEM_ROOT *mem_root) - { /* never called */ } - static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); } -#ifdef HAVE_valgrind - bool dummy_for_valgrind; - inline Sql_alloc() :dummy_for_valgrind(0) {} - inline ~Sql_alloc() {} -#else - inline Sql_alloc() {} - inline ~Sql_alloc() {} -#endif - -}; - +#include "sql_alloc.h" /** Simple intrusive linked list. @@ -69,6 +28,7 @@ public: a pointer to the first element in the list and a indirect reference to the last element. */ + template <typename T> class SQL_I_List :public Sql_alloc { @@ -662,6 +622,10 @@ struct ilink { DBUG_ASSERT(prev != 0 && next != 0); } + inline void assert_not_linked() + { + DBUG_ASSERT(prev == 0 && next == 0); + } virtual ~ilink() { unlink(); } /*lint -e1740 */ }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 2a389d68ef9..9a15d4b9c0d 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -19,7 +19,7 @@ /* Copy data from a textfile to table */ /* 2006-12 Erik Wetterberg : LOAD XML added */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_load.h" @@ -839,7 +839,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, if (n++) query_str.append(STRING_WITH_LEN(", ")); append_identifier(thd, &query_str, item->name.str, item->name.length); - query_str.append(val->name.str, val->name.length); + query_str.append(&val->name); } } diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index a2efa5e072c..f8b96279378 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -20,12 +20,11 @@ !! This file is built from my_locale.pl !! */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_locale.h" #include "sql_class.h" // THD -#include "my_sys.h" // MY_*, NullS, NULL enum err_msgs_index diff --git a/sql/sql_locale.h b/sql/sql_locale.h index ec2f3d29e15..d1009832e0c 100644 --- a/sql/sql_locale.h +++ b/sql/sql_locale.h @@ -22,7 +22,6 @@ typedef struct my_locale_errmsgs const char ***errmsgs; } MY_LOCALE_ERRMSGS; -#include "my_global.h" /* uint */ typedef struct st_typelib TYPELIB; diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 8c8aee0cb03..f787d39b774 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -21,7 +21,7 @@ * o Berkeley DB: removing unneeded log files. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_manager.h" #include "sql_base.h" // flush_tables diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a24a6955dc3..51f39c4afb7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" // sql_kill, *_precheck, *_prepare #include "lock.h" // try_transactional_lock, @@ -82,7 +82,6 @@ #include <m_ctype.h> #include <myisam.h> #include <my_dir.h> -#include "rpl_handler.h" #include "rpl_mi.h" #include "sql_digest.h" @@ -99,7 +98,6 @@ #include "debug_sync.h" #include "probes_mysql.h" #include "set_var.h" -#include "log_slow.h" #include "sql_bootstrap.h" #include "sql_sequence.h" @@ -601,7 +599,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED;; + CF_CAN_BE_EXPLAINED; sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -1222,8 +1220,8 @@ bool do_command(THD *thd) the client, the connection is closed or "net_wait_timeout" number of seconds has passed. */ - if(!thd->skip_wait_timeout) - my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + if (!thd->skip_wait_timeout) + my_net_set_read_timeout(net, thd->get_net_wait_timeout()); /* Errors and diagnostics are cleared once here before query */ thd->clear_error(1); @@ -1286,7 +1284,8 @@ bool do_command(THD *thd) mysql_mutex_lock(&thd->LOCK_wsrep_thd); if (thd->wsrep_conflict_state == MUST_ABORT) { - DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu", thd->real_id)); + DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu", + (ulong) thd->real_id)); wsrep_client_rollback(thd); } mysql_mutex_unlock(&thd->LOCK_wsrep_thd); @@ -1550,9 +1549,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, "<?>"))); bool drop_more_results= 0; - if (!is_com_multi) - inc_thread_running(); - /* keep it withing 1 byte */ compile_time_assert(COM_END == 255); @@ -1600,17 +1596,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_EXECUTE_IF("crash_dispatch_command_before", { DBUG_PRINT("crash_dispatch_command_before", ("now")); - DBUG_ABORT(); }); + DBUG_SUICIDE(); }); /* Performance Schema Interface instrumentation, begin */ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi, com_statement_info[command]. m_key); + /* + We should always call reset_for_next_command() before a query. + mysql_parse() will do this for queries. Ensure it's also done + for other commands. + */ + if (command != COM_QUERY) + thd->reset_for_next_command(); thd->set_command(command); /* - Commands which always take a long time are logged into - the slow log only if opt_log_slow_admin_statements is set. + thd->variables.log_slow_disabled_statements defines which statements + are logged to slow log */ thd->enable_slow_log= thd->variables.sql_log_slow; thd->query_plan_flags= QPLAN_INIT; @@ -1893,6 +1896,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_query_and_id(beginning_of_next_stmt, length, thd->charset(), next_query_id()); + /* Count each statement from the client. */ @@ -1902,7 +1906,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); /* Reset the query start time. */ parser_state.reset(beginning_of_next_stmt, length); - /* TODO: set thd->lex->sql_command to SQLCOM_END here */ if (WSREP_ON) wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state, @@ -1960,7 +1963,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } packet= arg_end + 1; - thd->reset_for_next_command(0); // Don't clear errors // thd->reset_for_next_command reset state => restore it if (is_next_command) { @@ -2055,8 +2057,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, status_var_increment(thd->status_var.com_other); - thd->enable_slow_log&= opt_log_slow_admin_statements; - thd->query_plan_flags|= QPLAN_ADMIN; + thd->prepare_logs_for_admin_command(); if (check_global_access(thd, REPL_SLAVE_ACL)) break; @@ -2407,10 +2408,8 @@ com_multi_end: thd->m_digest= NULL; if (!is_com_multi) - { - dec_thread_running(); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory - } + thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); @@ -2437,9 +2436,12 @@ com_multi_end: /* + Log query to slow queries, if it passes filtering + @note This function must call delete_explain_query(). */ + void log_slow_statement(THD *thd) { DBUG_ENTER("log_slow_statement"); @@ -2451,19 +2453,26 @@ void log_slow_statement(THD *thd) */ if (unlikely(thd->in_sub_stmt)) goto end; // Don't set time for sub stmt + if (!thd->enable_slow_log || !global_system_variables.sql_log_slow) + goto end; + if ((thd->server_status & + (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && + !(sql_command_flags[thd->last_sql_command] & CF_STATUS_COMMAND) && + (!thd->variables.log_slow_filter || + (thd->variables.log_slow_filter & QPLAN_NOT_USING_INDEX))) + { + thd->query_plan_flags|= QPLAN_NOT_USING_INDEX; + /* We are always logging no index queries if enabled in filter */ + thd->server_status|= SERVER_QUERY_WAS_SLOW; + } /* Follow the slow log filter configuration. */ - if (!thd->enable_slow_log || !global_system_variables.sql_log_slow || - (thd->variables.log_slow_filter - && !(thd->variables.log_slow_filter & thd->query_plan_flags))) + if (thd->variables.log_slow_filter && + !(thd->variables.log_slow_filter & thd->query_plan_flags)) goto end; - - if (((thd->server_status & SERVER_QUERY_WAS_SLOW) || - ((thd->server_status & - (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && - opt_log_queries_not_using_indexes && - !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) && + + if ((thd->server_status & SERVER_QUERY_WAS_SLOW) && thd->get_examined_row_count() >= thd->variables.min_examined_row_limit) { thd->status_var.long_query_count++; @@ -2878,6 +2887,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp) { /* bits that should be cleared in thd->server_status */ uint bits_to_be_cleared= 0; + ulonglong affected_rows; if (sp->m_flags & sp_head::MULTI_RESULTS) { if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS)) @@ -2917,7 +2927,9 @@ static bool do_execute_sp(THD *thd, sp_head *sp) return 1; // Substatement should already have sent error } - my_ok(thd, (thd->get_row_count_func() < 0) ? 0 : thd->get_row_count_func()); + affected_rows= thd->affected_rows; // Affected rows for all sub statements + thd->affected_rows= 0; // Reset total, as my_ok() adds to it + my_ok(thd, affected_rows); return 0; } @@ -3148,6 +3160,13 @@ bool Sql_cmd_call::execute(THD *thd) if (do_execute_sp(thd, sp)) return true; + + /* + Disable slow log for the above call(), if calls are disabled. + Instead we will log the executed statements to the slow log. + */ + if (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_CALL) + thd->enable_slow_log= 0; } return false; } @@ -3229,6 +3248,12 @@ mysql_execute_command(THD *thd) table_list.first); /* + Remember last commmand executed, so that we can use it in functions called by + dispatch_command() + */ + thd->last_sql_command= lex->sql_command; + + /* Reset warning count for each query that uses tables A better approach would be to reset this for any commands that is not a SHOW command or a select that only access local @@ -4213,7 +4238,7 @@ mysql_execute_command(THD *thd) (!thd->is_current_stmt_binlog_format_row() || !create_info.tmp_table())) { - WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name, NULL) + WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name, NULL); } /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); @@ -4258,14 +4283,13 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL) + WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL); /* Currently CREATE INDEX or DROP INDEX cause a full table rebuild and thus classify as slow administrative statements just like ALTER TABLE. */ - thd->enable_slow_log&= opt_log_slow_admin_statements; - thd->query_plan_flags|= QPLAN_ADMIN; + thd->prepare_logs_for_admin_command(); bzero((char*) &create_info, sizeof(create_info)); create_info.db_type= 0; @@ -4384,7 +4408,7 @@ end_with_restore_list: if (check_rename_table(thd, first_table, all_tables)) goto error; - WSREP_TO_ISOLATION_BEGIN(0, 0, first_table) + WSREP_TO_ISOLATION_BEGIN(0, 0, first_table); if (mysql_rename_tables(thd, first_table, 0)) goto error; @@ -5164,7 +5188,7 @@ end_with_restore_list: (CREATE_ACL | DROP_ACL) : CREATE_ACL, &lex->name)) break; - WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL); res= mysql_create_db(thd, lex->name.str, lex->create_info, &lex->create_info); break; @@ -5173,7 +5197,7 @@ end_with_restore_list: { if (prepare_db_action(thd, DROP_ACL, &lex->name)) break; - WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL); res= mysql_rm_db(thd, lex->name.str, lex->if_exists()); break; } @@ -5205,7 +5229,7 @@ end_with_restore_list: res= 1; break; } - WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL); res= mysql_upgrade_db(thd, db); if (!res) my_ok(thd); @@ -5216,7 +5240,7 @@ end_with_restore_list: LEX_CSTRING *db= &lex->name; if (prepare_db_action(thd, ALTER_ACL, db)) break; - WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL); res= mysql_alter_db(thd, db->str, &lex->create_info); break; } @@ -5306,7 +5330,7 @@ end_with_restore_list: "mysql", NULL, NULL, 1, 0)) break; #ifdef HAVE_DLOPEN - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res = mysql_create_function(thd, &lex->udf))) my_ok(thd); #else @@ -5324,7 +5348,7 @@ end_with_restore_list: "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* Conditionally writes to binlog */ if (!(res= mysql_create_user(thd, lex->users_list, lex->sql_command == SQLCOM_CREATE_ROLE))) @@ -5338,7 +5362,7 @@ end_with_restore_list: check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res= mysql_drop_user(thd, lex->users_list, lex->sql_command == SQLCOM_DROP_ROLE))) my_ok(thd); @@ -5351,7 +5375,7 @@ end_with_restore_list: check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (lex->sql_command == SQLCOM_ALTER_USER) res= mysql_alter_user(thd, lex->users_list); else @@ -5367,7 +5391,7 @@ end_with_restore_list: break; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res = mysql_revoke_all(thd, lex->users_list))) my_ok(thd); break; @@ -5427,7 +5451,7 @@ end_with_restore_list: if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, sph, 0)) goto error; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= mysql_routine_grant(thd, all_tables, sph, lex->users_list, grants, lex->sql_command == SQLCOM_REVOKE, TRUE); @@ -5440,7 +5464,7 @@ end_with_restore_list: all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= mysql_table_grant(thd, all_tables, lex->users_list, lex->columns, lex->grant, lex->sql_command == SQLCOM_REVOKE); @@ -5456,7 +5480,7 @@ end_with_restore_list: } else { - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* Conditionally writes to binlog */ res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, lex->sql_command == SQLCOM_REVOKE, @@ -5482,7 +5506,7 @@ end_with_restore_list: case SQLCOM_REVOKE_ROLE: case SQLCOM_GRANT_ROLE: { - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res= mysql_grant_role(thd, lex->users_list, lex->sql_command != SQLCOM_GRANT_ROLE))) my_ok(thd); @@ -5540,7 +5564,7 @@ end_with_restore_list: REFRESH_STATUS | REFRESH_USER_RESOURCES)) { - WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL); } #endif /* WITH_WSREP*/ @@ -5574,11 +5598,11 @@ end_with_restore_list: */ if (first_table) { - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); + WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); } else { - WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL); + WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL); } } #endif /* WITH_WSREP */ @@ -5923,7 +5947,7 @@ end_with_restore_list: if (check_routine_access(thd, ALTER_PROC_ACL, db, name, Sp_handler::handler(lex->sql_command), 0)) goto error; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* Conditionally writes to binlog */ sp_result= sph->sp_drop_routine(thd, lex->spname); @@ -6044,7 +6068,7 @@ end_with_restore_list: if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog. */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); break; } @@ -6147,7 +6171,7 @@ end_with_restore_list: if (check_global_access(thd, SUPER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= create_server(thd, &lex->server_options); break; @@ -6160,7 +6184,7 @@ end_with_restore_list: if (check_global_access(thd, SUPER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if ((error= alter_server(thd, &lex->server_options))) { @@ -6180,7 +6204,7 @@ end_with_restore_list: if (check_global_access(thd, SUPER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if ((err_code= drop_server(thd, &lex->server_options))) { @@ -7523,13 +7547,13 @@ void THD::reset_for_next_command(bool do_clear_error) reset_dynamic(&thd->user_var_events); thd->user_var_events_alloc= thd->mem_root; } + thd->enable_slow_log= thd->variables.sql_log_slow; thd->get_stmt_da()->reset_for_next_command(); thd->rand_used= 0; thd->m_sent_row_count= thd->m_examined_row_count= 0; thd->accessed_rows_and_keys= 0; - thd->query_plan_flags= QPLAN_INIT; - thd->query_plan_fsort_passes= 0; + reset_slow_query_state(); thd->reset_current_stmt_binlog_format_row(); thd->binlog_unsafe_warning_flags= 0; @@ -7611,7 +7635,6 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) DBUG_RETURN(1); unit->init_query(); - unit->init_select(); unit->thd= thd; unit->include_down(lex->current_select); unit->link_next= 0; @@ -7685,28 +7708,23 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) @param var_name Variable name */ -void create_select_for_variable(const char *var_name) +void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) { - THD *thd; LEX *lex; - LEX_CSTRING tmp; Item *var; char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end; DBUG_ENTER("create_select_for_variable"); - thd= current_thd; lex= thd->lex; mysql_init_select(lex); lex->sql_command= SQLCOM_SELECT; - tmp.str= var_name; - tmp.length=strlen(var_name); /* We set the name of Item to @@session.var_name because that then is used as the column name in the output. */ - if ((var= get_system_var(thd, OPT_SESSION, tmp, null_clex_str))) + if ((var= get_system_var(thd, OPT_SESSION, var_name, &null_clex_str))) { - end= strxmov(buff, "@@session.", var_name, NullS); + end= strxmov(buff, "@@session.", var_name->str, NullS); var->set_name(thd, buff, (uint)(end-buff), system_charset_info); add_item_to_list(thd, var); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index b8d7bb46e08..b0371a2cb81 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -16,7 +16,6 @@ #ifndef SQL_PARSE_INCLUDED #define SQL_PARSE_INCLUDED -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_acl.h" /* GLOBAL_ACLS */ class Comp_creator; @@ -94,7 +93,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state, bool is_com_multi, bool is_next_command); bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); -void create_select_for_variable(const char *var_name); +void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index f951a81331b..1080f9d9de2 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -47,7 +47,7 @@ /* Some general useful functions */ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_partition.h" #include "key.h" // key_restore diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index d51556c94db..8168397da85 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -15,6 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_parse.h" // check_one_table_access // check_merge_table_access // check_one_table_access @@ -91,7 +92,7 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd) DBUG_ASSERT(!create_info.data_file_name && !create_info.index_file_name); WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->prepare_logs_for_admin_command(); DBUG_RETURN(exchange_partition(thd, first_table, &alter_info)); #ifdef WITH_WSREP error: diff --git a/sql/sql_plist.h b/sql/sql_plist.h index df50cccc874..14f6eb5e2aa 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -16,8 +16,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> - template <typename T, typename L> class I_P_List_iterator; class I_P_List_null_counter; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 978188199c5..462829a255b 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -15,8 +15,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 "sql_plugin.h" // Includes my_global.h -#include "sql_priv.h" // SHOW_MY_BOOL +#include "sql_plugin.h" // SHOW_MY_BOOL +#include "sql_priv.h" #include "unireg.h" #include "sql_class.h" // set_var.h: THD #include "sys_vars_shared.h" @@ -945,6 +945,10 @@ SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type) } +/* + If LEX is passed non-NULL, an automatic unlock of the plugin will happen + in the LEX destructor. +*/ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, uint state_mask= PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED | @@ -990,6 +994,16 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, } +/* + Notes on lifetime: + + If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made + in the thd->lex which will cause an automatic unlock of the plugin in the LEX + destructor. In this case, no manual unlock must be done. + + Otherwise, when passing a NULL THD, the caller must arrange that plugin + unlock happens later. +*/ plugin_ref plugin_lock(THD *thd, plugin_ref ptr) { LEX *lex= thd ? thd->lex : 0; @@ -1026,6 +1040,16 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr) } +/* + Notes on lifetime: + + If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made + in the thd->lex which will cause an automatic unlock of the plugin in the LEX + destructor. In this case, no manual unlock must be done. + + Otherwise, when passing a NULL THD, the caller must arrange that plugin + unlock happens later. +*/ plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type) { LEX *lex= thd ? thd->lex : 0; @@ -1131,6 +1155,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf); goto err; } + if (plugin_maturity_map[plugin->maturity] < plugin_maturity) { char buf[256]; @@ -1143,6 +1168,14 @@ static bool plugin_add(MEM_ROOT *tmp_root, report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf); goto err; } + else if (plugin_maturity_map[plugin->maturity] < SERVER_MATURITY_LEVEL) + { + sql_print_warning("Plugin '%s' is of maturity level %s while the server is %s", + tmp.name.str, + plugin_maturity_names[plugin->maturity], + plugin_maturity_names[SERVER_MATURITY_LEVEL]); + } + tmp.plugin= plugin; tmp.ref_count= 0; tmp.state= PLUGIN_IS_UNINITIALIZED; @@ -1643,7 +1676,7 @@ int plugin_init(int *argc, char **argv, int flags) */ global_system_variables.table_plugin = intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr)); - DBUG_ASSERT(plugin_ptr->ref_count == 1); + DBUG_SLOW_ASSERT(plugin_ptr->ref_count == 1); } mysql_mutex_unlock(&LOCK_plugin); @@ -1814,7 +1847,7 @@ static void plugin_load(MEM_ROOT *tmp_root) goto end; } table->use_all_columns(); - while (!(error= read_record_info.read_record(&read_record_info))) + while (!(error= read_record_info.read_record())) { DBUG_PRINT("info", ("init plugin record")); String str_name, str_dl; @@ -1938,6 +1971,12 @@ void plugin_shutdown(void) if (initialized) { + if (opt_gtid_pos_auto_plugins) + { + free_engine_list(opt_gtid_pos_auto_plugins); + opt_gtid_pos_auto_plugins= NULL; + } + mysql_mutex_lock(&LOCK_plugin); reap_needed= true; @@ -2825,7 +2864,8 @@ sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, mysql_mutex_unlock(&LOCK_plugin); if (!throw_error && !var) - my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str); + my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), + (int) (length ? length : strlen(str)), (char*) str); DBUG_RETURN(var); } diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 469ae5c88a3..5903029cd68 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -24,8 +24,9 @@ #define SHOW_always_last SHOW_KEY_CACHE_LONG, \ SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \ SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \ - SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_LEX_STRING -#include <my_global.h> + SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_UINT32_STATUS, \ + SHOW_LEX_STRING +#include "mariadb.h" #undef SHOW_always_last #include "m_string.h" /* LEX_STRING */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index fe5a82805b6..80ac2b22a86 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -84,7 +84,7 @@ When one supplies long data for a placeholder: at statement execute. */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_class.h" // set_var.h: THD @@ -2902,7 +2902,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) for (order= sl->order_list.first; order; order= order->next) order->item= &order->item_ptr; { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool res= #endif sl->handle_derived(lex, DT_REINIT); @@ -3732,7 +3732,7 @@ void Prepared_statement::cleanup_stmt() { DBUG_ENTER("Prepared_statement::cleanup_stmt"); DBUG_PRINT("enter",("stmt: %p", this)); - thd->restore_set_statement_var(); + lex->restore_set_statement_var(); cleanup_items(free_list); thd->cleanup_after_query(); thd->rollback_item_tree_changes(); @@ -4203,7 +4203,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query, packet_end= packet_end_arg; iterations= TRUE; start_param= true; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS Item *free_list_state= thd->free_list; #endif thd->select_number= select_number_after_prepare; @@ -4428,7 +4428,7 @@ Prepared_statement::reprepare() { swap_prepared_statement(©); swap_parameter_array(param_array, copy.param_array, param_count); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS is_reprepared= TRUE; #endif /* diff --git a/sql/sql_priv.h b/sql/sql_priv.h index db75dc2198b..ba37d933f12 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -227,7 +227,7 @@ #define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28) #define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29) #define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30) -#define OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED (1ULL << 31) +#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31) #define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ @@ -254,7 +254,7 @@ OPTIMIZER_SWITCH_EXISTS_TO_IN | \ OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \ - OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED) + OPTIMIZER_SWITCH_SPLIT_MATERIALIZED) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you @@ -327,6 +327,8 @@ /* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ #define UNDEF_POS (-1) +#define IN_SUBQUERY_CONVERSION_THRESHOLD 1000 + #endif /* !MYSQL_CLIENT */ /* BINLOG_DUMP options */ @@ -352,6 +354,8 @@ enum enum_parsing_place IN_ON, IN_GROUP_BY, IN_ORDER_BY, + IN_UPDATE_ON_DUP_KEY, + IN_PART_FUNC, PARSING_PLACE_SIZE /* always should be the last */ }; diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 7206162792a..c1a13ebd210 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -29,10 +29,9 @@ - "profiling_history_size", integer, session + global, "Num queries stored?" */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_profile.h" -#include <my_sys.h> #include "sql_show.h" // schema_table_store_record #include "sql_class.h" // THD diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 79968c029ea..93db089b24a 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_reload.h" #include "sql_priv.h" #include "mysqld.h" // select_errors diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 2cba634e17c..61e312646da 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -19,7 +19,7 @@ Atomic rename of table; RENAME TABLE t1 to t2, tmp to t1 [,...] */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_rename.h" diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1be3bc845f9..7db9f49f25e 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" @@ -28,9 +28,9 @@ #include "log_event.h" #include "rpl_filter.h" #include <my_dir.h> -#include "rpl_handler.h" #include "debug_sync.h" -#include "log.h" // get_gtid_list_event +#include "semisync_master.h" +#include "semisync_slave.h" enum enum_gtid_until_state { GTID_UNTIL_NOT_DONE, @@ -160,6 +160,7 @@ struct binlog_send_info { bool clear_initial_log_pos; bool should_stop; + size_t dirlen; binlog_send_info(THD *thd_arg, String *packet_arg, ushort flags_arg, char *lfn) @@ -313,16 +314,43 @@ static int reset_transmit_packet(binlog_send_info *info, ushort flags, packet->length(0); packet->set("\0", 1, &my_charset_bin); - if (RUN_HOOK(binlog_transmit, reserve_header, (info->thd, flags, packet))) + if (info->thd->semi_sync_slave) { - info->error= ER_UNKNOWN_ERROR; - *errmsg= "Failed to run hook 'reserve_header'"; - ret= 1; + if (repl_semisync_master.reserve_sync_header(packet)) + { + info->error= ER_UNKNOWN_ERROR; + *errmsg= "Failed to run hook 'reserve_header'"; + ret= 1; + } } + *ev_offset= packet->length(); return ret; } +int get_user_var_int(const char *name, + long long int *value, int *null_value) +{ + bool null_val; + user_var_entry *entry= + (user_var_entry*) my_hash_search(¤t_thd->user_vars, + (uchar*) name, strlen(name)); + if (!entry) + return 1; + *value= entry->val_int(&null_val); + if (null_value) + *null_value= null_val; + return 0; +} + +inline bool is_semi_sync_slave() +{ + int null_value; + long long val= 0; + get_user_var_int("rpl_semi_sync_slave", &val, &null_value); + return val; +} + static int send_file(THD *thd) { NET* net = &thd->net; @@ -1606,6 +1634,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, enum enum_binlog_checksum_alg current_checksum_alg= info->current_checksum_alg; slave_connection_state *gtid_state= &info->gtid_state; slave_connection_state *until_gtid_state= info->until_gtid_state; + bool need_sync= false; if (event_type == GTID_LIST_EVENT && info->using_gtid_state && until_gtid_state) @@ -1916,8 +1945,10 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, THD_STAGE_INFO(info->thd, stage_sending_binlog_event_to_slave); pos= my_b_tell(log); - if (RUN_HOOK(binlog_transmit, before_send_event, - (info->thd, info->flags, packet, info->log_file_name, pos))) + if (repl_semisync_master.update_sync_header(info->thd, + (uchar*) packet->c_ptr(), + info->log_file_name + info->dirlen, + pos, &need_sync)) { info->error= ER_UNKNOWN_ERROR; return "run 'before_send_event' hook failed"; @@ -1939,8 +1970,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, } } - if (RUN_HOOK(binlog_transmit, after_send_event, - (info->thd, info->flags, packet))) + if (need_sync && repl_semisync_master.flush_net(info->thd, packet->c_ptr())) { info->error= ER_UNKNOWN_ERROR; return "Failed to run hook 'after_send_event'"; @@ -2370,7 +2400,7 @@ static int wait_new_events(binlog_send_info *info, /* in */ PSI_stage_info old_stage; mysql_bin_log.lock_binlog_end_pos(); - info->thd->ENTER_COND(mysql_bin_log.get_log_cond(), + info->thd->ENTER_COND(mysql_bin_log.get_bin_log_cond(), mysql_bin_log.get_binlog_end_pos_lock(), &stage_master_has_sent_all_binlog_to_slave, &old_stage); @@ -2681,7 +2711,7 @@ static int send_one_binlog_file(binlog_send_info *info, /** end of file or error */ return (int)end_pos; } - + info->dirlen= dirname_length(info->log_file_name); /** * send events from current position up to end_pos */ @@ -2703,6 +2733,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, binlog_send_info infoobj(thd, packet, flags, linfo.log_file_name); binlog_send_info *info= &infoobj; + bool has_transmit_started= false; int old_max_allowed_packet= thd->variables.max_allowed_packet; thd->variables.max_allowed_packet= MAX_MAX_ALLOWED_PACKET; @@ -2715,11 +2746,11 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, if (init_binlog_sender(info, &linfo, log_ident, &pos)) goto err; - /* - run hook first when all check has been made that slave seems to - be requesting a reasonable position. i.e when transmit actually starts - */ - if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos))) + has_transmit_started= true; + + /* Check if the dump thread is created by a slave with semisync enabled. */ + thd->semi_sync_slave = is_semi_sync_slave(); + if (repl_semisync_master.dump_start(thd, log_ident, pos)) { info->errmsg= "Failed to run hook 'transmit_start'"; info->error= ER_UNKNOWN_ERROR; @@ -2841,7 +2872,10 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, err: THD_STAGE_INFO(thd, stage_waiting_to_finalize_termination); - RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags)); + if (has_transmit_started) + { + repl_semisync_master.dump_end(thd); + } if (info->thd->killed == KILL_SLAVE_SAME_ID) { @@ -3307,7 +3341,8 @@ int reset_slave(THD *thd, Master_info* mi) else if (global_system_variables.log_warnings > 1) sql_print_information("Deleted Master_info file '%s'.", fname); - RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi)); + if (rpl_semi_sync_slave_enabled) + repl_semisync_slave.reset_slave(mi); err: mi->unlock_slave_threads(); if (error) @@ -3810,11 +3845,13 @@ int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len, return 1; } - if (mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len, - next_log_number)) - return 1; - RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */)); - return 0; + bool ret= 0; + /* Temporarily disable master semisync before reseting master. */ + repl_semisync_master.before_reset_master(); + ret= mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len, + next_log_number); + repl_semisync_master.after_reset_master(); + return ret; } @@ -3931,7 +3968,7 @@ bool mysql_show_binlog_events(THD* thd) my_off_t scan_pos = BIN_LOG_HEADER_SIZE; while (scan_pos < pos) { - ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event, + ev= Log_event::read_log_event(&log, description_event, opt_master_verify_checksum); scan_pos = my_b_tell(&log); if (ev == NULL || !ev->is_valid()) @@ -3965,7 +4002,7 @@ bool mysql_show_binlog_events(THD* thd) my_b_seek(&log, pos); for (event_count = 0; - (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0, + (ev = Log_event::read_log_event(&log, description_event, opt_master_verify_checksum)); ) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index cada01cb566..bf0b04bcf7c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016 Oracle and/or its affiliates. - Copyright (c) 2009, 2016 MariaDB + Copyright (c) 2009, 2016, 2017 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 @@ -29,7 +29,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_select.h" @@ -50,7 +50,6 @@ #include "filesort.h" // filesort_free_buffers #include "sql_union.h" // mysql_union #include "opt_subselect.h" -#include "log_slow.h" #include "sql_derived.h" #include "sql_statistics.h" #include "sql_cte.h" @@ -81,9 +80,11 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", "index_merge", "hash_ALL", "hash_range", "hash_index", "hash_index_merge" }; +LEX_CSTRING group_key= {STRING_WITH_LEN("group_key")}; +LEX_CSTRING distinct_key= {STRING_WITH_LEN("distinct_key")}; + struct st_sargable_param; -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); static bool make_join_statistics(JOIN *join, List<TABLE_LIST> &leaves, DYNAMIC_ARRAY *keyuse); static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, @@ -91,8 +92,6 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables, COND *conds, table_map table_map, SELECT_LEX *select_lex, SARGABLE_PARAM **sargables); -static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, - bool skip_unprefixed_keyparts); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, @@ -711,7 +710,7 @@ JOIN::prepare(TABLE_LIST *tables_init, union_part= unit_arg->is_unit_op(); if (select_lex->handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(1); + DBUG_RETURN(-1); thd->lex->current_select->context_analysis_place= NO_MATTER; thd->lex->current_select->is_item_list_lookup= 1; @@ -962,6 +961,8 @@ JOIN::prepare(TABLE_LIST *tables_init, (*ord->item)->field_type() == MYSQL_TYPE_BIT) { Item_field *field= new (thd->mem_root) Item_field(thd, *(Item_field**)ord->item); + if (!field) + DBUG_RETURN(-1); int el= all_fields.elements; ref_ptrs[el]= field; all_fields.push_front(field, thd->mem_root); @@ -1094,14 +1095,16 @@ err: DBUG_RETURN(res); /* purecov: inspected */ } -void JOIN::build_explain() + +bool JOIN::build_explain() { create_explain_query_if_not_exists(thd->lex, thd->mem_root); have_query_plan= QEP_AVAILABLE; - save_explain_data(thd->lex->explain, false /* can overwrite */, - need_tmp, - !skip_sort_order && !no_order && (order || group_list), - select_distinct); + if (save_explain_data(thd->lex->explain, false /* can overwrite */, + need_tmp, + !skip_sort_order && !no_order && (order || group_list), + select_distinct)) + return 1; uint select_nr= select_lex->select_number; JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt(); for (uint i= 0; i < aggr_tables; i++, curr_tab++) @@ -1119,8 +1122,10 @@ void JOIN::build_explain() get_using_temporary_read_tracker(); } } + return 0; } + int JOIN::optimize() { int res= 0; @@ -1133,14 +1138,13 @@ int JOIN::optimize() if (optimization_state != JOIN::NOT_OPTIMIZED) return FALSE; optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS; - is_for_splittable_grouping_derived= false; res= optimize_inner(); } if (!with_two_phase_optimization || init_state == JOIN::OPTIMIZATION_PHASE_1_DONE) { if (!res && have_query_plan != QEP_DELETED) - build_explain(); + res= build_explain(); optimization_state= JOIN::OPTIMIZATION_DONE; } return res; @@ -1217,6 +1221,11 @@ JOIN::optimize_inner() DBUG_RETURN(TRUE); table_count= select_lex->leaf_tables.elements; } + + if (select_lex->first_cond_optimization && + transform_in_predicates_into_in_subq(thd)) + DBUG_RETURN(1); + // Update used tables after all handling derived table procedures select_lex->update_used_tables(); @@ -1574,9 +1583,6 @@ int JOIN::optimize_stage2() if (subq_exit_fl) goto setup_subq_exit; - if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) - DBUG_RETURN(1); - if (thd->check_killed()) DBUG_RETURN(1); @@ -1584,6 +1590,9 @@ int JOIN::optimize_stage2() if (get_best_combination()) DBUG_RETURN(1); + if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); + if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS)) drop_unused_derived_keys(); @@ -1729,6 +1738,9 @@ int JOIN::optimize_stage2() { ref_item= substitute_for_best_equal_field(thd, tab, ref_item, equals, map2table); + if (thd->is_fatal_error) + DBUG_RETURN(1); + if (first_inner) { equals= first_inner->cond_equal; @@ -2041,7 +2053,8 @@ int JOIN::optimize_stage2() /* Perform FULLTEXT search before all regular searches */ if (!(select_options & SELECT_DESCRIBE)) - init_ftfuncs(thd, select_lex, MY_TEST(order)); + if (init_ftfuncs(thd, select_lex, MY_TEST(order))) + DBUG_RETURN(1); /* It's necessary to check const part of HAVING cond as @@ -2402,20 +2415,22 @@ bool JOIN::make_aggr_tables_info() /* All optimization is done. Check if we can use the storage engines - group by handler to evaluate the group by + group by handler to evaluate the group by. + Some storage engines, like spider can also do joins, group by and + distinct in the engine, so we do this for all queries, not only + GROUP BY queries. */ - if (tables_list && (tmp_table_param.sum_func_count || group_list) && - !procedure) + if (tables_list && !procedure) { /* At the moment we only support push down for queries where all tables are in the same storage engine */ TABLE_LIST *tbl= tables_list; - handlerton *ht= tbl && tbl->table ? tbl->table->file->ht : 0; + handlerton *ht= tbl && tbl->table ? tbl->table->file->partition_ht() : 0; for (tbl= tbl->next_local; ht && tbl; tbl= tbl->next_local) { - if (!tbl->table || tbl->table->file->ht != ht) + if (!tbl->table || tbl->table->file->partition_ht() != ht) ht= 0; } @@ -2428,7 +2443,8 @@ bool JOIN::make_aggr_tables_info() if (gbh) { - pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh); + if (!(pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh))) + DBUG_RETURN(1); /* We must store rows in the tmp table if we need to do an ORDER BY or DISTINCT and the storage handler can't handle it. @@ -2445,7 +2461,8 @@ bool JOIN::make_aggr_tables_info() curr_tab->ref.key= -1; curr_tab->join= this; - curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param); + if (!(curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param))) + DBUG_RETURN(1); TABLE* table= create_tmp_table(thd, curr_tab->tmp_table_param, all_fields, NULL, query.distinct, @@ -2455,7 +2472,8 @@ bool JOIN::make_aggr_tables_info() if (!table) DBUG_RETURN(1); - curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab); + if (!(curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab))) + DBUG_RETURN(1); curr_tab->aggr->set_write_func(::end_send); curr_tab->table= table; /* @@ -2895,7 +2913,8 @@ bool JOIN::make_aggr_tables_info() curr_tab= join_tab + exec_join_tab_cnt() + aggr_tables - 1; if (select_lex->window_funcs.elements) { - curr_tab->window_funcs_step= new Window_funcs_computation; + if (!(curr_tab->window_funcs_step= new Window_funcs_computation)) + DBUG_RETURN(true); if (curr_tab->window_funcs_step->setup(thd, &select_lex->window_funcs, curr_tab)) DBUG_RETURN(true); @@ -2937,7 +2956,8 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields, !select_lex->with_sum_func) ? select_limit : HA_POS_ERROR; - tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param); + if (!(tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param))) + DBUG_RETURN(true); tab->tmp_table_param->skip_create_table= true; TABLE* table= create_tmp_table(thd, tab->tmp_table_param, *table_fields, table_group, distinct, @@ -2951,8 +2971,7 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields, DBUG_ASSERT(tab > tab->join->join_tab || !top_join_tab_count || !tables_list); if (tab > join_tab) (tab - 1)->next_select= sub_select_postjoin_aggr; - tab->aggr= new (thd->mem_root) AGGR_OP(tab); - if (!tab->aggr) + if (!(tab->aggr= new (thd->mem_root) AGGR_OP(tab))) goto err; tab->table= table; table->reginfo.join_tab= tab; @@ -3102,33 +3121,42 @@ bool JOIN::setup_subquery_caches() select_lex->expr_cache_may_be_used[IN_ON] || select_lex->expr_cache_may_be_used[NO_MATTER]) { - if (conds) - conds= conds->transform(thd, &Item::expr_cache_insert_transformer, - NULL); JOIN_TAB *tab; + if (conds && + !(conds= conds->transform(thd, &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { - if (tab->select_cond) - tab->select_cond= - tab->select_cond->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (tab->select_cond && + !(tab->select_cond= + tab->select_cond->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); if (tab->cache_select && tab->cache_select->cond) - tab->cache_select->cond= - tab->cache_select-> - cond->transform(thd, &Item::expr_cache_insert_transformer, - NULL); - + if (!(tab->cache_select->cond= + tab->cache_select-> + cond->transform(thd, &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } - if (having) - having= having->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (having && + !(having= having->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); + if (tmp_having) { DBUG_ASSERT(having == NULL); - tmp_having= tmp_having->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (!(tmp_having= + tmp_having->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } } if (select_lex->expr_cache_may_be_used[SELECT_LIST] || @@ -3139,9 +3167,11 @@ bool JOIN::setup_subquery_caches() Item *item; while ((item= li++)) { - Item *new_item= - item->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + Item *new_item; + if (!(new_item= + item->transform(thd, &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); if (new_item != item) { thd->change_item_tree(li.ref(), new_item); @@ -3149,18 +3179,22 @@ bool JOIN::setup_subquery_caches() } for (ORDER *tmp_group= group_list; tmp_group ; tmp_group= tmp_group->next) { - *tmp_group->item= - (*tmp_group->item)->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (!(*tmp_group->item= + (*tmp_group->item)->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } } if (select_lex->expr_cache_may_be_used[NO_MATTER]) { for (ORDER *ord= order; ord; ord= ord->next) { - *ord->item= - (*ord->item)->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (!(*ord->item= + (*ord->item)->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } } DBUG_RETURN(FALSE); @@ -3285,7 +3319,8 @@ JOIN::reinit() } if (!(select_options & SELECT_DESCRIBE)) - init_ftfuncs(thd, select_lex, MY_TEST(order)); + if (init_ftfuncs(thd, select_lex, MY_TEST(order))) + DBUG_RETURN(1); DBUG_RETURN(0); } @@ -3327,7 +3362,14 @@ err: } -void JOIN::save_explain_data(Explain_query *output, bool can_overwrite, +/** + @retval + 0 ok + 1 error +*/ + + +bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite, bool need_tmp_table, bool need_order, bool distinct) { @@ -3346,9 +3388,8 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite, /* It's a degenerate join */ message= zero_result_cause ? zero_result_cause : "No tables used"; } - save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order, - distinct, message); - return; + return save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order, + distinct, message); } /* @@ -3368,11 +3409,13 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite, { if (join_tab[i].filesort) { - join_tab[i].filesort->tracker= - new Filesort_tracker(thd->lex->analyze_stmt); + if (!(join_tab[i].filesort->tracker= + new Filesort_tracker(thd->lex->analyze_stmt))) + return 1; } } } + return 0; } @@ -3639,7 +3682,11 @@ JOIN::destroy() cleanup_item_list(tmp_all_fields1); cleanup_item_list(tmp_all_fields3); destroy_sj_tmp_tables(this); - delete_dynamic(&keyuse); + delete_dynamic(&keyuse); + if (save_qep) + delete(save_qep); + if (ext_keyuses_for_splitting) + delete(ext_keyuses_for_splitting); delete procedure; DBUG_RETURN(error); } @@ -4093,6 +4140,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array);); } + for (s= stat; s < stat_end; s++) + { + if (s->table->is_splittable()) + s->add_keyuses_for_splitting(); + } + join->const_table_map= no_rows_const_tables; join->const_tables= const_count; eliminate_tables(join); @@ -4169,6 +4222,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, keyuse->val->is_null() && keyuse->null_rejecting) { s->type= JT_CONST; + s->table->const_table= 1; mark_as_null_row(table); found_const_table_map|= table->map; join->const_table_map|= table->map; @@ -4274,6 +4328,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, s->type= JT_CONST; join->const_table_map|=table->map; set_position(join,const_count++,s,start_keyuse); + /* create_ref_for_key will set s->table->const_table */ if (create_ref_for_key(join, s, start_keyuse, FALSE, found_const_table_map)) goto error; @@ -4479,12 +4534,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, join->const_table_map|= s->table->map; set_position(join,const_count++,s,(KEYUSE*) 0); s->type= JT_CONST; + s->table->const_table= 1; if (*s->on_expr_ref) { /* Generate empty row */ s->info= ET_IMPOSSIBLE_ON_CONDITION; found_const_table_map|= s->table->map; - s->type= JT_CONST; mark_as_null_row(s->table); // All fields are NULL } } @@ -4564,9 +4619,6 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (join->choose_subquery_plan(all_table_map & ~join->const_table_map)) goto error; - if (join->improve_chosen_plan(join->thd)) - goto error; - DEBUG_SYNC(join->thd, "inside_make_join_statistics"); DBUG_RETURN(0); @@ -4596,23 +4648,6 @@ error: keyuse Pointer to possible keys *****************************************************************************/ -/// Used when finding key fields -struct KEY_FIELD { - Field *field; - Item_bool_func *cond; - Item *val; ///< May be empty if diff constant - uint level; - uint optimize; - bool eq_func; - /** - If true, the condition this struct represents will not be satisfied - when val IS NULL. - */ - bool null_rejecting; - bool *cond_guard; /* See KEYUSE::cond_guard */ - uint sj_pred_no; /* See KEYUSE::sj_pred_no */ -}; - /** Merge new key definitions to old ones, remove those not used in both. @@ -5263,7 +5298,7 @@ Item_func_ne::add_key_fields(JOIN *join, KEY_FIELD **key_fields, /* QQ: perhaps test for !is_local_field(args[1]) is not really needed here. Other comparison functions, e.g. Item_func_le, Item_func_gt, etc, - do not have this test. See Item_bool_func2::add_key_field_optimize_op(). + do not have this test. See Item_bool_func2::add_key_fieldoptimize_op(). Check with the optimizer team. */ if (is_local_field(args[0]) && !is_local_field(args[1])) @@ -5446,6 +5481,7 @@ add_keyuse(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field, keyuse.null_rejecting= key_field->null_rejecting; keyuse.cond_guard= key_field->cond_guard; keyuse.sj_pred_no= key_field->sj_pred_no; + keyuse.validity_ref= 0; return (insert_dynamic(keyuse_array,(uchar*) &keyuse)); } @@ -5491,7 +5527,9 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) key_field->val->used_tables()) { if (!field->can_optimize_hash_join(key_field->cond, key_field->val)) - return false; + return false; + if (form->is_splittable()) + form->add_splitting_info_for_key_field(key_field); /* If a key use is extracted from an equi-join predicate then it is added not only as a key use for every index whose component can @@ -5505,7 +5543,6 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) return FALSE; } - static bool add_ft_keys(DYNAMIC_ARRAY *keyuse_array, JOIN_TAB *stat,COND *cond,table_map usable_tables) @@ -5567,6 +5604,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, keyuse.optimize= 0; keyuse.keypart_map= 0; keyuse.sj_pred_no= UINT_MAX; + keyuse.validity_ref= 0; return insert_dynamic(keyuse_array,(uchar*) &keyuse); } @@ -5854,8 +5892,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, Special treatment for ft-keys. */ -static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, - bool skip_unprefixed_keyparts) +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts) { KEYUSE key_end, *prev, *save_pos, *use; uint found_eq_constant, i; @@ -5923,7 +5961,7 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, Update some values in keyuse for faster choose_plan() loop. */ -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) +void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) { KEYUSE *end,*keyuse= dynamic_element(keyuse_array, 0, KEYUSE*); @@ -5964,7 +6002,6 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) } - /** Check for the presence of AGGFN(DISTINCT a) queries that may be subject to loose index scan. @@ -6272,6 +6309,7 @@ best_access_path(JOIN *join, bool best_uses_jbuf= FALSE; MY_BITMAP *eq_join_set= &s->table->eq_join_set; KEYUSE *hj_start_key= 0; + SplM_plan_info *spl_plan= 0; disable_jbuf= disable_jbuf || idx == join->const_tables; @@ -6281,7 +6319,10 @@ best_access_path(JOIN *join, bitmap_clear_all(eq_join_set); loose_scan_opt.init(join, s, remaining_tables); - + + if (s->table->is_splittable()) + spl_plan= s->choose_best_splitting(record_count, remaining_tables); + if (s->keyuse) { /* Use key if possible */ KEYUSE *keyuse; @@ -6331,7 +6372,7 @@ best_access_path(JOIN *join, loose_scan_opt.next_ref_key(); DBUG_PRINT("info", ("Considering ref access on key %s", - keyuse->table->key_info[keyuse->key].name)); + keyuse->table->key_info[keyuse->key].name.str)); do /* For each keypart */ { @@ -6346,6 +6387,7 @@ best_access_path(JOIN *join, 2. we won't get two ref-or-null's */ if (!(remaining_tables & keyuse->used_tables) && + (!keyuse->validity_ref || *keyuse->validity_ref) && s->access_from_tables_is_allowed(keyuse->used_tables, join->sjm_lookup_tables) && !(ref_or_null_part && (keyuse->optimize & @@ -6658,6 +6700,7 @@ best_access_path(JOIN *join, tmp += s->startup_cost; loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); } /* not ft_key */ + if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE) { best_time= tmp + records/(double) TIME_FOR_COMPARE; @@ -6845,6 +6888,7 @@ best_access_path(JOIN *join, pos->ref_depend_map= best_ref_depends_map; pos->loosescan_picker.loosescan_key= MAX_KEY; pos->use_join_buffer= best_uses_jbuf; + pos->spl_plan= spl_plan; loose_scan_opt.save_to_position(s, loose_scan_pos); @@ -8899,192 +8943,15 @@ JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab) return tab; } -static -bool key_can_be_used_to_split_by_fields(KEY *key_info, uint used_key_parts, - List<Field> &fields) -{ - if (used_key_parts < fields.elements) - return false; - List_iterator_fast<Field> li(fields); - Field *fld; - KEY_PART_INFO *start= key_info->key_part; - KEY_PART_INFO *end= start + fields.elements; - while ((fld= li++)) - { - KEY_PART_INFO *key_part; - for (key_part= start; key_part < end; key_part++) - { - if (key_part->fieldnr == fld->field_index + 1) - break; - } - if (key_part == end) - return false; - } - return true; -} - -bool JOIN::check_for_splittable_grouping_derived(THD *thd) -{ - partition_list= 0; - st_select_lex_unit *unit= select_lex->master_unit(); - TABLE_LIST *derived= unit->derived; - if (!optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED)) - return false; - if (!(derived && derived->is_materialized_derived())) - return false; - if (unit->first_select()->next_select()) - return false; - if (derived->prohibit_cond_pushdown) - return false; - if (derived->is_recursive_with_table()) - return false; - if (group_list) - { - if (!select_lex->have_window_funcs()) - partition_list= group_list; - } - else if (select_lex->have_window_funcs() && - select_lex->window_specs.elements == 1) - { - partition_list= - select_lex->window_specs.head()->partition_list->first; - } - if (!partition_list) - return false; - - ORDER *ord; - TABLE *table= 0; - key_map ref_keys; - uint group_fields= 0; - ref_keys.set_all(); - for (ord= partition_list; ord; ord= ord->next, group_fields++) - { - Item *ord_item= *ord->item; - if (ord_item->real_item()->type() != Item::FIELD_ITEM) - return false; - Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; - if (!table) - table= ord_field->table; - else if (table != ord_field->table) - return false; - ref_keys.intersect(ord_field->part_of_key); - } - if (ref_keys.is_clear_all()) - return false; - - uint i; - List<Field> grouping_fields; - List<Field> splitting_fields; - List_iterator<Item> li(fields_list); - for (ord= partition_list; ord; ord= ord->next) - { - Item *item; - i= 0; - while ((item= li++)) - { - if ((*ord->item)->eq(item, 0)) - break; - i++; - } - if (!item) - return false; - if (splitting_fields.push_back(derived->table->field[i], thd->mem_root)) - return false; - Item_field *ord_field= (Item_field *)(item->real_item()); - if (grouping_fields.push_back(ord_field->field, thd->mem_root)) - return false; - li.rewind(); - } - - for (i= 0; i < table->s->keys; i++) - { - if (!(ref_keys.is_set(i))) - continue; - KEY *key_info= table->key_info + i; - if (key_can_be_used_to_split_by_fields(key_info, - table->actual_n_key_parts(key_info), - grouping_fields)) - break; - } - if (i == table->s->keys) - return false; - - derived->table->splitting_fields= splitting_fields; - is_for_splittable_grouping_derived= true; - return true; -} - bool JOIN::check_two_phase_optimization(THD *thd) { - if (!check_for_splittable_grouping_derived(thd)) - return false; - return true; + if (check_for_splittable_materialized()) + return true; + return false; } -Item *JOIN_TAB::get_splitting_cond_for_grouping_derived(THD *thd) -{ - /* this is a stub */ - TABLE_LIST *derived= table->pos_in_table_list; - st_select_lex *sel= derived->get_unit()->first_select(); - Item *cond= 0; - table_map used_tables= OUTER_REF_TABLE_BIT; - POSITION *pos= join->best_positions; - for (; pos->table != this; pos++) - { - used_tables|= pos->table->table->map; - } - - if (!pos->key) - return 0; - - KEY *key_info= table->key_info + pos->key->key; - if (!key_can_be_used_to_split_by_fields(key_info, - key_info->user_defined_key_parts, - table->splitting_fields)) - return 0; - - create_ref_for_key(join, this, pos->key, - false, used_tables); - List<Item> cond_list; - KEY_PART_INFO *start= key_info->key_part; - KEY_PART_INFO *end= start + table->splitting_fields.elements; - List_iterator_fast<Field> li(table->splitting_fields); - Field *fld= li++; - for (ORDER *ord= sel->join->partition_list; ord; - ord= ord->next, fld= li++) - { - Item *left_item= (*ord->item)->build_clone(thd, thd->mem_root); - uint i= 0; - for (KEY_PART_INFO *key_part= start; key_part < end; key_part++, i++) - { - if (key_part->fieldnr == fld->field_index + 1) - break; - } - Item *right_item= ref.items[i]->build_clone(thd, thd->mem_root); - Item_func_eq *eq_item= 0; - right_item= right_item->build_clone(thd, thd->mem_root); - if (left_item && right_item) - { - right_item->walk(&Item::set_fields_as_dependent_processor, - false, join->select_lex); - right_item->update_used_tables(); - eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); - } - if (!eq_item || cond_list.push_back(eq_item, thd->mem_root)) - return 0; - } - switch (cond_list.elements) { - case 0: break; - case 1: cond= cond_list.head(); break; - default: cond= new (thd->mem_root) Item_cond_and(thd, cond_list); - } - if (cond) - cond->fix_fields(thd,0); - return cond; -} - bool JOIN::inject_cond_into_where(Item *injected_cond) { Item *where_item= injected_cond; @@ -9119,48 +8986,6 @@ bool JOIN::inject_cond_into_where(Item *injected_cond) } -bool JOIN::push_splitting_cond_into_derived(THD *thd, Item *cond) -{ - enum_reopt_result reopt_result= REOPT_NONE; - table_map all_table_map= 0; - for (JOIN_TAB *tab= join_tab; - tab < join_tab + top_join_tab_count; tab++) - all_table_map|= tab->table->map; - reopt_result= reoptimize(cond, all_table_map & ~const_table_map, NULL); - if (reopt_result == REOPT_ERROR) - return true; - if (inject_cond_into_where(cond)) - return true; - if (cond->used_tables() & OUTER_REF_TABLE_BIT) - { - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; - st_select_lex_unit *unit= select_lex->master_unit(); - unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; - } - return false; -} - -bool JOIN::improve_chosen_plan(THD *thd) -{ - for (JOIN_TAB *tab= join_tab + const_tables; - tab < join_tab + table_count; tab++) - { - TABLE_LIST *tbl= tab->table->pos_in_table_list; - if (tbl->is_materialized_derived()) - { - st_select_lex *sel= tbl->get_unit()->first_select(); - JOIN *derived_join= sel->join; - if (derived_join && derived_join->is_for_splittable_grouping_derived) - { - Item *cond= tab->get_splitting_cond_for_grouping_derived(thd); - if (cond && derived_join->push_splitting_cond_into_derived(thd, cond)) - return true; - } - } - } - return false; -} - static Item * const null_ptr= NULL; @@ -9229,6 +9054,9 @@ bool JOIN::get_best_combination() full_join=0; hash_join= FALSE; + if (fix_all_splittings_in_plan()) + DBUG_RETURN(TRUE); + fix_semijoin_strategies_for_picked_join_order(this); JOIN_TAB_RANGE *root_range; @@ -9443,7 +9271,8 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->flags= HA_GENERATED_KEY; keyinfo->is_statistics_from_stat_tables= FALSE; - keyinfo->name= (char *) "$hj"; + keyinfo->name.str= "$hj"; + keyinfo->name.length= 3; keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts); if (!keyinfo->rec_per_key) DBUG_RETURN(TRUE); @@ -9568,6 +9397,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, do { if (!(~used_tables & keyuse->used_tables) && + (!keyuse->validity_ref || *keyuse->validity_ref) && j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse)) { if (are_tables_local(j, keyuse->val->used_tables())) @@ -9638,6 +9468,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, for (i=0 ; i < keyparts ; keyuse++,i++) { while (((~used_tables) & keyuse->used_tables) || + (keyuse->validity_ref && !(*keyuse->validity_ref)) || !j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse) || keyuse->keypart == NO_KEYPART || (keyuse->keypart != @@ -11022,32 +10853,32 @@ pick_table_access_method(JOIN_TAB *tab) { case JT_REF: tab->read_first_record= join_read_always_key; - tab->read_record.read_record= join_read_next_same; + tab->read_record.read_record_func= join_read_next_same; break; case JT_REF_OR_NULL: tab->read_first_record= join_read_always_key_or_null; - tab->read_record.read_record= join_read_next_same_or_null; + tab->read_record.read_record_func= join_read_next_same_or_null; break; case JT_CONST: tab->read_first_record= join_read_const; - tab->read_record.read_record= join_no_more_records; + tab->read_record.read_record_func= join_no_more_records; break; case JT_EQ_REF: tab->read_first_record= join_read_key; - tab->read_record.read_record= join_no_more_records; + tab->read_record.read_record_func= join_no_more_records; break; case JT_FT: tab->read_first_record= join_ft_read_first; - tab->read_record.read_record= join_ft_read_next; + tab->read_record.read_record_func= join_ft_read_next; break; case JT_SYSTEM: tab->read_first_record= join_read_system; - tab->read_record.read_record= join_no_more_records; + tab->read_record.read_record_func= join_no_more_records; break; /* keep gcc happy */ @@ -12249,7 +12080,8 @@ bool JOIN_TAB::preread_init() /* init ftfuns for just initialized derived table */ if (table->fulltext_searched) - init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order)); + if (init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order))) + return TRUE; return FALSE; } @@ -14294,7 +14126,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, equals on top level, or the constant. */ Item *head_item= (!item_const && current_sjm && - current_sjm_head != field_item) ? current_sjm_head: head; + current_sjm_head != field_item) ? current_sjm_head: head; Item *head_real_item= head_item->real_item(); if (head_real_item->type() == Item::FIELD_ITEM) head_item= head_real_item; @@ -14459,7 +14291,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab, This works OK with PS/SP re-execution as changes are made to the arguments of AND/OR items only */ - if (new_item != item) + if (new_item && new_item != item) li.replace(new_item); } @@ -14538,7 +14370,9 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab, while((item_equal= it++)) { REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab}; - cond= cond->transform(thd, &Item::replace_equal_field, (uchar *) &arg); + if (!(cond= cond->transform(thd, &Item::replace_equal_field, + (uchar *) &arg))) + return 0; } cond_equal= cond_equal->upper_levels; } @@ -14695,6 +14529,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, { cond->marker=1; COND_CMP *tmp2; + /* Will work, even if malloc would fail */ if ((tmp2= new (thd->mem_root) COND_CMP(and_father, func))) save_list->push_back(tmp2); } @@ -14727,6 +14562,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, thd->change_item_tree(args + 1, value); cond->marker=1; COND_CMP *tmp2; + /* Will work, even if malloc would fail */ if ((tmp2=new (thd->mem_root) COND_CMP(and_father, func))) save_list->push_back(tmp2); } @@ -16158,6 +15994,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value, query_cache_abort(thd, &thd->query_cache_tls); #endif COND *new_cond, *cond= this; + /* If this fails, we will catch it later before executing query */ if ((new_cond= new (thd->mem_root) Item_func_eq(thd, args[0], new (thd->mem_root) Item_int(thd, "last_insert_id()", thd->read_first_successful_insert_id_in_prev_stmt(), @@ -16813,8 +16650,6 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, (int) distinct, (int) save_sum_fields, (ulong) rows_limit, MY_TEST(group))); - thd->query_plan_flags|= QPLAN_TMP_TABLE; - if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES)) temp_pool_slot = bitmap_lock_set_next(&temp_pool); @@ -16997,7 +16832,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, Item *arg= sum_item->get_arg(i); if (!arg->const_item()) { - Field *new_field= + Item *tmp_item; + Field *new_field= create_tmp_field(thd, table, arg, arg->type(), ©_func, tmp_from_field, &default_field[fieldnr], group != 0,not_all_columns, @@ -17022,7 +16858,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, string_total_length+= new_field->pack_length(); } thd->mem_root= mem_root_save; - arg= sum_item->set_arg(i, thd, new (thd->mem_root) Item_temptable_field(thd, new_field)); + if (!(tmp_item= new (thd->mem_root) + Item_temptable_field(thd, new_field))) + goto err; + arg= sum_item->set_arg(i, thd, tmp_item); thd->mem_root= &table->mem_root; if (param->force_not_null_cols) { @@ -17377,7 +17216,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->collected_stats= NULL; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; - keyinfo->name= (char*) "group_key"; + keyinfo->name= group_key; ORDER *cur_group= group; for (; cur_group ; cur_group= cur_group->next, key_part_info++) { @@ -17488,7 +17327,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY; keyinfo->ext_key_flags= keyinfo->flags; keyinfo->key_length= 0; // Will compute the sum of the parts below. - keyinfo->name= (char*) "distinct_key"; + keyinfo->name= distinct_key; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->read_stats= NULL; @@ -17618,7 +17457,6 @@ err: } - /****************************************************************************/ void *Virtual_tmp_table::operator new(size_t size, THD *thd) throw() @@ -17942,7 +17780,6 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, table->in_use->inc_status_created_tmp_disk_tables(); table->in_use->inc_status_created_tmp_tables(); - table->in_use->query_plan_flags|= QPLAN_TMP_DISK; share->db_record_offset= 1; table->set_created(); DBUG_RETURN(0); @@ -18097,7 +17934,6 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } table->in_use->inc_status_created_tmp_disk_tables(); table->in_use->inc_status_created_tmp_tables(); - table->in_use->query_plan_flags|= QPLAN_TMP_DISK; share->db_record_offset= 1; table->set_created(); DBUG_RETURN(0); @@ -18264,7 +18100,12 @@ free_tmp_table(THD *thd, TABLE *entry) { entry->file->ha_index_or_rnd_end(); if (entry->db_stat) + { + entry->file->info(HA_STATUS_VARIABLE); + thd->tmp_tables_size+= (entry->file->stats.data_file_length + + entry->file->stats.index_file_length); entry->file->ha_drop_table(entry->s->table_name.str); + } else entry->file->ha_delete_table(entry->s->table_name.str); delete entry->file; @@ -18605,6 +18446,10 @@ bool instantiate_tmp_table(TABLE *table, KEY *keyinfo, { if (table->s->db_type() == TMP_ENGINE_HTON) { + /* + If it is not heap (in-memory) table then convert index to unique + constrain. + */ if (create_internal_tmp_table(table, keyinfo, start_recinfo, recinfo, options)) return TRUE; @@ -18970,7 +18815,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) skip_over= TRUE; } - error= info->read_record(info); + error= info->read_record(); if (skip_over && !error) { @@ -19488,6 +19333,7 @@ join_read_system(JOIN_TAB *tab) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); + table->const_table= 1; mark_as_null_row(tab->table); empty_record(table); // Make empty record return -1; @@ -19813,7 +19659,7 @@ int read_first_record_seq(JOIN_TAB *tab) { if (tab->read_record.table->file->ha_rnd_init_with_error(1)) return 1; - return (*tab->read_record.read_record)(&tab->read_record); + return tab->read_record.read_record(); } static int @@ -19873,7 +19719,7 @@ int join_init_read_record(JOIN_TAB *tab) if (init_read_record(&tab->read_record, tab->join->thd, tab->table, tab->select, tab->filesort_result, 1,1, FALSE)) return 1; - return (*tab->read_record.read_record)(&tab->read_record); + return tab->read_record.read_record(); } int @@ -19893,9 +19739,9 @@ join_read_record_no_init(JOIN_TAB *tab) tab->read_record.copy_field= save_copy; tab->read_record.copy_field_end= save_copy_end; - tab->read_record.read_record= rr_sequential_and_unpack; + tab->read_record.read_record_func= rr_sequential_and_unpack; - return (*tab->read_record.read_record)(&tab->read_record); + return tab->read_record.read_record(); } @@ -19928,7 +19774,7 @@ join_read_first(JOIN_TAB *tab) !table->covering_keys.is_set(tab->index) || table->file->keyread == tab->index); tab->table->status=0; - tab->read_record.read_record=join_read_next; + tab->read_record.read_record_func= join_read_next; tab->read_record.table=table; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; @@ -19968,7 +19814,7 @@ join_read_last(JOIN_TAB *tab) !table->covering_keys.is_set(tab->index) || table->file->keyread == tab->index); tab->table->status=0; - tab->read_record.read_record=join_read_prev; + tab->read_record.read_record_func= join_read_prev; tab->read_record.table=table; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; @@ -21934,7 +21780,7 @@ check_reverse_order: with key part (A) and then traverse the index backwards. */ tab->read_first_record= join_read_last_key; - tab->read_record.read_record= join_read_prev_same; + tab->read_record.read_record_func= join_read_prev_same; /* Cancel Pushed Index Condition, as it doesn't work for reverse scans. */ @@ -22096,7 +21942,7 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort) table->file->ha_end_keyread(); if (tab->type == JT_FT) - table->file->ft_end(); + table->file->ha_ft_end(); else table->file->ha_index_or_rnd_end(); @@ -22891,6 +22737,10 @@ setup_new_fields(THD *thd, List<Item> &fields, Try to use the fields in the order given by 'order' to allow one to optimize away 'order by'. + + @retval + 0 OOM error if thd->is_fatal_error is set. Otherwise group was eliminated + # Pointer to new group */ ORDER * @@ -22953,6 +22803,8 @@ create_distinct_group(THD *thd, Ref_ptr_array ref_pointer_array, BIT type and will be returned [el]client. */ Item_field *new_item= new (thd->mem_root) Item_field(thd, (Item_field*)item); + if (!new_item) + return 0; int el= all_fields.elements; orig_ref_pointer_array[el]= new_item; all_fields.push_front(new_item, thd->mem_root); @@ -23634,7 +23486,10 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) item_field= item; else if (item->type() == Item::FIELD_ITEM) - item_field= item->get_tmp_table_item(thd); + { + if (!(item_field= item->get_tmp_table_item(thd))) + DBUG_RETURN(true); + } else if (item->type() == Item::FUNC_ITEM && ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC) { @@ -23742,8 +23597,13 @@ change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, if (item->type() == Item::SUM_FUNC_ITEM && item->const_item()) new_item= item; else - new_item= item->get_tmp_table_item(thd); - res_all_fields.push_back(new_item, thd->mem_root); + { + if (!(new_item= item->get_tmp_table_item(thd))) + return 1; + } + + if (res_all_fields.push_back(new_item, thd->mem_root)) + return 1; ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]= new_item; } @@ -24109,7 +23969,9 @@ bool JOIN::rollup_init() */ for (i= 0 ; i < send_group_parts ; i++) { - rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd); + if (!(rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd))) + return true; + List<Item> *rollup_fields= &rollup.fields[i]; rollup_fields->empty(); rollup.ref_pointer_arrays[i]= Ref_ptr_array(ref_array, all_fields.elements); @@ -24453,8 +24315,12 @@ void JOIN::clear() } -/* +/** Print an EXPLAIN line with all NULLs and given message in the 'Extra' column + + @retval + 0 ok + 1 OOM error or error from send_data() */ int print_explain_message_line(select_result_sink *result, @@ -24513,7 +24379,7 @@ int print_explain_message_line(select_result_sink *result, else item_list.push_back(item_null, mem_root); - if (result->send_data(item_list)) + if (thd->is_fatal_error || result->send_data(item_list)) return 1; return 0; } @@ -24547,13 +24413,14 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table, for (j=0 ; j < table->s->keys ; j++) { if (possible_keys.is_set(j)) - list.append_str(alloc, table->key_info[j].name); + if (!(list.append_str(alloc, table->key_info[j].name.str))) + return 1; } return 0; } -void JOIN_TAB::save_explain_data(Explain_table_access *eta, +bool JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tables, bool distinct_arg, JOIN_TAB *first_top_tab) { @@ -24582,9 +24449,11 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, if (filesort) { - eta->pre_join_sort= new Explain_aggr_filesort(thd->mem_root, - thd->lex->analyze_stmt, - filesort); + if (!(eta->pre_join_sort= + new Explain_aggr_filesort(thd->mem_root, + thd->lex->analyze_stmt, + filesort))) + return 1; } tracker= &eta->tracker; @@ -24681,7 +24550,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, // psergey-todo: why does this use thd MEM_ROOT??? Doesn't this // break ANALYZE ? thd->mem_root will be freed, and after that we will // attempt to print the query plan? - append_possible_keys(thd->mem_root, eta->possible_keys, table, keys); + if (append_possible_keys(thd->mem_root, eta->possible_keys, table, keys)) + return 1; // psergey-todo: ^ check for error return code /* Build "key", "key_len", and "ref" */ @@ -24702,7 +24572,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, */ if (tab_select && tab_select->quick && tab_type != JT_CONST) { - eta->quick_info= tab_select->quick->get_explain(thd->mem_root); + if (!(eta->quick_info= tab_select->quick->get_explain(thd->mem_root))) + return 1; } if (key_info) /* 'index' or 'ref' access */ @@ -24715,10 +24586,14 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, for (uint kp= 0; kp < ref.key_parts; kp++) { if ((key_part_map(1) << kp) & ref.const_ref_part_map) - eta->ref_list.append_str(thd->mem_root, "const"); + { + if (!(eta->ref_list.append_str(thd->mem_root, "const"))) + return 1; + } else { - eta->ref_list.append_str(thd->mem_root, (*key_ref)->name()); + if (!(eta->ref_list.append_str(thd->mem_root, (*key_ref)->name()))) + return 1; key_ref++; } } @@ -24975,7 +24850,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, if (cache) { eta->push_extra(ET_USING_JOIN_BUFFER); - cache->save_explain_data(&eta->bka_type); + if (cache->save_explain_data(&eta->bka_type)) + return 1; } } @@ -24988,15 +24864,21 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, /* The same for non-merged semi-joins */ eta->non_merged_sjm_number = get_non_merged_semijoin_select(); + + return 0; } /* Walk through join->aggr_tables and save aggregation/grouping query plan into an Explain_select object + + @retval + 0 ok + 1 error */ -void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) +bool save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) { JOIN_TAB *join_tab=join->join_tab + join->exec_join_tab_cnt(); Explain_aggr_node *prev_node; @@ -25008,7 +24890,8 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) { // Each aggregate means a temp.table prev_node= node; - node= new Explain_aggr_tmp_table; + if (!(node= new Explain_aggr_tmp_table)) + return 1; node->child= prev_node; if (join_tab->window_funcs_step) @@ -25016,19 +24899,20 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) Explain_aggr_node *new_node= join_tab->window_funcs_step->save_explain_plan(thd->mem_root, is_analyze); - if (new_node) - { - prev_node=node; - node= new_node; - node->child= prev_node; - } + if (!new_node) + return 1; + + prev_node=node; + node= new_node; + node->child= prev_node; } /* The below matches execution in join_init_read_record() */ if (join_tab->distinct) { prev_node= node; - node= new Explain_aggr_remove_dups; + if (!(node= new Explain_aggr_remove_dups)) + return 1; node->child= prev_node; } @@ -25036,20 +24920,27 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) { Explain_aggr_filesort *eaf = new Explain_aggr_filesort(thd->mem_root, is_analyze, join_tab->filesort); + if (!eaf) + return 1; prev_node= node; node= eaf; node->child= prev_node; } } xpl_sel->aggr_tree= node; + return 0; } -/* +/** Save Query Plan Footprint @note Currently, this function may be called multiple times + + @retval + 0 ok + 1 error */ int JOIN::save_explain_data_intern(Explain_query *output, @@ -25058,7 +24949,6 @@ int JOIN::save_explain_data_intern(Explain_query *output, const char *message) { JOIN *join= this; /* Legacy: this code used to be a non-member function */ - int cur_error= 0; DBUG_ENTER("JOIN::save_explain_data_intern"); DBUG_PRINT("info", ("Select %p, type %s, message %s", join->select_lex, join->select_lex->type, @@ -25076,8 +24966,11 @@ int JOIN::save_explain_data_intern(Explain_query *output, if (message) { - explain= new (output->mem_root) Explain_select(output->mem_root, - thd->lex->analyze_stmt); + if (!(explain= new (output->mem_root) + Explain_select(output->mem_root, + thd->lex->analyze_stmt))) + DBUG_RETURN(1); + join->select_lex->set_explain_type(true); explain->select_id= join->select_lex->select_number; @@ -25090,13 +24983,17 @@ int JOIN::save_explain_data_intern(Explain_query *output, if (select_lex->master_unit()->derived) explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; - save_agg_explain_data(this, explain); + if (save_agg_explain_data(this, explain)) + DBUG_RETURN(1); + output->add_node(explain); } else if (pushdown_query) { - explain= new (output->mem_root) Explain_select(output->mem_root, - thd->lex->analyze_stmt); + if (!(explain= new (output->mem_root) + Explain_select(output->mem_root, + thd->lex->analyze_stmt))) + DBUG_RETURN(1); select_lex->set_explain_type(true); explain->select_id= select_lex->select_number; @@ -25116,6 +25013,9 @@ int JOIN::save_explain_data_intern(Explain_query *output, explain= xpl_sel= new (output->mem_root) Explain_select(output->mem_root, thd->lex->analyze_stmt); + if (!explain) + DBUG_RETURN(1); + table_map used_tables=0; join->select_lex->set_explain_type(true); @@ -25125,7 +25025,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, if (select_lex->master_unit()->derived) xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; - save_agg_explain_data(this, xpl_sel); + if (save_agg_explain_data(this, xpl_sel)) + DBUG_RETURN(1); xpl_sel->exec_const_cond= exec_const_cond; xpl_sel->outer_ref_cond= outer_ref_cond; @@ -25157,6 +25058,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, Explain_table_access *eta= (new (output->mem_root) Explain_table_access(output->mem_root)); + if (!eta) + DBUG_RETURN(1); if (tab->bush_root_tab != prev_bush_root_tab) { if (tab->bush_root_tab) @@ -25164,7 +25067,9 @@ int JOIN::save_explain_data_intern(Explain_query *output, /* We've entered an SJ-Materialization nest. Create an object for it. */ - cur_parent= new (output->mem_root) Explain_basic_join(output->mem_root); + if (!(cur_parent= + new (output->mem_root) Explain_basic_join(output->mem_root))) + DBUG_RETURN(1); JOIN_TAB *first_child= tab->bush_root_tab->bush_children->start; cur_parent->select_id= @@ -25184,7 +25089,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, prev_bush_root_tab= tab->bush_root_tab; cur_parent->add_table(eta, output); - tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab); + if (tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab)) + DBUG_RETURN(1); if (saved_join_tab) tab= saved_join_tab; @@ -25216,10 +25122,10 @@ int JOIN::save_explain_data_intern(Explain_query *output, } } - if (!cur_error && select_lex->is_top_level_node()) + if (select_lex->is_top_level_node()) output->query_plan_ready(); - DBUG_RETURN(cur_error); + DBUG_RETURN(0); } @@ -25680,6 +25586,12 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) { DBUG_ASSERT(thd); + + if (tvc) + { + tvc->print(thd, str, query_type); + return; + } if ((query_type & QT_SHOW_SELECT_NUMBER) && thd->lex->all_selects_list && @@ -26859,7 +26771,7 @@ AGGR_OP::end_send() error= join_init_read_record(join_tab); } else - error= join_tab->read_record.read_record(&join_tab->read_record); + error= join_tab->read_record.read_record(); if (error > 0 || (join->thd->is_error())) // Fatal error rc= NESTED_LOOP_ERROR; diff --git a/sql/sql_select.h b/sql/sql_select.h index 4e2206dd098..82df7d2930f 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -73,9 +73,45 @@ typedef struct keyuse_t { */ uint sj_pred_no; + /* + If this is NULL than KEYUSE is always enabled. + Otherwise it points to the enabling flag for this keyuse (true <=> enabled) + */ + bool *validity_ref; + bool is_for_hash_join() { return is_hash_join_key_no(key); } } KEYUSE; + +struct KEYUSE_EXT: public KEYUSE +{ + /* + This keyuse can be used only when the partial join being extended + contains the tables from this table map + */ + table_map needed_in_prefix; + /* The enabling flag for keyuses usable for splitting */ + bool validity_var; +}; + +/// Used when finding key fields +struct KEY_FIELD { + Field *field; + Item_bool_func *cond; + Item *val; ///< May be empty if diff constant + uint level; + uint optimize; + bool eq_func; + /** + If true, the condition this struct represents will not be satisfied + when val IS NULL. + */ + bool null_rejecting; + bool *cond_guard; /* See KEYUSE::cond_guard */ + uint sj_pred_no; /* See KEYUSE::sj_pred_no */ +}; + + #define NO_KEYPART ((uint)(-1)) class store_key; @@ -201,6 +237,8 @@ class SJ_TMP_TABLE; class JOIN_TAB_RANGE; class AGGR_OP; class Filesort; +struct SplM_plan_info; +class SplM_opt_info; typedef struct st_join_table { st_join_table() {} @@ -608,14 +646,16 @@ typedef struct st_join_table { void remove_redundant_bnl_scan_conds(); - void save_explain_data(Explain_table_access *eta, table_map prefix_tables, + bool save_explain_data(Explain_table_access *eta, table_map prefix_tables, bool distinct, struct st_join_table *first_top_tab); bool use_order() const; ///< Use ordering provided by chosen index? bool sort_table(); bool remove_duplicates(); - Item *get_splitting_cond_for_grouping_derived(THD *thd); - + void add_keyuses_for_splitting(); + SplM_plan_info *choose_best_splitting(double record_count, + table_map remaining_tables); + bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables); } JOIN_TAB; @@ -920,6 +960,9 @@ typedef struct st_position Firstmatch_picker firstmatch_picker; LooseScan_picker loosescan_picker; Sj_materialization_picker sjmat_picker; + + /* Info on splitting plan used at this position */ + SplM_plan_info *spl_plan; } POSITION; typedef Bounds_checked_array<Item_null_result*> Item_null_array; @@ -1053,11 +1096,13 @@ protected: /* Support for plan reoptimization with rewritten conditions. */ enum_reopt_result reoptimize(Item *added_where, table_map join_tables, Join_plan_state *save_to); + /* Choose a subquery plan for a table-less subquery. */ + bool choose_tableless_subquery_plan(); + +public: void save_query_plan(Join_plan_state *save_to); void reset_query_plan(); void restore_query_plan(Join_plan_state *restore_from); - /* Choose a subquery plan for a table-less subquery. */ - bool choose_tableless_subquery_plan(); public: JOIN_TAB *join_tab, **best_ref; @@ -1415,9 +1460,14 @@ public: */ bool implicit_grouping; - bool is_for_splittable_grouping_derived; bool with_two_phase_optimization; - ORDER *partition_list; + + /* Saved execution plan for this join */ + Join_plan_state *save_qep; + /* Info on splittability of the table materialized by this plan*/ + SplM_opt_info *spl_opt_info; + /* Contains info on keyuses usable for splitting */ + Dynamic_array<KEYUSE_EXT> *ext_keyuses_for_splitting; JOIN_TAB *sort_and_group_aggr_tab; @@ -1436,7 +1486,7 @@ public: table_count= 0; top_join_tab_count= 0; const_tables= 0; - const_table_map= 0; + const_table_map= found_const_table_map= 0; aggr_tables= 0; eliminated_tables= 0; join_list= 0; @@ -1465,7 +1515,10 @@ public: need_distinct= 0; skip_sort_order= 0; with_two_phase_optimization= 0; - is_for_splittable_grouping_derived= 0; + save_qep= 0; + spl_opt_info= 0; + ext_keyuses_for_splitting= 0; + spl_opt_info= 0; need_tmp= 0; hidden_group_fields= 0; /*safety*/ error= 0; @@ -1526,7 +1579,7 @@ public: int optimize(); int optimize_inner(); int optimize_stage2(); - void build_explain(); + bool build_explain(); int reinit(); int init_execution(); void exec(); @@ -1667,17 +1720,20 @@ public: { return (unit->item && unit->item->is_in_predicate()); } - void save_explain_data(Explain_query *output, bool can_overwrite, + bool save_explain_data(Explain_query *output, bool can_overwrite, bool need_tmp_table, bool need_order, bool distinct); int save_explain_data_intern(Explain_query *output, bool need_tmp_table, bool need_order, bool distinct, const char *message); JOIN_TAB *first_breadth_first_tab() { return join_tab; } bool check_two_phase_optimization(THD *thd); - bool check_for_splittable_grouping_derived(THD *thd); bool inject_cond_into_where(Item *injected_cond); - bool push_splitting_cond_into_derived(THD *thd, Item *cond); - bool improve_chosen_plan(THD *thd); + bool check_for_splittable_materialized(); + void add_keyuses_for_splitting(); + bool inject_best_splitting_cond(table_map remaining_tables); + bool fix_all_splittings_in_plan(); + + bool transform_in_predicates_into_in_subq(THD *thd); private: /** Create a temporary table to be used for processing DISTINCT/ORDER @@ -2294,6 +2350,11 @@ bool open_tmp_table(TABLE *table); void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps); double prev_record_reads(POSITION *positions, uint idx, table_map found_ref); void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist); +double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size); +double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size); +void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts); struct st_cond_statistic { diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index 540b5cb7f09..de4cd3522a5 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -15,6 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_class.h" #include "sql_list.h" #include "sql_sequence.h" @@ -848,7 +849,7 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) 0, 0)) DBUG_RETURN(TRUE); /* purecov: inspected */ - if (check_grant(thd, ALTER_ACL, first_table, FALSE, UINT_MAX, FALSE)) + if (check_grant(thd, ALTER_ACL, first_table, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ if (if_exists()) diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 5e826dff2e0..30b2e11139b 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -33,7 +33,7 @@ currently running transactions etc will not be disrupted. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_servers.h" #include "unireg.h" @@ -208,7 +208,7 @@ static bool servers_load(THD *thd, TABLE_LIST *tables) if (init_read_record(&read_record_info,thd,table=tables[0].table, NULL, NULL, 1,0, FALSE)) DBUG_RETURN(1); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { /* return_val is already TRUE, so no need to set */ if ((get_server_from_table_to_cache(table))) diff --git a/sql/sql_servers.h b/sql/sql_servers.h index d5668f0dfcb..1cb05416c63 100644 --- a/sql/sql_servers.h +++ b/sql/sql_servers.h @@ -16,7 +16,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* uint */ #include "slave.h" // for tables_ok(), rpl_filter class THD; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ae18e1cac04..850383d697c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -17,7 +17,7 @@ /* Function with list databases, tables or fields */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" // SHOW_MY_BOOL #include "sql_priv.h" #include "unireg.h" #include "sql_acl.h" // fill_schema_*_privileges @@ -1082,6 +1082,17 @@ find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db, if (is_show_command(thd)) #endif tl.sort(); +#ifndef DBUG_OFF + else + { + /* + sort_desc() is used to find easier unstable mtr tests that query + INFORMATION_SCHEMA.{SCHEMATA|TABLES} without a proper ORDER BY. + This can be removed in some release after 10.3 (e.g. in 10.4). + */ + tl.sort_desc(); + } +#endif DBUG_PRINT("info",("found: %zu files", files->elements())); my_dirend(dirp); @@ -1859,7 +1870,7 @@ static void append_create_options(THD *thd, String *packet, if (opt->quoted_value) append_unescaped(packet, opt->value.str, opt->value.length); else - packet->append(opt->value.str, opt->value.length); + packet->append(&opt->value); } if (in_comment) packet->append(STRING_WITH_LEN(" */")); @@ -2151,13 +2162,18 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, */ old_map= tmp_use_all_columns(table, table->read_set); + bool not_the_first_field= false; for (ptr=table->field ; (field= *ptr); ptr++) { + uint flags = field->flags; - if (ptr != table->field) + if (field->field_visibility > USER_DEFINED_INVISIBLE) + continue; + if (not_the_first_field) packet->append(STRING_WITH_LEN(",\n")); + not_the_first_field= true; packet->append(STRING_WITH_LEN(" ")); append_identifier(thd,packet,field->field_name.str, field->field_name.length); @@ -2210,6 +2226,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" NULL")); } + if (field->field_visibility == USER_DEFINED_INVISIBLE) + { + packet->append(STRING_WITH_LEN(" INVISIBLE")); + } def_value.set(def_value_buf, sizeof(def_value_buf), system_charset_info); if (get_field_default_value(thd, field, &def_value, 1)) { @@ -2251,11 +2271,13 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, for (uint i=0 ; i < share->keys ; i++,key_info++) { + if (key_info->flags & HA_INVISIBLE_KEY) + continue; KEY_PART_INFO *key_part= key_info->key_part; bool found_primary=0; packet->append(STRING_WITH_LEN(",\n ")); - if (i == primary_key && !strcmp(key_info->name, primary_key_name)) + if (i == primary_key && !strcmp(key_info->name.str, primary_key_name)) { found_primary=1; /* @@ -2274,7 +2296,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("KEY ")); if (!found_primary) - append_identifier(thd, packet, key_info->name, strlen(key_info->name)); + append_identifier(thd, packet, key_info->name.str, key_info->name.length); packet->append(STRING_WITH_LEN(" (")); @@ -3481,6 +3503,9 @@ const char* get_one_variable(THD *thd, case SHOW_MY_BOOL: end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); break; + case SHOW_UINT32_STATUS: + value= ((char *) status_var + (intptr) value); + /* fall through */ case SHOW_UINT: end= int10_to_str((long) *(uint*) value, buff, 10); break; @@ -3707,6 +3732,8 @@ uint calc_sum_of_all_status(STATUS_VAR *to) add_to_status(to, &tmp->status_var); to->local_memory_used+= tmp->status_var.local_memory_used; } + if (tmp->get_command() != COM_SLEEP) + to->threads_running++; } mysql_mutex_unlock(&LOCK_thread_count); @@ -5695,6 +5722,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, for (; (field= *ptr) ; ptr++) { + if(field->field_visibility > USER_DEFINED_INVISIBLE) + continue; uchar *pos; char tmp[MAX_FIELD_WIDTH]; String type(tmp,sizeof(tmp), system_charset_info); @@ -5752,11 +5781,11 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[16]->store((const char*) pos, strlen((const char*) pos), cs); + StringBuffer<256> buf; if (field->unireg_check == Field::NEXT_NUMBER) - table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs); + buf.set(STRING_WITH_LEN("auto_increment"),cs); if (print_on_update_clause(field, &type, true)) - table->field[17]->store(type.ptr(), type.length(), cs); - + buf.set(type.ptr(), type.length(),cs); if (field->vcol_info) { String gen_s(tmp,sizeof(tmp), system_charset_info); @@ -5767,13 +5796,20 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[20]->store(STRING_WITH_LEN("ALWAYS"), cs); if (field->vcol_info->stored_in_db) - table->field[17]->store(STRING_WITH_LEN("STORED GENERATED"), cs); + buf.set(STRING_WITH_LEN("STORED GENERATED"), cs); else - table->field[17]->store(STRING_WITH_LEN("VIRTUAL GENERATED"), cs); + buf.set(STRING_WITH_LEN("VIRTUAL GENERATED"), cs); } else table->field[20]->store(STRING_WITH_LEN("NEVER"), cs); - + /*Invisible can coexist with auto_increment and virtual */ + if (field->field_visibility == USER_DEFINED_INVISIBLE) + { + if (buf.length()) + buf.append(STRING_WITH_LEN(", ")); + buf.append(STRING_WITH_LEN("INVISIBLE"),cs); + } + table->field[17]->store(buf.ptr(), buf.length(), cs); table->field[19]->store(field->comment.str, field->comment.length, cs); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); @@ -6353,6 +6389,9 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, } for (uint i=0 ; i < show_table->s->keys ; i++,key_info++) { + if ((key_info->flags & HA_INVISIBLE_KEY) && + DBUG_EVALUATE_IF("test_invisible_index", 0, 1)) + continue; KEY_PART_INFO *key_part= key_info->key_part; LEX_CSTRING *str; LEX_CSTRING unknown= {STRING_WITH_LEN("?unknown field?") }; @@ -6365,7 +6404,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, table->field[3]->store((longlong) ((key_info->flags & HA_NOSAME) ? 0 : 1), TRUE); table->field[4]->store(db_name->str, db_name->length, cs); - table->field[5]->store(key_info->name, strlen(key_info->name), cs); + table->field[5]->store(key_info->name.str, key_info->name.length, cs); table->field[6]->store((longlong) (j+1), TRUE); str= (key_part->field ? &key_part->field->field_name : &unknown); @@ -6613,17 +6652,17 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, if (i != primary_key && !(key_info->flags & HA_NOSAME)) continue; - if (i == primary_key && !strcmp(key_info->name, primary_key_name)) + if (i == primary_key && !strcmp(key_info->name.str, primary_key_name)) { - if (store_constraints(thd, table, db_name, table_name, key_info->name, - strlen(key_info->name), + if (store_constraints(thd, table, db_name, table_name, + key_info->name.str, key_info->name.length, STRING_WITH_LEN("PRIMARY KEY"))) DBUG_RETURN(1); } else if (key_info->flags & HA_NOSAME) { - if (store_constraints(thd, table, db_name, table_name, key_info->name, - strlen(key_info->name), + if (store_constraints(thd, table, db_name, table_name, + key_info->name.str, key_info->name.length, STRING_WITH_LEN("UNIQUE"))) DBUG_RETURN(1); } @@ -6819,8 +6858,7 @@ static int get_schema_key_column_usage_record(THD *thd, f_idx++; restore_record(table, s->default_values); store_key_column_usage(table, db_name, table_name, - key_info->name, - strlen(key_info->name), + key_info->name.str, key_info->name.length, key_part->field->field_name.str, key_part->field->field_name.length, (longlong) f_idx); @@ -7219,7 +7257,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, tmp_res.length(0); if (part_elem->has_null_value) { - tmp_str.append("NULL"); + tmp_str.append(STRING_WITH_LEN("NULL")); if (num_items > 0) tmp_str.append(","); } @@ -7228,7 +7266,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, if (part_info->column_list) { if (part_info->part_field_list.elements > 1U) - tmp_str.append("("); + tmp_str.append(STRING_WITH_LEN("(")); if (get_partition_column_description(thd, part_info, list_value, diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index 6a57b8fc9ce..1e1c2c1d624 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sp_head.h" #include "sp_pcontext.h" diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 6c97ad7e9ab..d57239671a8 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -17,7 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "my_base.h" /* ha_rows */ -#include "my_sys.h" /* qsort2_cmp */ +#include <my_sys.h> /* qsort2_cmp */ #include "queues.h" typedef struct st_buffpek BUFFPEK; diff --git a/sql/sql_state.c b/sql/sql_state.c index 2bfd61d6696..046868a78a7 100644 --- a/sql/sql_state.c +++ b/sql/sql_state.c @@ -16,7 +16,7 @@ /* Functions to map mysqld errno to sql_state */ -#include <my_global.h> +#include "mariadb.h" #include <mysqld_error.h> #include <my_base.h> diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index e1465d47f72..2cb47634fe4 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -23,7 +23,7 @@ @{ */ -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" #include "key.h" #include "sql_statistics.h" @@ -666,16 +666,22 @@ public: { if (find_stat()) { + bool res; store_record_for_update(); store_stat_fields(); - return update_record(); + res= update_record(); + DBUG_ASSERT(res == 0); + return res; } else { int err; store_stat_fields(); if ((err= stat_file->ha_write_row(record[0]))) + { + DBUG_ASSERT(0); return TRUE; + } /* Make change permanent and avoid 'table is marked as crashed' errors */ stat_file->extra(HA_EXTRA_FLUSH); } @@ -1319,8 +1325,8 @@ public: void set_index_prefix_key_fields(KEY *index_info) { set_full_table_name(); - const char *index_name= index_info->name; - index_name_field->store(index_name, strlen(index_name), + const char *index_name= index_info->name.str; + index_name_field->store(index_name, index_info->name.length, system_charset_info); table_key_info= index_info; } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index a24eaaa6fde..d0d0e35000d 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -20,8 +20,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> -#include <my_sys.h> +#include "mariadb.h" #include <m_string.h> #include <m_ctype.h> #include <mysql_com.h> diff --git a/sql/sql_string.h b/sql/sql_string.h index 3908cdff252..41d31dc33c2 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -25,7 +25,7 @@ #endif #include "m_ctype.h" /* my_charset_bin */ -#include "my_sys.h" /* alloc_root, my_free, my_realloc */ +#include <my_sys.h> /* alloc_root, my_free, my_realloc */ #include "m_string.h" /* TRASH */ class String; @@ -181,6 +181,8 @@ public: } static void *operator new(size_t size, MEM_ROOT *mem_root) throw () { return (void*) alloc_root(mem_root, (uint) size); } + static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } static void operator delete(void *ptr_arg, size_t size) { (void) ptr_arg; @@ -189,6 +191,10 @@ public: } static void operator delete(void *, MEM_ROOT *) { /* never called */ } + static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); } + static void operator delete[](void *ptr, MEM_ROOT *mem_root) + { /* never called */ } + ~String() { free(); } /* Mark variable thread specific it it's not allocated already */ @@ -589,6 +595,10 @@ public: { qs_append(str, (uint32)strlen(str)); } + void qs_append(const LEX_CSTRING *str) + { + qs_append(str->str, str->length); + } void qs_append(const char *str, uint32 len); void qs_append_hex(const char *str, uint32 len); void qs_append(double d); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 944f4c2a0af..f7a988bb398 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -18,7 +18,7 @@ /* drop and alter of tables */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "debug_sync.h" @@ -62,12 +62,16 @@ const char *primary_key_name="PRIMARY"; -static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); +static int check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start, KEY *end); static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, List<Virtual_column_info> *vcol, uint *nr); +static const +char * make_unique_invisible_field_name(THD *thd, const char *field_name, + List<Create_field> *fields); + static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, List<Create_field> &create, bool ignore, uint order_num, ORDER *order, @@ -438,6 +442,13 @@ uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length) } +static bool check_if_frm_exists(char *path, const char *db, const char *table) +{ + fn_format(path, table, db, reg_ext, MYF(0)); + return !access(path, F_OK); +} + + /* Translate a table name to a file name (WL #1324). @@ -528,13 +539,18 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db, DBUG_PRINT("enter", ("db: '%s' table_name: '%s' ext: '%s' flags: %x", db, table_name, ext, flags)); + (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff)); + + /* Check if this is a temporary table name. Allow it if a corresponding .frm file exists */ + if (is_prefix(table_name, tmp_file_prefix) && strlen(table_name) < NAME_CHAR_LEN && + check_if_frm_exists(tbbuff, dbbuff, table_name)) + flags|= FN_IS_TMP; + if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP strmake(tbbuff, table_name, sizeof(tbbuff)-1); else (void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff)); - (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff)); - char *end = buff + bufflen; /* Don't add FN_ROOTDIR if mysql_data_home already includes it */ char *pos = strnmov(buff, mysql_data_home, bufflen); @@ -2796,9 +2812,9 @@ static int sort_keys(KEY *a, KEY *b) /* Sort NOT NULL keys before other keys */ return (a_flags & HA_NULL_PART_KEY) ? 1 : -1; } - if (a->name == primary_key_name) + if (a->name.str == primary_key_name) return -1; - if (b->name == primary_key_name) + if (b->name.str == primary_key_name) return 1; /* Sort keys don't containing partial segments before others */ if ((a_flags ^ b_flags) & HA_KEY_HAS_PART_KEY_SEG) @@ -3087,34 +3103,29 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, */ List_iterator_fast<Key_part_spec> k_column_iterator(k->columns); - - bool all_columns_are_identical= true; - + uint i; key_column_iterator.rewind(); - for (uint i= 0; i < key->columns.elements; ++i) + for (i= 0; i < key->columns.elements; ++i) { Key_part_spec *c1= key_column_iterator++; Key_part_spec *c2= k_column_iterator++; DBUG_ASSERT(c1 && c2); - if (my_strcasecmp(system_charset_info, - c1->field_name.str, c2->field_name.str) || + if (lex_string_cmp(system_charset_info, + &c1->field_name, &c2->field_name) || (c1->length != c2->length)) - { - all_columns_are_identical= false; break; - } } // Report a warning if we have two identical keys. - if (all_columns_are_identical) + if (i == key->columns.elements) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX), - key_info->name); + key_info->name.str); break; } } @@ -3256,8 +3267,70 @@ bool Column_definition::prepare_stage1_check_typelib_default() } return false; } +/* + This function adds a invisible field to field_list + SYNOPSIS + mysql_add_invisible_field() + thd Thread Object + field_list list of all table fields + field_name name/prefix of invisible field + ( Prefix in the case when it is + *COMPLETELY_INVISIBLE* + and given name is duplicate) + type_handler field data type + field_visibility + default value + RETURN VALUE + Create_field pointer +*/ +int mysql_add_invisible_field(THD *thd, List<Create_field> * field_list, + const char *field_name, Type_handler *type_handler, + field_visible_type field_visibility, Item* default_value) +{ + Create_field *fld= new(thd->mem_root)Create_field(); + const char *new_name= NULL; + /* Get unique field name if field_visibility == COMPLETELY_INVISIBLE */ + if (field_visibility == COMPLETELY_INVISIBLE) + { + if ((new_name= make_unique_invisible_field_name(thd, field_name, + field_list))) + { + fld->field_name.str= new_name; + fld->field_name.length= strlen(new_name); + } + else + return 1; //Should not happen + } + else + { + fld->field_name.str= thd->strmake(field_name, strlen(field_name)); + fld->field_name.length= strlen(field_name); + } + fld->set_handler(type_handler); + fld->field_visibility= field_visibility; + if (default_value) + { + Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); + v->expr= default_value; + v->utf8= 0; + fld->default_value= v; + } + field_list->push_front(fld, thd->mem_root); + return 0; +} - +Key * +mysql_add_invisible_index(THD *thd, List<Key> *key_list, + LEX_CSTRING* field_name, enum Key::Keytype type) +{ + Key *key= NULL; + key= new (thd->mem_root) Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, + false, DDL_options(DDL_options::OPT_NONE)); + key->columns.push_back(new(thd->mem_root) Key_part_spec(field_name, 0), + thd->mem_root); + key_list->push_back(key, thd->mem_root); + return key; +} /* Preparation for table creation @@ -3305,6 +3378,23 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); + DBUG_EXECUTE_IF("test_pseudo_invisible",{ + mysql_add_invisible_field(thd, &alter_info->create_list, + "invisible", &type_handler_long, SYSTEM_INVISIBLE, + new (thd->mem_root)Item_int(thd, 9)); + }); + DBUG_EXECUTE_IF("test_completely_invisible",{ + mysql_add_invisible_field(thd, &alter_info->create_list, + "invisible", &type_handler_long, COMPLETELY_INVISIBLE, + new (thd->mem_root)Item_int(thd, 9)); + }); + DBUG_EXECUTE_IF("test_invisible_index",{ + LEX_CSTRING temp; + temp.str= "invisible"; + temp.length= strlen("invisible"); + mysql_add_invisible_index(thd, &alter_info->key_list + , &temp, Key::MULTIPLE); + }); LEX_CSTRING* connect_string = &create_info->connect_string; if (connect_string->length != 0 && connect_string->length > CONNECT_STRING_MAXLEN && @@ -3375,9 +3465,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, /* Check if we have used the same field name before */ for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++) { - if (my_strcasecmp(system_charset_info, - sql_field->field_name.str, - dup_field->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->field_name, + &dup_field->field_name) == 0) { /* If this was a CREATE ... SELECT statement, accept a field @@ -3433,7 +3523,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, while ((sql_field=it++)) { DBUG_ASSERT(sql_field->charset != 0); - if (sql_field->prepare_stage2(file, file->ha_table_flags())) DBUG_RETURN(TRUE); if (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR) @@ -3453,8 +3542,18 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (sql_field->stored_in_db()) record_offset+= sql_field->pack_length; + if (sql_field->field_visibility == USER_DEFINED_INVISIBLE && + sql_field->flags & NOT_NULL_FLAG && + sql_field->flags & NO_DEFAULT_VALUE_FLAG) + { + my_error(ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT, MYF(0), + sql_field->field_name.str); + DBUG_RETURN(TRUE); + } } - /* Update virtual fields' offset*/ + /* Update virtual fields' offset and give error if + All fields are invisible */ + bool is_all_invisible= true; it.rewind(); while ((sql_field=it++)) { @@ -3463,6 +3562,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->offset= record_offset; record_offset+= sql_field->pack_length; } + if (sql_field->field_visibility == NOT_INVISIBLE) + is_all_invisible= false; + } + if (is_all_invisible) + { + my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0)); + DBUG_RETURN(TRUE); } if (auto_increment > 1) { @@ -3709,24 +3815,42 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, it.rewind(); field=0; while ((sql_field=it++) && - my_strcasecmp(system_charset_info, - column->field_name.str, - sql_field->field_name.str)) + lex_string_cmp(system_charset_info, + &column->field_name, + &sql_field->field_name)) field++; + /* + Either field is not present or field visibility is > + USER_DEFINED_INVISIBLE + */ if (!sql_field) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } + if (sql_field->field_visibility > USER_DEFINED_INVISIBLE && + !key->invisible && DBUG_EVALUATE_IF("test_invisible_index", 0, 1)) + { + my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); + DBUG_RETURN(TRUE); + } while ((dup_column= cols2++) != column) { - if (!my_strcasecmp(system_charset_info, - column->field_name.str, dup_column->field_name.str)) + if (!lex_string_cmp(system_charset_info, + &column->field_name, &dup_column->field_name)) { my_error(ER_DUP_FIELDNAME, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } } + + if (sql_field->compression_method()) + { + my_error(ER_COMPRESSED_COLUMN_USED_AS_KEY, MYF(0), + column->field_name.str); + DBUG_RETURN(TRUE); + } + cols2.rewind(); if (key->type == Key::FULLTEXT) { @@ -3970,12 +4094,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_DUP_KEYNAME, MYF(0), key_name); DBUG_RETURN(TRUE); } - key_info->name=(char*) key_name; + key_info->name.str= (char*) key_name; + key_info->name.length= strlen(key_name); } } - if (!key_info->name || check_column_name(key_info->name)) + if (!key_info->name.str || check_column_name(key_info->name.str)) { - my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name); + my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name.str); DBUG_RETURN(TRUE); } if (key->type == Key::UNIQUE && !(key_info->flags & HA_NULL_PART_KEY)) @@ -3990,7 +4115,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (validate_comment_length(thd, &key->key_create_info.comment, INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT, - key_info->name)) + key_info->name.str)) DBUG_RETURN(TRUE); key_info->comment.length= key->key_create_info.comment.length; @@ -4089,9 +4214,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Virtual_column_info *dup_check; while ((dup_check= dup_it++) && dup_check != check) { - if (check->name.length == dup_check->name.length && - my_strcasecmp(system_charset_info, - check->name.str, dup_check->name.str) == 0) + if (!lex_string_cmp(system_charset_info, + &check->name, &dup_check->name)) { my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str); DBUG_RETURN(TRUE); @@ -4770,7 +4894,8 @@ int create_table_impl(THD *thd, if (!frm_only && create_info->tmp_table()) { TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm, - path, db, table_name, true); + path, db, table_name, true, + false); if (!table) { @@ -5017,17 +5142,36 @@ err: /* ** Give the key name after the first field with an optional '_#' after + @returns + 0 if keyname does not exists + [1..) index + 1 of duplicate key name **/ -static bool +static int check_if_keyname_exists(const char *name, KEY *start, KEY *end) { - for (KEY *key=start ; key != end ; key++) - if (!my_strcasecmp(system_charset_info,name,key->name)) - return 1; + uint i= 1; + for (KEY *key=start; key != end ; key++, i++) + if (!my_strcasecmp(system_charset_info, name, key->name.str)) + return i; return 0; } +/** + Returns 1 if field name exists otherwise 0 +*/ +static bool +check_if_field_name_exists(const char *name, List<Create_field> * fields) +{ + Create_field *fld; + List_iterator<Create_field>it(*fields); + while ((fld = it++)) + { + if (!my_strcasecmp(system_charset_info, fld->field_name.str, name)) + return 1; + } + return 0; +} static char * make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) @@ -5085,6 +5229,33 @@ static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, } } +/** + COMPLETELY_INVISIBLE are internally created. They are completely invisible + to Alter command (Opposite of SYSTEM_INVISIBLE which throws an + error when same name column is added by Alter). So in the case of when + user added a same column name as of COMPLETELY_INVISIBLE , we change + COMPLETELY_INVISIBLE column name. +*/ +static const +char * make_unique_invisible_field_name(THD *thd, const char *field_name, + List<Create_field> *fields) +{ + if (!check_if_field_name_exists(field_name, fields)) + return field_name; + char buff[MAX_FIELD_NAME], *buff_end; + buff_end= strmake_buf(buff, field_name); + if (buff_end - buff < 5) + return NULL; // Should not happen + + for (uint i=1 ; i < 10000; i++) + { + char *real_end= int10_to_str(i, buff_end, 10); + if (check_if_field_name_exists(buff, fields)) + continue; + return (const char *)thd->strmake(buff, real_end - buff); + } + return NULL; //Should not happen +} /**************************************************************************** ** Alter a table definition @@ -5686,8 +5857,9 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) */ for (f_ptr=table->field; *f_ptr; f_ptr++) { - if (my_strcasecmp(system_charset_info, - sql_field->field_name.str, (*f_ptr)->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->field_name, + &(*f_ptr)->field_name) == 0) goto drop_create_field; } { @@ -5699,8 +5871,9 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) Create_field *chk_field; while ((chk_field= chk_it++) && chk_field != sql_field) { - if (my_strcasecmp(system_charset_info, - sql_field->field_name.str, chk_field->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->field_name, + &chk_field->field_name) == 0) goto drop_create_field; } } @@ -5735,8 +5908,9 @@ drop_create_field: */ for (f_ptr=table->field; *f_ptr; f_ptr++) { - if (my_strcasecmp(system_charset_info, - sql_field->change.str, (*f_ptr)->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->change, + &(*f_ptr)->field_name) == 0) { break; } @@ -5805,7 +5979,8 @@ drop_create_field: for (n_key=0; n_key < table->s->keys; n_key++) { if (my_strcasecmp(system_charset_info, - drop->name, table->key_info[n_key].name) == 0) + drop->name, + table->key_info[n_key].name.str) == 0) { remove_drop= FALSE; break; @@ -5903,7 +6078,7 @@ drop_create_field: for (n_key=0; n_key < table->s->keys; n_key++) { if (my_strcasecmp(system_charset_info, - keyname, table->key_info[n_key].name) == 0) + keyname, table->key_info[n_key].name.str) == 0) { goto remove_key; } @@ -6054,8 +6229,8 @@ remove_key: { Virtual_column_info *dup= table->check_constraints[c]; if (dup->name.length == check->name.length && - my_strcasecmp(system_charset_info, - check->name.str, dup->name.str) == 0) + lex_string_cmp(system_charset_info, + &check->name, &dup->name) == 0) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_CONSTRAINT_NAME, ER_THD(thd, ER_DUP_CONSTRAINT_NAME), @@ -6362,8 +6537,8 @@ static bool fill_alter_inplace_info(THD *thd, } /* Check if field was renamed */ - if (my_strcasecmp(system_charset_info, field->field_name.str, - new_field->field_name.str)) + if (lex_string_cmp(system_charset_info, &field->field_name, + &new_field->field_name)) { field->flags|= FIELD_IS_RENAMED; ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_NAME; @@ -6494,7 +6669,8 @@ static bool fill_alter_inplace_info(THD *thd, new_key < new_key_end; new_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (new_key >= new_key_end) @@ -6503,7 +6679,7 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->index_drop_buffer [ha_alter_info->index_drop_count++]= table_key; - DBUG_PRINT("info", ("index dropped: '%s'", table_key->name)); + DBUG_PRINT("info", ("index dropped: '%s'", table_key->name.str)); continue; } @@ -6571,7 +6747,7 @@ static bool fill_alter_inplace_info(THD *thd, [ha_alter_info->index_add_count++]= (uint)(new_key - ha_alter_info->key_info_buffer); /* Mark all old fields which are used in newly created index. */ - DBUG_PRINT("info", ("index changed: '%s'", table_key->name)); + DBUG_PRINT("info", ("index changed: '%s'", table_key->name.str)); } /*end of for (; table_key < table_key_end;) */ @@ -6585,7 +6761,8 @@ static bool fill_alter_inplace_info(THD *thd, /* Search an old key with the same name. */ for (table_key= table->key_info; table_key < table_key_end; table_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (table_key >= table_key_end) @@ -6594,7 +6771,7 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->index_add_buffer [ha_alter_info->index_add_count++]= (uint)(new_key - ha_alter_info->key_info_buffer); - DBUG_PRINT("info", ("index added: '%s'", new_key->name)); + DBUG_PRINT("info", ("index added: '%s'", new_key->name.str)); } else ha_alter_info->create_info->indexes_option_struct[table_key - table->key_info]= @@ -6666,7 +6843,8 @@ static bool fill_alter_inplace_info(THD *thd, if (new_key->flags & HA_NOSAME) { - bool is_pk= !my_strcasecmp(system_charset_info, new_key->name, primary_key_name); + bool is_pk= !my_strcasecmp(system_charset_info, + new_key->name.str, primary_key_name); if ((!(new_key->flags & HA_KEY_HAS_PART_KEY_SEG) && !(new_key->flags & HA_NULL_PART_KEY)) || @@ -6818,9 +6996,9 @@ bool mysql_compare_tables(TABLE *table, create_info->table_options|= HA_OPTION_PACK_RECORD; /* Check if field was renamed */ - if (my_strcasecmp(system_charset_info, - field->field_name.str, - tmp_new_field->field_name.str)) + if (lex_string_cmp(system_charset_info, + &field->field_name, + &tmp_new_field->field_name)) DBUG_RETURN(false); /* Evaluate changes bitmap and send to check_if_incompatible_data() */ @@ -6847,7 +7025,8 @@ bool mysql_compare_tables(TABLE *table, /* Search a key with the same name. */ for (new_key= key_info_buffer; new_key < new_key_end; new_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (new_key >= new_key_end) @@ -6886,7 +7065,8 @@ bool mysql_compare_tables(TABLE *table, /* Search a key with the same name. */ for (table_key= table->key_info; table_key < table_key_end; table_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (table_key >= table_key_end) @@ -7504,6 +7684,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, bitmap_clear_all(&table->tmp_set); for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++) { + if (field->field_visibility == COMPLETELY_INVISIBLE) + continue; Alter_drop *drop; if (field->type() == MYSQL_TYPE_VARCHAR) create_info->varchar= TRUE; @@ -7515,7 +7697,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, !my_strcasecmp(system_charset_info,field->field_name.str, drop->name)) break; } - if (drop) + if (drop && field->field_visibility < SYSTEM_INVISIBLE) { /* Reset auto_increment value if it was dropped */ if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER && @@ -7536,11 +7718,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, while ((def=def_it++)) { if (def->change.str && - !my_strcasecmp(system_charset_info,field->field_name.str, - def->change.str)) + !lex_string_cmp(system_charset_info, &field->field_name, + &def->change)) break; } - if (def) + if (def && field->field_visibility < SYSTEM_INVISIBLE) { // Field is changed def->field=field; /* @@ -7595,12 +7777,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, def_it.rewind(); while ((def=def_it++)) // Add new columns { + Create_field *find; if (def->change.str && ! def->field) { /* Check if there is modify for newly added field. */ - Create_field *find; find_it.rewind(); while((find=find_it++)) { @@ -7640,7 +7822,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, new_create_list.push_back(def, thd->mem_root); else { - Create_field *find; if (def->change.str) { find_it.rewind(); @@ -7671,8 +7852,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, find_it.rewind(); while ((find=find_it++)) { - if (!my_strcasecmp(system_charset_info, def->after.str, - find->field_name.str)) + if (!lex_string_cmp(system_charset_info, &def->after, + &find->field_name)) break; } if (!find) @@ -7727,10 +7908,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Collect all keys which isn't in drop list. Add only those for which some fields exists. */ - + for (uint i=0 ; i < table->s->keys ; i++,key_info++) { - const char *key_name= key_info->name; + if (key_info->flags & HA_INVISIBLE_KEY) + continue; + const char *key_name= key_info->name.str; Alter_drop *drop; drop_it.rewind(); while ((drop=drop_it++)) @@ -8086,8 +8269,8 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info, { Field *old_field= new_field->field; - if (my_strcasecmp(system_charset_info, old_field->field_name.str, - new_field->field_name.str)) + if (lex_string_cmp(system_charset_info, &old_field->field_name, + &new_field->field_name)) { /* Copy algorithm doesn't support proper renaming of columns in @@ -8200,10 +8383,10 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table, if ((drop->type == Alter_drop::FOREIGN_KEY) && (my_strcasecmp(system_charset_info, f_key->foreign_id->str, drop->name) == 0) && - (my_strcasecmp(table_alias_charset, f_key->foreign_db->str, - table->s->db.str) == 0) && - (my_strcasecmp(table_alias_charset, f_key->foreign_table->str, - table->s->table_name.str) == 0)) + (lex_string_cmp(table_alias_charset, f_key->foreign_db, + &table->s->db) == 0) && + (lex_string_cmp(table_alias_charset, f_key->foreign_table, + &table->s->table_name) == 0)) fk_parent_key_it.remove(); } } @@ -9115,7 +9298,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - false))) + false, true))) goto err_new_table_cleanup; /* Set markers for fields in TABLE object for altered table. */ @@ -9130,10 +9313,12 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, &altered_table->s->all_set); restore_record(altered_table, s->default_values); // Create empty record /* Check that we can call default functions with default field values */ + thd->count_cuted_fields= CHECK_FIELD_EXPRESSION; altered_table->reset_default_fields(); if (altered_table->default_field && altered_table->update_default_fields(0, 1)) goto err_new_table_cleanup; + thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Ask storage engine whether to use copy or in-place enum_alter_inplace_result inplace_supported= @@ -9258,7 +9443,8 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, } // It's now safe to take the table level lock. - if (lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) + if (lock_tables(thd, table_list, alter_ctx.tables_opened, + MYSQL_LOCK_USE_MALLOC)) goto err_new_table_cleanup; if (ha_create_table(thd, alter_ctx.get_tmp_path(), @@ -9275,7 +9461,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - true); + true, true); if (!tmp_table) { goto err_new_table_cleanup; @@ -9309,7 +9495,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - true); + true, true); } if (!new_table) goto err_new_table_cleanup; @@ -9683,7 +9869,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (mysql_trans_prepare_alter_copy_data(thd)) DBUG_RETURN(-1); - if (!(copy= new Copy_field[to->s->fields])) + if (!(copy= new (thd->mem_root) Copy_field[to->s->fields])) DBUG_RETURN(-1); /* purecov: inspected */ /* We need external lock before we can disable/enable keys */ @@ -9795,7 +9981,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, thd->progress.max_counter= from->file->records(); time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10; - while (!(error=info.read_record(&info))) + while (!(error= info.read_record())) { if (thd->killed) { @@ -9964,8 +10150,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy) HA_CREATE_INFO create_info; Alter_info alter_info; TABLE_LIST *next_table= table_list->next_global; - DBUG_ENTER("mysql_recreate_table"); + /* Set lock type which is appropriate for ALTER TABLE. */ table_list->lock_type= TL_READ_NO_INSERT; /* Same applies to MDL request. */ @@ -9983,6 +10169,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy) if (table_copy) alter_info.requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY; + thd->prepare_logs_for_admin_command(); + bool res= mysql_alter_table(thd, NullS, NullS, &create_info, table_list, &alter_info, 0, (ORDER *) 0, 0); diff --git a/sql/sql_table.h b/sql/sql_table.h index 2e080462deb..e45ddef6548 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -17,8 +17,7 @@ #ifndef SQL_TABLE_INCLUDED #define SQL_TABLE_INCLUDED -#include "my_global.h" /* my_bool */ -#include "my_sys.h" // pthread_mutex_t +#include <my_sys.h> // pthread_mutex_t #include "m_string.h" // LEX_CUSTRING class Alter_info; diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc index 8b9e14e5a18..93a3007d1ea 100644 --- a/sql/sql_tablespace.cc +++ b/sql/sql_tablespace.cc @@ -15,7 +15,7 @@ /* drop and alter of tablespaces */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_tablespace.h" diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 56d10ddaa13..3d43c35177d 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -16,7 +16,7 @@ /* Write some debug info */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_test.h" diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 24aa7b1b8a6..309ede45ecc 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -17,7 +17,7 @@ /* Functions to handle date and time */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_time.h" #include "tztime.h" // struct Time_zone diff --git a/sql/sql_time.h b/sql/sql_time.h index 4e8f280514f..1832e4501ed 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -17,7 +17,6 @@ #ifndef SQL_TIME_INCLUDED #define SQL_TIME_INCLUDED -#include "my_global.h" /* ulong */ #include "my_time.h" #include "mysql_time.h" /* timestamp_type */ #include "sql_error.h" /* Sql_condition */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index e7e7dd9589d..be85fb892b0 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -16,7 +16,7 @@ #define MYSQL_LEX 1 -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sp_head.h" @@ -693,7 +693,7 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, /* Create statement for storing trigger (without trigger order) */ if (lex->trg_chistics.ordering_clause == TRG_ORDER_NONE) - trigger_def->append(stmt_definition.str, stmt_definition.length); + trigger_def->append(&stmt_definition); else { /* Copy data before FOLLOWS/PRECEDES trigger_name */ @@ -755,8 +755,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, DBUG_RETURN(true); /* Trigger must be in the same schema as target table. */ - if (my_strcasecmp(table_alias_charset, table->s->db.str, - lex->spname->m_db.str)) + if (lex_string_cmp(table_alias_charset, &table->s->db, + &lex->spname->m_db)) { my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); DBUG_RETURN(true); @@ -928,8 +928,8 @@ err_without_cleanup: if (trigger_dropped) { String drop_trg_query; - drop_trg_query.append("DROP TRIGGER /* generated by failed CREATE TRIGGER */ "); - drop_trg_query.append(lex->spname->m_name.str); + drop_trg_query.append(STRING_WITH_LEN("DROP TRIGGER /* generated by failed CREATE TRIGGER */ ")); + drop_trg_query.append(&lex->spname->m_name); /* We dropped an existing trigger and was not able to recreate it because of an internal error. Ensure it's also dropped on the slave. @@ -1089,8 +1089,8 @@ Trigger *Table_triggers_list::find_trigger(const LEX_CSTRING *name, (trigger= *parent); parent= &trigger->next) { - if (my_strcasecmp(table_alias_charset, - trigger->name.str, name->str) == 0) + if (lex_string_cmp(table_alias_charset, + &trigger->name, name) == 0) { if (remove_from_list) { @@ -1638,8 +1638,8 @@ void Table_triggers_list::add_trigger(trg_event_type event, for ( ; *parent ; parent= &(*parent)->next, position++) { if (ordering_clause != TRG_ORDER_NONE && - !my_strcasecmp(table_alias_charset, anchor_trigger_name->str, - (*parent)->name.str)) + !lex_string_cmp(table_alias_charset, anchor_trigger_name, + &(*parent)->name)) { if (ordering_clause == TRG_ORDER_FOLLOWS) { diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index d4859efc7af..1d6edbc5fc9 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "debug_sync.h" // DEBUG_SYNC #include "table.h" // TABLE, FOREIGN_KEY_INFO #include "sql_class.h" // THD @@ -150,18 +151,18 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table) /* Loop over the set of foreign keys for which this table is a parent. */ while ((fk_info= it++)) { - DBUG_ASSERT(!my_strcasecmp(system_charset_info, - fk_info->referenced_db->str, - table->s->db.str)); - - DBUG_ASSERT(!my_strcasecmp(system_charset_info, - fk_info->referenced_table->str, - table->s->table_name.str)); - - if (my_strcasecmp(system_charset_info, fk_info->foreign_db->str, - table->s->db.str) || - my_strcasecmp(system_charset_info, fk_info->foreign_table->str, - table->s->table_name.str)) + DBUG_ASSERT(!lex_string_cmp(system_charset_info, + fk_info->referenced_db, + &table->s->db)); + + DBUG_ASSERT(!lex_string_cmp(system_charset_info, + fk_info->referenced_table, + &table->s->table_name)); + + if (lex_string_cmp(system_charset_info, fk_info->foreign_db, + &table->s->db) || + lex_string_cmp(system_charset_info, fk_info->foreign_table, + &table->s->table_name)) break; } diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc new file mode 100644 index 00000000000..79e894f5a7f --- /dev/null +++ b/sql/sql_tvc.cc @@ -0,0 +1,859 @@ +/* Copyright (c) 2017, 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mariadb.h" +#include "sql_list.h" +#include "sql_tvc.h" +#include "sql_class.h" +#include "opt_range.h" +#include "sql_select.h" +#include "sql_explain.h" +#include "sql_parse.h" +#include "sql_cte.h" + +/** + @brief + Fix fields for TVC values + + @param + @param thd The context of the statement + @param li The iterator on the list of lists + + @details + Call fix_fields procedure for TVC values. + + @retval + true if an error was reported + false otherwise +*/ + +bool fix_fields_for_tvc(THD *thd, List_iterator_fast<List_item> &li) +{ + DBUG_ENTER("fix_fields_for_tvc"); + List_item *lst; + li.rewind(); + + while ((lst= li++)) + { + List_iterator_fast<Item> it(*lst); + Item *item; + + while ((item= it++)) + { + if (item->fix_fields(thd, 0)) + DBUG_RETURN(true); + } + } + DBUG_RETURN(false); +} + + +/** + @brief + Defines types of matrix columns elements where matrix rows are defined by + some lists of values. + + @param + @param thd The context of the statement + @param li The iterator on the list of lists + @param holders The structure where types of matrix columns are stored + @param first_list_el_count Count of the list values. It should be the same + for each list of lists elements. It contains + number of elements of the first list from list of + lists. + + @details + For each list list_a from list of lists the procedure gets its elements + types and aggregates them with the previous ones stored in holders. If + list_a is the first one in the list of lists its elements types are put in + holders. The errors can be reported when count of list_a elements is + different from the first_list_el_count. Also error can be reported whe + n aggregation can't be made. + + @retval + true if an error was reported + false otherwise +*/ + +bool join_type_handlers_for_tvc(THD *thd, List_iterator_fast<List_item> &li, + Type_holder *holders, uint first_list_el_count) +{ + DBUG_ENTER("join_type_handlers_for_tvc"); + List_item *lst; + li.rewind(); + bool first= true; + + while ((lst= li++)) + { + List_iterator_fast<Item> it(*lst); + Item *item; + + if (first_list_el_count != lst->elements) + { + my_message(ER_WRONG_NUMBER_OF_VALUES_IN_TVC, + ER_THD(thd, ER_WRONG_NUMBER_OF_VALUES_IN_TVC), + MYF(0)); + DBUG_RETURN(true); + } + for (uint pos= 0; (item=it++); pos++) + { + const Type_handler *item_type_handler= item->real_type_handler(); + if (first) + holders[pos].set_handler(item_type_handler); + else if (holders[pos].aggregate_for_result(item_type_handler)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + holders[pos].type_handler()->name().ptr(), + item_type_handler->name().ptr(), + "TABLE VALUE CONSTRUCTOR"); + DBUG_RETURN(true); + } + } + first= false; + } + DBUG_RETURN(false); +} + + +/** + @brief + Define attributes of matrix columns elements where matrix rows are defined + by some lists of values. + + @param + @param thd The context of the statement + @param li The iterator on the list of lists + @param holders The structure where names of matrix columns are stored + @param count_of_lists Count of list of lists elements + @param first_list_el_count Count of the list values. It should be the same + for each list of lists elements. It contains + number of elements of the first list from list + of lists. + + @details + For each list list_a from list of lists the procedure gets its elements + attributes and aggregates them with the previous ones stored in holders. + The errors can be reported when aggregation can't be made. + + @retval + true if an error was reported + false otherwise +*/ + +bool get_type_attributes_for_tvc(THD *thd, + List_iterator_fast<List_item> &li, + Type_holder *holders, uint count_of_lists, + uint first_list_el_count) +{ + DBUG_ENTER("get_type_attributes_for_tvc"); + List_item *lst; + li.rewind(); + + for (uint pos= 0; pos < first_list_el_count; pos++) + { + if (holders[pos].alloc_arguments(thd, count_of_lists)) + DBUG_RETURN(true); + } + + while ((lst= li++)) + { + List_iterator_fast<Item> it(*lst); + Item *item; + for (uint holder_pos= 0 ; (item= it++); holder_pos++) + { + DBUG_ASSERT(item->fixed); + holders[holder_pos].add_argument(item); + } + } + + for (uint pos= 0; pos < first_list_el_count; pos++) + { + if (holders[pos].aggregate_attributes(thd)) + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +/** + @brief + Prepare of TVC + + @param + @param thd The context of the statement + @param sl The select where this TVC is defined + @param tmp_result Structure that contains the information + about where to send the result of the query + @param unit_arg The union where sl is defined + + @details + Gets types and attributes of values of this TVC that will be used + for temporary table creation for this TVC. It creates Item_type_holders + for each element of the first list from list of lists (VALUES from tvc), + using its elements name, defined type and attribute. + + @retval + true if an error was reported + false otherwise +*/ + +bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl, + select_result *tmp_result, + st_select_lex_unit *unit_arg) +{ + DBUG_ENTER("table_value_constr::prepare"); + select_lex->in_tvc= true; + List_iterator_fast<List_item> li(lists_of_values); + + List_item *first_elem= li++; + uint cnt= first_elem->elements; + Type_holder *holders; + + if (fix_fields_for_tvc(thd, li)) + DBUG_RETURN(true); + + if (!(holders= new (thd->mem_root) + Type_holder[cnt]) || + join_type_handlers_for_tvc(thd, li, holders, + cnt) || + get_type_attributes_for_tvc(thd, li, holders, + lists_of_values.elements, cnt)) + DBUG_RETURN(true); + + List_iterator_fast<Item> it(*first_elem); + Item *item; + + sl->item_list.empty(); + for (uint pos= 0; (item= it++); pos++) + { + /* Error's in 'new' will be detected after loop */ + Item_type_holder *new_holder= new (thd->mem_root) + Item_type_holder(thd, + &item->name, + holders[pos].type_handler(), + &holders[pos]/*Type_all_attributes*/, + holders[pos].get_maybe_null()); + new_holder->fix_fields(thd, 0); + sl->item_list.push_back(new_holder); + } + + if (thd->is_fatal_error) + DBUG_RETURN(true); // out of memory + + result= tmp_result; + + if (result && result->prepare(sl->item_list, unit_arg)) + DBUG_RETURN(true); + + select_lex->in_tvc= false; + DBUG_RETURN(false); +} + + +/** + Save Query Plan Footprint +*/ + +int table_value_constr::save_explain_data_intern(THD *thd, + Explain_query *output) +{ + const char *message= "No tables used"; + DBUG_ENTER("table_value_constr::save_explain_data_intern"); + DBUG_PRINT("info", ("Select %p, type %s, message %s", + select_lex, select_lex->type, + message)); + DBUG_ASSERT(have_query_plan == QEP_AVAILABLE); + + /* There should be no attempts to save query plans for merged selects */ + DBUG_ASSERT(!select_lex->master_unit()->derived || + select_lex->master_unit()->derived->is_materialized_derived() || + select_lex->master_unit()->derived->is_with_table()); + + explain= new (output->mem_root) Explain_select(output->mem_root, + thd->lex->analyze_stmt); + if (!explain) + DBUG_RETURN(1); + + select_lex->set_explain_type(true); + + explain->select_id= select_lex->select_number; + explain->select_type= select_lex->type; + explain->linkage= select_lex->linkage; + explain->using_temporary= NULL; + explain->using_filesort= NULL; + /* Setting explain->message means that all other members are invalid */ + explain->message= message; + + if (select_lex->master_unit()->derived) + explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; + + output->add_node(explain); + + if (select_lex->is_top_level_node()) + output->query_plan_ready(); + + DBUG_RETURN(0); +} + + +/** + Optimization of TVC +*/ + +bool table_value_constr::optimize(THD *thd) +{ + create_explain_query_if_not_exists(thd->lex, thd->mem_root); + have_query_plan= QEP_AVAILABLE; + + if (select_lex->select_number != UINT_MAX && + select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ && + have_query_plan != QEP_NOT_PRESENT_YET && + thd->lex->explain && // for "SET" command in SPs. + (!thd->lex->explain->get_select(select_lex->select_number))) + { + return save_explain_data_intern(thd, thd->lex->explain); + } + return 0; +} + + +/** + Execute of TVC +*/ + +bool table_value_constr::exec(SELECT_LEX *sl) +{ + DBUG_ENTER("table_value_constr::exec"); + List_iterator_fast<List_item> li(lists_of_values); + List_item *elem; + + if (select_options & SELECT_DESCRIBE) + DBUG_RETURN(false); + + if (result->send_result_set_metadata(sl->item_list, + Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) + { + DBUG_RETURN(true); + } + + while ((elem= li++)) + { + result->send_data(*elem); + } + + if (result->send_eof()) + DBUG_RETURN(true); + + DBUG_RETURN(false); +} + + +/** + @brief + Print list + + @param str The reference on the string representation of the list + @param list The list that needed to be print + @param query_type The mode of printing + + @details + The method saves a string representation of list in the + string str. +*/ + +void print_list_item(String *str, List_item *list, + enum_query_type query_type) +{ + bool is_first_elem= true; + List_iterator_fast<Item> it(*list); + Item *item; + + str->append('('); + + while ((item= it++)) + { + if (is_first_elem) + is_first_elem= false; + else + str->append(','); + + item->print(str, query_type); + } + + str->append(')'); +} + + +/** + @brief + Print this TVC + + @param thd The context of the statement + @param str The reference on the string representation of this TVC + @param query_type The mode of printing + + @details + The method saves a string representation of this TVC in the + string str. +*/ + +void table_value_constr::print(THD *thd, String *str, + enum_query_type query_type) +{ + DBUG_ASSERT(thd); + + str->append(STRING_WITH_LEN("values ")); + + bool is_first_elem= true; + List_iterator_fast<List_item> li(lists_of_values); + List_item *list; + + while ((list= li++)) + { + if (is_first_elem) + is_first_elem= false; + else + str->append(','); + + print_list_item(str, list, query_type); + } +} + + +/** + @brief + Create list of lists for TVC from the list of this IN predicate + + @param thd The context of the statement + @param values TVC list of values + + @details + The method uses the list of values of this IN predicate to build + an equivalent list of values that can be used in TVC. + + E.g.: + + <value_list> = 5,2,7 + <transformed_value_list> = (5),(2),(7) + + <value_list> = (5,2),(7,1) + <transformed_value_list> = (5,2),(7,1) + + @retval + false if the method succeeds + true otherwise +*/ + +bool Item_func_in::create_value_list_for_tvc(THD *thd, + List< List<Item> > *values) +{ + bool is_list_of_rows= args[1]->type() == Item::ROW_ITEM; + + for (uint i=1; i < arg_count; i++) + { + List<Item> *tvc_value; + if (!(tvc_value= new (thd->mem_root) List<Item>())) + return true; + + if (is_list_of_rows) + { + Item_row *row_list= (Item_row *)(args[i]); + + for (uint j=0; j < row_list->cols(); j++) + { + if (tvc_value->push_back(row_list->element_index(j), + thd->mem_root)) + return true; + } + } + else if (tvc_value->push_back(args[i])) + return true; + + if (values->push_back(tvc_value, thd->mem_root)) + return true; + } + return false; +} + + +/** + @brief + Create name for the derived table defined by TVC + + @param thd The context of the statement + @param parent_select The SELECT where derived table is used + @param alias The returned created name + + @details + Create name for the derived table using current TVC number + for this parent_select stored in parent_select + + @retval + true if creation fails + false otherwise +*/ + +static bool create_tvc_name(THD *thd, st_select_lex *parent_select, + LEX_CSTRING *alias) +{ + char buff[6]; + + alias->length= my_snprintf(buff, sizeof(buff), + "tvc_%u", parent_select->curr_tvc_name); + alias->str= thd->strmake(buff, alias->length); + if (!alias->str) + return true; + + return false; +} + + +bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, + st_select_lex *tvc_sl) +{ + LEX *lex= thd->lex; + /* SELECT_LEX object where the transformation is performed */ + SELECT_LEX *parent_select= lex->current_select; + uint8 save_derived_tables= lex->derived_tables; + + Query_arena backup; + Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + + /* + Create SELECT_LEX of the subquery SQ used in the result of transformation + */ + lex->current_select= tvc_sl; + if (mysql_new_select(lex, 0, NULL)) + goto err; + mysql_init_select(lex); + /* Create item list as '*' for the subquery SQ */ + Item *item; + SELECT_LEX *sq_select; // select for IN subquery; + sq_select= lex->current_select; + sq_select->linkage= tvc_sl->linkage; + sq_select->parsing_place= SELECT_LIST; + item= new (thd->mem_root) Item_field(thd, &sq_select->context, + NULL, NULL, &star_clex_str); + if (item == NULL || add_item_to_list(thd, item)) + goto err; + (sq_select->with_wild)++; + + /* Exclude SELECT with TVC */ + tvc_sl->exclude(); + /* + Create derived table DT that will wrap TVC in the result of transformation + */ + SELECT_LEX *tvc_select; // select for tvc + SELECT_LEX_UNIT *derived_unit; // unit for tvc_select + if (mysql_new_select(lex, 1, tvc_sl)) + goto err; + tvc_select= lex->current_select; + derived_unit= tvc_select->master_unit(); + tvc_select->linkage= DERIVED_TABLE_TYPE; + + lex->current_select= sq_select; + + /* + Create the name of the wrapping derived table and + add it to the FROM list of the subquery SQ + */ + Table_ident *ti; + LEX_CSTRING alias; + TABLE_LIST *derived_tab; + if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) || + create_tvc_name(thd, parent_select, &alias)) + goto err; + if (!(derived_tab= + sq_select->add_table_to_list(thd, + ti, &alias, 0, + TL_READ, MDL_SHARED_READ))) + goto err; + sq_select->add_joined_table(derived_tab); + sq_select->add_where_field(derived_unit->first_select()); + sq_select->context.table_list= sq_select->table_list.first; + sq_select->context.first_name_resolution_table= sq_select->table_list.first; + sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; + lex->derived_tables|= DERIVED_SUBQUERY; + + sq_select->where= 0; + sq_select->set_braces(false); + derived_unit->set_with_clause(0); + + if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE) + ((subselect_single_select_engine *) engine)->change_select(sq_select); + + if (arena) + thd->restore_active_arena(arena, &backup); + lex->current_select= sq_select; + return false; + +err: + if (arena) + thd->restore_active_arena(arena, &backup); + lex->derived_tables= save_derived_tables; + lex->current_select= parent_select; + return true; +} + + +/** + @brief + Transform IN predicate into IN subquery + + @param thd The context of the statement + @param arg Not used + + @details + The method transforms this IN predicate into in equivalent IN subquery: + + <left_expr> IN (<value_list>) + => + <left_expr> IN (SELECT * FROM (VALUES <transformed_value_list>) AS tvc_#) + + E.g.: + + <value_list> = 5,2,7 + <transformed_value_list> = (5),(2),(7) + + <value_list> = (5,2),(7,1) + <transformed_value_list> = (5,2),(7,1) + + If the transformation succeeds the method returns the result IN subquery, + otherwise this IN predicate is returned. + + @retval + pointer to the result of transformation if succeeded + pointer to this IN predicate otherwise +*/ + +Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd, + uchar *arg) +{ + if (!transform_into_subq) + return this; + + transform_into_subq= false; + + List<List_item> values; + + LEX *lex= thd->lex; + /* SELECT_LEX object where the transformation is performed */ + SELECT_LEX *parent_select= lex->current_select; + uint8 save_derived_tables= lex->derived_tables; + + for (uint i=1; i < arg_count; i++) + { + if (!args[i]->const_item()) + return this; + } + + Query_arena backup; + Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + + /* + Create SELECT_LEX of the subquery SQ used in the result of transformation + */ + if (mysql_new_select(lex, 1, NULL)) + goto err; + mysql_init_select(lex); + /* Create item list as '*' for the subquery SQ */ + Item *item; + SELECT_LEX *sq_select; // select for IN subquery; + sq_select= lex->current_select; + sq_select->parsing_place= SELECT_LIST; + item= new (thd->mem_root) Item_field(thd, &sq_select->context, + NULL, NULL, &star_clex_str); + if (item == NULL || add_item_to_list(thd, item)) + goto err; + (sq_select->with_wild)++; + /* + Create derived table DT that will wrap TVC in the result of transformation + */ + SELECT_LEX *tvc_select; // select for tvc + SELECT_LEX_UNIT *derived_unit; // unit for tvc_select + if (mysql_new_select(lex, 1, NULL)) + goto err; + mysql_init_select(lex); + tvc_select= lex->current_select; + derived_unit= tvc_select->master_unit(); + tvc_select->linkage= DERIVED_TABLE_TYPE; + + /* Create TVC used in the transformation */ + if (create_value_list_for_tvc(thd, &values)) + goto err; + if (!(tvc_select->tvc= + new (thd->mem_root) + table_value_constr(values, + tvc_select, + tvc_select->options))) + goto err; + + lex->current_select= sq_select; + + /* + Create the name of the wrapping derived table and + add it to the FROM list of the subquery SQ + */ + Table_ident *ti; + LEX_CSTRING alias; + TABLE_LIST *derived_tab; + if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) || + create_tvc_name(thd, parent_select, &alias)) + goto err; + if (!(derived_tab= + sq_select->add_table_to_list(thd, + ti, &alias, 0, + TL_READ, MDL_SHARED_READ))) + goto err; + sq_select->add_joined_table(derived_tab); + sq_select->add_where_field(derived_unit->first_select()); + sq_select->context.table_list= sq_select->table_list.first; + sq_select->context.first_name_resolution_table= sq_select->table_list.first; + sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; + lex->derived_tables|= DERIVED_SUBQUERY; + + sq_select->where= 0; + sq_select->set_braces(false); + derived_unit->set_with_clause(0); + + /* Create IN subquery predicate */ + sq_select->parsing_place= parent_select->parsing_place; + Item_in_subselect *in_subs; + Item *sq; + if (!(in_subs= + new (thd->mem_root) Item_in_subselect(thd, args[0], sq_select))) + goto err; + sq= in_subs; + if (negated) + sq= negate_expression(thd, in_subs); + else + in_subs->emb_on_expr_nest= emb_on_expr_nest; + + if (arena) + thd->restore_active_arena(arena, &backup); + thd->lex->current_select= parent_select; + + if (sq->fix_fields(thd, (Item **)&sq)) + goto err; + + parent_select->curr_tvc_name++; + return sq; + +err: + if (arena) + thd->restore_active_arena(arena, &backup); + lex->derived_tables= save_derived_tables; + thd->lex->current_select= parent_select; + return NULL; +} + + +/** + @brief + Check if this IN-predicate can be transformed in IN-subquery + with TVC + + @param thd The context of the statement + + @details + Compare the number of elements in the list of + values in this IN-predicate with the + in_subquery_conversion_threshold special variable + + @retval + true if transformation can be made + false otherwise +*/ + +bool Item_func_in::to_be_transformed_into_in_subq(THD *thd) +{ + uint values_count= arg_count-1; + + if (args[1]->type() == Item::ROW_ITEM) + values_count*= ((Item_row *)(args[1]))->cols(); + + if (values_count < thd->variables.in_subquery_conversion_threshold) + return false; + + return true; +} + + +/** + @brief + Transform IN predicates into IN subqueries in WHERE and ON expressions + + @param thd The context of the statement + + @details + For each IN predicate from AND parts of the WHERE condition and/or + ON expressions of the SELECT for this join the method performs + the intransformation into an equivalent IN sunquery if it's needed. + + @retval + false always +*/ + +bool JOIN::transform_in_predicates_into_in_subq(THD *thd) +{ + DBUG_ENTER("JOIN::transform_in_predicates_into_in_subq"); + if (!select_lex->in_funcs.elements) + DBUG_RETURN(false); + + SELECT_LEX *save_current_select= thd->lex->current_select; + enum_parsing_place save_parsing_place= select_lex->parsing_place; + thd->lex->current_select= select_lex; + if (conds) + { + select_lex->parsing_place= IN_WHERE; + conds= + conds->transform(thd, + &Item::in_predicate_to_in_subs_transformer, + (uchar*) 0); + if (!conds) + DBUG_RETURN(true); + select_lex->prep_where= conds ? conds->copy_andor_structure(thd) : 0; + select_lex->where= conds; + } + + if (join_list) + { + TABLE_LIST *table; + List_iterator<TABLE_LIST> li(*join_list); + select_lex->parsing_place= IN_ON; + + while ((table= li++)) + { + if (table->on_expr) + { + table->on_expr= + table->on_expr->transform(thd, + &Item::in_predicate_to_in_subs_transformer, + (uchar*) 0); + if (!table->on_expr) + DBUG_RETURN(true); + table->prep_on_expr= table->on_expr ? + table->on_expr->copy_andor_structure(thd) : 0; + } + } + } + + select_lex->in_funcs.empty(); + select_lex->parsing_place= save_parsing_place; + thd->lex->current_select= save_current_select; + DBUG_RETURN(false); +} + diff --git a/sql/sql_tvc.h b/sql/sql_tvc.h new file mode 100644 index 00000000000..420311cccb2 --- /dev/null +++ b/sql/sql_tvc.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2017, 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef SQL_TVC_INCLUDED +#define SQL_TVC_INCLUDED +#include "sql_type.h" + +typedef List<Item> List_item; +class select_result; +class Explain_select; +class Explain_query; +class Item_func_in; +class st_select_lex_unit; +typedef class st_select_lex SELECT_LEX; + +/** + @class table_value_constr + @brief Definition of a Table Value Construction(TVC) + + It contains a list of lists of values which this TVC is defined by and + reference on SELECT where this TVC is defined. +*/ +class table_value_constr : public Sql_alloc +{ +public: + List<List_item> lists_of_values; + select_result *result; + SELECT_LEX *select_lex; + + enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE} have_query_plan; + + Explain_select *explain; + ulonglong select_options; + + table_value_constr(List<List_item> tvc_values, SELECT_LEX *sl, + ulonglong select_options_arg) : + lists_of_values(tvc_values), result(0), select_lex(sl), + have_query_plan(QEP_NOT_PRESENT_YET), explain(0), + select_options(select_options_arg) + { }; + + bool prepare(THD *thd_arg, SELECT_LEX *sl, + select_result *tmp_result, + st_select_lex_unit *unit_arg); + + int save_explain_data_intern(THD *thd_arg, + Explain_query *output); + bool optimize(THD *thd_arg); + bool exec(SELECT_LEX *sl); + + void print(THD *thd_arg, String *str, enum_query_type query_type); +}; +#endif /* SQL_TVC_INCLUDED */ diff --git a/sql/sql_type.cc b/sql/sql_type.cc index dd4cb2b8068..e84bdb1455d 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_type.h" #include "sql_const.h" #include "sql_class.h" @@ -54,11 +55,13 @@ Type_handler_set type_handler_set; Type_handler_string type_handler_string; Type_handler_var_string type_handler_var_string; Type_handler_varchar type_handler_varchar; +static Type_handler_varchar_compressed type_handler_varchar_compressed; Type_handler_tiny_blob type_handler_tiny_blob; Type_handler_medium_blob type_handler_medium_blob; Type_handler_long_blob type_handler_long_blob; Type_handler_blob type_handler_blob; +static Type_handler_blob_compressed type_handler_blob_compressed; #ifdef HAVE_SPATIAL Type_handler_geometry type_handler_geometry; @@ -922,6 +925,9 @@ Type_handler::get_handler_by_field_type(enum_field_types type) in field_type() context and add DBUG_ASSERT(0) here. */ return &type_handler_newdate; + case MYSQL_TYPE_VARCHAR_COMPRESSED: + case MYSQL_TYPE_BLOB_COMPRESSED: + break; }; DBUG_ASSERT(0); return &type_handler_string; @@ -945,10 +951,12 @@ Type_handler::get_handler_by_real_type(enum_field_types type) case MYSQL_TYPE_DOUBLE: return &type_handler_double; case MYSQL_TYPE_NULL: return &type_handler_null; case MYSQL_TYPE_VARCHAR: return &type_handler_varchar; + case MYSQL_TYPE_VARCHAR_COMPRESSED: return &type_handler_varchar_compressed; case MYSQL_TYPE_TINY_BLOB: return &type_handler_tiny_blob; case MYSQL_TYPE_MEDIUM_BLOB: return &type_handler_medium_blob; case MYSQL_TYPE_LONG_BLOB: return &type_handler_long_blob; case MYSQL_TYPE_BLOB: return &type_handler_blob; + case MYSQL_TYPE_BLOB_COMPRESSED: return &type_handler_blob_compressed; case MYSQL_TYPE_VAR_STRING: /* VAR_STRING is actually a field_type(), not a real_type(), @@ -1304,6 +1312,21 @@ Field *Type_handler_varchar::make_conversion_table_field(TABLE *table, } +Field *Type_handler_varchar_compressed::make_conversion_table_field(TABLE *table, + uint metadata, + const Field *target) + const +{ + return new(table->in_use->mem_root) + Field_varstring_compressed(NULL, metadata, + HA_VARCHAR_PACKLENGTH(metadata), + (uchar *) "", 1, Field::NONE, + &empty_clex_str, + table->s, target->charset(), + zlib_compression_method); +} + + Field *Type_handler_tiny_blob::make_conversion_table_field(TABLE *table, uint metadata, const Field *target) @@ -1326,6 +1349,19 @@ Field *Type_handler_blob::make_conversion_table_field(TABLE *table, } +Field *Type_handler_blob_compressed::make_conversion_table_field(TABLE *table, + uint metadata, + const Field *target) + const +{ + return new(table->in_use->mem_root) + Field_blob_compressed(NULL, (uchar *) "", 1, Field::NONE, + &empty_clex_str, + table->s, 2, target->charset(), + zlib_compression_method); +} + + Field *Type_handler_medium_blob::make_conversion_table_field(TABLE *table, uint metadata, const Field *target) @@ -1706,6 +1742,7 @@ bool Type_handler_string_result:: const { def->redefine_stage1_common(dup, file, schema); + def->set_compression_method(dup->compression_method()); def->create_length_to_internal_length_string(); return false; } diff --git a/sql/sql_type.h b/sql/sql_type.h index 6dbd51a1ebd..427d59718f6 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -73,7 +73,6 @@ struct Schema_specification_st; struct TABLE; struct SORT_FIELD_ATTR; - /* Flags for collation aggregation modes, used in TDCollation::agg(): @@ -2568,6 +2567,14 @@ public: }; +class Type_handler_varchar_compressed: public Type_handler_varchar +{ +public: + Field *make_conversion_table_field(TABLE *, uint metadata, + const Field *target) const; +}; + + class Type_handler_blob_common: public Type_handler_longstr { public: @@ -2669,6 +2676,14 @@ public: }; +class Type_handler_blob_compressed: public Type_handler_blob +{ +public: + Field *make_conversion_table_field(TABLE *, uint metadata, + const Field *target) const; +}; + + #ifdef HAVE_SPATIAL class Type_handler_geometry: public Type_handler_string_result { diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 612cc97f6a2..7b2d041a094 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -31,7 +31,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" // close_mysql_tables @@ -189,7 +189,7 @@ void udf_init() } table->use_all_columns(); - while (!(error= read_record_info.read_record(&read_record_info))) + while (!(error= read_record_info.read_record())) { DBUG_PRINT("info",("init udf record")); LEX_CSTRING name; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 47286f1df14..c7c4bee1c00 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -20,7 +20,7 @@ UNION's were introduced by Monty and Sinisa <sinisa@mysql.com> */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_union.h" @@ -694,64 +694,6 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl, } -class Type_holder: public Sql_alloc, - public Item_args, - public Type_handler_hybrid_field_type, - public Type_all_attributes, - public Type_geometry_attributes -{ - TYPELIB *m_typelib; - bool m_maybe_null; -public: - Type_holder() - :m_typelib(NULL), - m_maybe_null(false) - { } - - void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; } - bool get_maybe_null() const { return m_maybe_null; } - - uint decimal_precision() const - { - /* - Type_holder is not used directly to create fields, so - its virtual decimal_precision() is never called. - We should eventually extend create_result_table() to accept - an array of Type_holders directly, without having to allocate - Item_type_holder's and put them into List<Item>. - */ - DBUG_ASSERT(0); - return 0; - } - void set_geometry_type(uint type) - { - Type_geometry_attributes::set_geometry_type(type); - } - uint uint_geometry_type() const - { - return Type_geometry_attributes::get_geometry_type(); - } - void set_typelib(TYPELIB *typelib) - { - m_typelib= typelib; - } - TYPELIB *get_typelib() const - { - return m_typelib; - } - - bool aggregate_attributes(THD *thd) - { - for (uint i= 0; i < arg_count; i++) - m_maybe_null|= args[i]->maybe_null; - return - type_handler()->Item_hybrid_func_fix_attributes(thd, - "UNION", this, this, - args, arg_count); - } -}; - - /** Aggregate data type handlers for the "count" leftmost UNION parts. */ @@ -884,6 +826,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, bool is_union_select; bool have_except= FALSE, have_intersect= FALSE; bool instantiate_tmp_table= false; + bool single_tvc= !first_sl->next_select() && first_sl->tvc; DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ASSERT(thd == thd_arg); DBUG_ASSERT(thd == current_thd); @@ -910,16 +853,26 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, /* fast reinit for EXPLAIN */ for (sl= first_sl; sl; sl= sl->next_select()) { - sl->join->result= result; - select_limit_cnt= HA_POS_ERROR; - offset_limit_cnt= 0; - if (!sl->join->procedure && - result->prepare(sl->join->fields_list, this)) + if (sl->tvc) { - DBUG_RETURN(TRUE); + sl->tvc->result= result; + if (result->prepare(sl->item_list, this)) + DBUG_RETURN(TRUE); + sl->tvc->select_options|= SELECT_DESCRIBE; + } + else + { + sl->join->result= result; + select_limit_cnt= HA_POS_ERROR; + offset_limit_cnt= 0; + if (!sl->join->procedure && + result->prepare(sl->join->fields_list, this)) + { + DBUG_RETURN(TRUE); + } + sl->join->select_options|= SELECT_DESCRIBE; + sl->join->reinit(); } - sl->join->select_options|= SELECT_DESCRIBE; - sl->join->reinit(); } } DBUG_RETURN(FALSE); @@ -929,7 +882,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_sl; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; - is_union_select= is_unit_op() || fake_select_lex; + is_union_select= is_unit_op() || fake_select_lex || single_tvc; for (SELECT_LEX *s= first_sl; s; s= s->next_select()) { @@ -949,8 +902,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (is_union_select || is_recursive) { - if (is_unit_op() && !union_needs_tmp_table() && - !have_except && !have_intersect) + if ((is_unit_op() && !union_needs_tmp_table() && + !have_except && !have_intersect) || single_tvc) { SELECT_LEX *last= first_select(); while (last->next_select()) @@ -985,7 +938,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (!is_union_select && !is_recursive) { - if (prepare_join(thd_arg, first_sl, tmp_result, additional_options, + if (sl->tvc) + { + if (sl->tvc->prepare(thd_arg, sl, tmp_result, this)) + goto err; + } + else if (prepare_join(thd_arg, first_sl, tmp_result, additional_options, is_union_select)) goto err; types= first_sl->item_list; @@ -994,8 +952,13 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, for (;sl; sl= sl->next_select(), union_part_count++) { - if (prepare_join(thd_arg, sl, tmp_result, additional_options, - is_union_select)) + if (sl->tvc) + { + if (sl->tvc->prepare(thd_arg, sl, tmp_result, this)) + goto err; + } + else if (prepare_join(thd_arg, sl, tmp_result, additional_options, + is_union_select)) goto err; /* @@ -1315,6 +1278,18 @@ bool st_select_lex_unit::optimize() } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { + if (sl->tvc) + { + sl->tvc->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + if (sl->tvc->optimize(thd)) + { + thd->lex->current_select= lex_select_save; + DBUG_RETURN(TRUE); + } + continue; + } thd->lex->current_select= sl; if (optimized) @@ -1338,7 +1313,7 @@ bool st_select_lex_unit::optimize() we don't calculate found_rows() per union part. Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts. */ - sl->join->select_options= + sl->join->select_options= (select_limit_cnt == HA_POS_ERROR || sl->braces) ? sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; @@ -1425,15 +1400,28 @@ bool st_select_lex_unit::exec() we don't calculate found_rows() per union part. Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts. */ - sl->join->select_options= - (select_limit_cnt == HA_POS_ERROR || sl->braces) ? - sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; - saved_error= sl->join->optimize(); + if (sl->tvc) + { + sl->tvc->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + saved_error= sl->tvc->optimize(thd); + } + else + { + sl->join->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + saved_error= sl->join->optimize(); + } } if (!saved_error) { records_at_start= table->file->stats.records; - sl->join->exec(); + if (sl->tvc) + sl->tvc->exec(sl); + else + sl->join->exec(); if (sl == union_distinct && !(with_element && with_element->is_recursive)) { // This is UNION DISTINCT, so there should be a fake_select_lex @@ -1442,7 +1430,8 @@ bool st_select_lex_unit::exec() DBUG_RETURN(TRUE); table->no_keyread=1; } - saved_error= sl->join->error; + if (!sl->tvc) + saved_error= sl->join->error; offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : 0); @@ -1672,8 +1661,13 @@ bool st_select_lex_unit::exec_recursive() for (st_select_lex *sl= start ; sl != end; sl= sl->next_select()) { thd->lex->current_select= sl; - sl->join->exec(); - saved_error= sl->join->error; + if (sl->tvc) + sl->tvc->exec(sl); + else + { + sl->join->exec(); + saved_error= sl->join->error; + } if (!saved_error) { examined_rows+= thd->get_examined_row_count(); diff --git a/sql/sql_union.h b/sql/sql_union.h index 171f607fba7..d5659e5d947 100644 --- a/sql/sql_union.h +++ b/sql/sql_union.h @@ -16,8 +16,6 @@ #ifndef SQL_UNION_INCLUDED #define SQL_UNION_INCLUDED -#include "my_global.h" /* ulong */ - class THD; class select_result; struct LEX; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 55be8723ab3..0b6564d8e49 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -20,7 +20,7 @@ Multi-table updates were introduced by Sinisa & Monty */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_update.h" #include "sql_cache.h" // query_cache_* @@ -255,11 +255,12 @@ int mysql_update(THD *thd, { bool using_limit= limit != HA_POS_ERROR; bool safe_update= thd->variables.option_bits & OPTION_SAFE_UPDATES; - bool used_key_is_modified= FALSE, transactional_table, will_batch; + bool used_key_is_modified= FALSE, transactional_table; + bool will_batch= FALSE; bool can_compare_record; int res; int error, loc_error; - uint dup_key_found; + ha_rows dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -276,6 +277,7 @@ int mysql_update(THD *thd, ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; + bool has_triggers, binlog_is_row, do_direct_update= FALSE; Update_plan query_plan(thd->mem_root); Explain_update *explain; TABLE_LIST *update_source_table; @@ -287,7 +289,7 @@ int mysql_update(THD *thd, if (open_tables(thd, &table_list, &table_count, 0)) DBUG_RETURN(1); - //Prepare views so they are handled correctly. + /* Prepare views so they are handled correctly */ if (mysql_handle_derived(thd->lex, DT_INIT)) DBUG_RETURN(1); @@ -451,7 +453,8 @@ int mysql_update(THD *thd, goto err; } } - init_ftfuncs(thd, select_lex, 1); + if (init_ftfuncs(thd, select_lex, 1)) + goto err; table->mark_columns_needed_for_update(); @@ -514,7 +517,6 @@ int mysql_update(THD *thd, query_plan.using_io_buffer= true; } - /* Ok, we have generated a query plan for the UPDATE. - if we're running EXPLAIN UPDATE, goto produce explain output @@ -522,16 +524,75 @@ int mysql_update(THD *thd, */ if (thd->lex->describe) goto produce_explain_and_leave; - explain= query_plan.save_explain_update_data(query_plan.mem_root, thd); + if (!(explain= query_plan.save_explain_update_data(query_plan.mem_root, thd))) + goto err; ANALYZE_START_TRACKING(&explain->command_tracker); DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", dbug_serve_apcs(thd, 1);); - + + has_triggers= (table->triggers && + (table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_BEFORE) || + table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER))); + DBUG_PRINT("info", ("has_triggers: %s", has_triggers ? "TRUE" : "FALSE")); + binlog_is_row= thd->is_current_stmt_binlog_format_row(); + DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE")); + if (!(select && select->quick)) status_var_increment(thd->status_var.update_scan_count); + /* + We can use direct update (update that is done silently in the handler) + if none of the following conditions are true: + - There are triggers + - There is binary logging + - using_io_buffer + - This means that the partition changed or the key we want + to use for scanning the table is changed + - ignore is set + - Direct updates don't return the number of ignored rows + - There is a virtual not stored column in the WHERE clause + - Changing a field used by a stored virtual column, which + would require the column to be recalculated. + - ORDER BY or LIMIT + - As this requires the rows to be updated in a specific order + - Note that Spider can handle ORDER BY and LIMIT in a cluster with + one data node. These conditions are therefore checked in + direct_update_rows_init(). + + Direct update does not require a WHERE clause + + Later we also ensure that we are only using one table (no sub queries) + */ + if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) && + !has_triggers && !binlog_is_row && + !query_plan.using_io_buffer && !ignore && + !table->check_virtual_columns_marked_for_read() && + !table->check_virtual_columns_marked_for_write()) + { + DBUG_PRINT("info", ("Trying direct update")); + if (select && select->cond && + (select->cond->used_tables() == table->map)) + { + DBUG_ASSERT(!table->file->pushed_cond); + if (!table->file->cond_push(select->cond)) + table->file->pushed_cond= select->cond; + } + + if (!table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) && + !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) && + !table->file->direct_update_rows_init()) + { + do_direct_update= TRUE; + + /* Direct update is not using_filesort and is not using_io_buffer */ + goto update_begin; + } + } + if (query_plan.using_filesort || query_plan.using_io_buffer) { /* @@ -619,7 +680,7 @@ int mysql_update(THD *thd, THD_STAGE_INFO(thd, stage_searching_rows_for_update); ha_rows tmp_limit= limit; - while (!(error=info.read_record(&info)) && !thd->killed) + while (!(error=info.read_record()) && !thd->killed) { explain->buf_tracker.on_record_read(); thd->inc_examined_row_count(1); @@ -691,6 +752,7 @@ int mysql_update(THD *thd, } } +update_begin: if (ignore) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -710,11 +772,20 @@ int mysql_update(THD *thd, transactional_table= table->file->has_transactions(); thd->abort_on_warning= !ignore && thd->is_strict_mode(); - if (table->prepare_triggers_for_update_stmt_or_event()) + + if (do_direct_update) { - will_batch= FALSE; + /* Direct updating is supported */ + DBUG_PRINT("info", ("Using direct update")); + table->reset_default_fields(); + if (!(error= table->file->ha_direct_update_rows(&updated))) + error= -1; + found= updated; + goto update_end; } - else + + if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_UPDATE) && + !table->prepare_triggers_for_update_stmt_or_event()) will_batch= !table->file->start_bulk_update(); /* @@ -735,7 +806,7 @@ int mysql_update(THD *thd, explain->tracker.on_scan_init(); THD_STAGE_INFO(thd, stage_updating); - while (!(error=info.read_record(&info)) && !thd->killed) + while (!(error=info.read_record()) && !thd->killed) { explain->tracker.on_record_read(); thd->inc_examined_row_count(1); @@ -799,6 +870,7 @@ int mysql_update(THD *thd, call then it should be included in the count of dup_key_found and error should be set to 0 (only if these errors are ignored). */ + DBUG_PRINT("info", ("Batched update")); error= table->file->ha_bulk_update_row(table->record[1], table->record[0], &dup_key_found); @@ -947,6 +1019,8 @@ int mysql_update(THD *thd, updated-= dup_key_found; if (will_batch) table->file->end_bulk_update(); + +update_end: table->file->try_semi_consistent_read(0); if (!transactional_table && updated > 0) @@ -1002,6 +1076,11 @@ int mysql_update(THD *thd, DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table); free_underlaid_joins(thd, select_lex); delete file_sort; + if (table->file->pushed_cond) + { + table->file->pushed_cond= 0; + table->file->cond_pop(); + } /* If LAST_INSERT_ID(X) was used, report X */ id= thd->arg_of_last_insert_id_function ? @@ -1027,7 +1106,6 @@ int mysql_update(THD *thd, *found_return= found; *updated_return= updated; - if (thd->lex->analyze_stmt) goto emit_explain_and_leave; @@ -1038,6 +1116,8 @@ err: delete file_sort; free_underlaid_joins(thd, select_lex); table->file->ha_end_keyread(); + if (table->file->pushed_cond) + table->file->cond_pop(); thd->abort_on_warning= 0; DBUG_RETURN(1); @@ -1046,7 +1126,8 @@ produce_explain_and_leave: We come here for various "degenerate" query plans: impossible WHERE, no-partitions-used, impossible-range, etc. */ - query_plan.save_explain_update_data(query_plan.mem_root, thd); + if (!query_plan.save_explain_update_data(query_plan.mem_root, thd)) + goto err; emit_explain_and_leave: int err2= thd->lex->explain->send_explain(thd); @@ -1776,7 +1857,7 @@ int multi_update::prepare(List<Item> ¬_used_values, switch_to_nullable_trigger_fields(*values_for_table[i], table); } } - copy_field= new Copy_field[max_fields]; + copy_field= new (thd->mem_root) Copy_field[max_fields]; DBUG_RETURN(thd->is_fatal_error != 0); } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c5a2555178d..b2be437965e 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -16,7 +16,7 @@ */ #define MYSQL_LEX 1 -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_view.h" @@ -140,7 +140,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view itc.rewind(); while ((check= itc++) && check != item) { - if (my_strcasecmp(system_charset_info, item->name.str, check->name.str) == 0) + if (lex_string_cmp(system_charset_info, &item->name, &check->name) == 0) { if (!gen_unique_view_name) goto err; @@ -650,8 +650,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { C_STRING_WITH_LEN("ALTER ") }, { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; - buff.append(command[thd->lex->create_view->mode].str, - command[thd->lex->create_view->mode].length); + buff.append(&command[thd->lex->create_view->mode]); view_store_options(thd, views, &buff); buff.append(STRING_WITH_LEN("VIEW ")); @@ -683,7 +682,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, buff.append(')'); } buff.append(STRING_WITH_LEN(" AS ")); - buff.append(views->source.str, views->source.length); + buff.append(&views->source); int errcode= query_error_code(thd, TRUE); /* diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 3e48fe6d267..db34b77ddcb 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1,3 +1,20 @@ +/* + Copyright (c) 2016, 2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mariadb.h" #include "sql_parse.h" #include "sql_select.h" #include "sql_list.h" @@ -5,7 +22,6 @@ #include "filesort.h" #include "sql_base.h" #include "sql_window.h" -#include "my_dbug.h" bool @@ -298,6 +314,12 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, } } + List_iterator_fast<Item_window_func> li(win_funcs); + while (Item_window_func * win_func_item= li++) + { + if (win_func_item->check_result_type_of_order_item()) + DBUG_RETURN(1); + } DBUG_RETURN(0); } @@ -716,7 +738,7 @@ public: void init(READ_RECORD *info) { ref_length= info->ref_length; - if (info->read_record == rr_from_pointers) + if (info->read_record_func == rr_from_pointers) { io_cache= NULL; cache_start= info->cache_pos; @@ -945,6 +967,8 @@ private: bool end_of_partition; }; + + ///////////////////////////////////////////////////////////////////////////// /* @@ -1712,7 +1736,17 @@ public: /* Walk to the end of the partition, find how many rows there are. */ while (!cursor.next()) num_rows_in_partition++; + set_win_funcs_row_count(num_rows_in_partition); + } + + ha_rows get_curr_rownum() const + { + return cursor.get_rownum(); + } +protected: + void set_win_funcs_row_count(ha_rows num_rows_in_partition) + { List_iterator_fast<Item_sum> it(sum_functions); Item_sum* item; while ((item= it++)) @@ -1722,11 +1756,43 @@ public: item_with_row_count->set_row_count(num_rows_in_partition); } } +}; + +class Frame_unbounded_following_set_count_no_nulls: + public Frame_unbounded_following_set_count +{ + +public: + Frame_unbounded_following_set_count_no_nulls(THD *thd, + SQL_I_List<ORDER> *partition_list, + SQL_I_List<ORDER> *order_list) : + Frame_unbounded_following_set_count(thd,partition_list, order_list) + { + order_item= order_list->first->item[0]; + } + void next_partition(ha_rows rownum) + { + ha_rows num_rows_in_partition= 0; + if (cursor.fetch()) + return; + + /* Walk to the end of the partition, find how many rows there are. */ + do + { + if (!order_item->is_null()) + num_rows_in_partition++; + } while (!cursor.next()); + + set_win_funcs_row_count(num_rows_in_partition); + } ha_rows get_curr_rownum() const { return cursor.get_rownum(); } + +private: + Item* order_item; }; ///////////////////////////////////////////////////////////////////////////// @@ -2467,6 +2533,21 @@ void add_special_frame_cursors(THD *thd, Cursor_manager *cursor_manager, cursor_manager->add_cursor(fc); break; } + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: + { + fc= new Frame_unbounded_preceding(thd, + spec->partition_list, + spec->order_list); + fc->add_sum_func(item_sum); + cursor_manager->add_cursor(fc); + fc= new Frame_unbounded_following(thd, + spec->partition_list, + spec->order_list); + fc->add_sum_func(item_sum); + cursor_manager->add_cursor(fc); + break; + } default: fc= new Frame_unbounded_preceding( thd, spec->partition_list, spec->order_list); @@ -2491,6 +2572,8 @@ static bool is_computed_with_remove(Item_sum::Sumfunctype sum_func) case Item_sum::NTILE_FUNC: case Item_sum::FIRST_VALUE_FUNC: case Item_sum::LAST_VALUE_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return false; default: return true; @@ -2521,9 +2604,18 @@ void get_window_functions_required_cursors( */ if (item_win_func->requires_partition_size()) { - fc= new Frame_unbounded_following_set_count(thd, + if (item_win_func->only_single_element_order_list()) + { + fc= new Frame_unbounded_following_set_count_no_nulls(thd, + item_win_func->window_spec->partition_list, + item_win_func->window_spec->order_list); + } + else + { + fc= new Frame_unbounded_following_set_count(thd, item_win_func->window_spec->partition_list, item_win_func->window_spec->order_list); + } fc->add_sum_func(sum_func); cursor_manager->add_cursor(fc); } @@ -2676,7 +2768,7 @@ bool compute_window_func(THD *thd, while (true) { - if ((err= info.read_record(&info))) + if ((err= info.read_record())) break; // End of file. /* Remember current row so that we can restore it before computing @@ -2704,6 +2796,13 @@ bool compute_window_func(THD *thd, { cursor_manager->notify_cursors_next_row(); } + + /* Check if we found any error in the window function while adding values + through cursors. */ + if (thd->is_error() || thd->is_killed()) + break; + + /* Return to current row after notifying cursors for each window function. */ tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf); @@ -2987,10 +3086,14 @@ Window_funcs_computation::save_explain_plan(MEM_ROOT *mem_root, Explain_aggr_window_funcs *xpl= new Explain_aggr_window_funcs; List_iterator<Window_funcs_sort> it(win_func_sorts); Window_funcs_sort *srt; + if (!xpl) + return 0; while ((srt = it++)) { Explain_aggr_filesort *eaf= new Explain_aggr_filesort(mem_root, is_analyze, srt->filesort); + if (!eaf) + return 0; xpl->sorts.push_back(eaf, mem_root); } return xpl; @@ -3120,5 +3223,3 @@ Window_funcs_computation::save_explain_plan(MEM_ROOT *mem_root, } else #endif - - diff --git a/sql/sql_window.h b/sql/sql_window.h index ed1d9e36492..392f89e8f03 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -1,9 +1,22 @@ +/* + Copyright (c) 2016, 2017 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SQL_WINDOW_INCLUDED #define SQL_WINDOW_INCLUDED -#include "my_global.h" -#include "item.h" #include "filesort.h" #include "records.h" diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a695d6afd60..63194e9d097 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -33,7 +33,7 @@ #define Lex (thd->lex) #define Select Lex->current_select -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" /* comp_*_creator */ #include "sql_table.h" /* primary_key_name */ @@ -67,6 +67,7 @@ #include "lex_token.h" #include "sql_lex.h" #include "sql_sequence.h" +#include "sql_tvc.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -217,7 +218,7 @@ int LEX::case_stmt_action_when(Item *when, bool simple) var= new (thd->mem_root) Item_case_expr(thd, spcont->get_current_case_expr_id()); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (var) { var->m_sp= sphead; @@ -399,7 +400,7 @@ LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar, Item_splocal(thd, name, spvar->offset, spvar->type_handler(), pos_in_q, len_in_q); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (item) item->m_sp= sphead; #endif @@ -1128,6 +1129,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token HEX_NUM %token HEX_STRING %token HIGH_PRIORITY +%token INVISIBLE_SYM %token HOST_SYM %token HOSTS_SYM %token HOUR_MICROSECOND_SYM @@ -1237,6 +1239,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM %token MAXVALUE_SYM /* SQL-2003-N */ +%token MEDIAN_SYM %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -1286,6 +1289,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOMAXVALUE_SYM %token NOMINVALUE_SYM %token NO_WAIT_SYM +%token NOWAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM %token NULL_SYM /* SQL-2003-R */ @@ -1328,6 +1332,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARTITIONING_SYM %token PASSWORD_SYM %token PERCENT_RANK_SYM +%token PERCENTILE_CONT_SYM +%token PERCENTILE_DISC_SYM %token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM @@ -1554,6 +1560,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UTC_TIMESTAMP_SYM %token UTC_TIME_SYM %token VALUES /* SQL-2003-R */ +%token VALUES_IN_SYM +%token VALUES_LESS_SYM %token VALUE_SYM /* SQL-2003-R */ %token VARBINARY %token VARCHAR /* SQL-2003-R */ @@ -1574,6 +1582,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WINDOW_SYM %token WHILE_SYM %token WITH /* SQL-2003-R */ +%token WITHIN %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ @@ -1647,6 +1656,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <const_simple_string> field_length opt_field_length opt_field_length_default_1 + opt_compression_method %type <string> text_string hex_or_bin_String opt_gconcat_separator @@ -1670,7 +1680,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <num> order_dir lock_option udf_type opt_local opt_no_write_to_binlog - opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not select_derived_init transaction_access_mode_types @@ -1736,6 +1746,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); window_func_expr window_func simple_window_func + inverse_distribution_function + percentile_function + inverse_distribution_function_def function_call_keyword function_call_nonkeyword function_call_generic @@ -1787,7 +1800,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_primary_ident table_primary_derived select_derived derived_table_list select_derived_union + derived_simple_table derived_query_specification + derived_table_value_constructor %type <date_time_type> date_time_type; %type <interval> interval @@ -1828,11 +1843,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <select_lex> subselect get_select_lex get_select_lex_derived + simple_table query_specification query_term_union_not_ready query_term_union_ready query_expression_body select_paren_derived + table_value_constructor %type <boolfunc2creator> comp_op @@ -1895,7 +1912,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_field_or_var_spec fields_or_vars opt_load_data_set_spec view_list_opt view_list view_select trigger_tail sp_tail sf_tail event_tail - udf_tail create_function_tail + udf_tail create_function_tail create_aggregate_function_tail install uninstall partition_entry binlog_base64_event normal_key_options normal_key_opts all_key_opt spatial_key_options fulltext_key_options normal_key_opt @@ -1909,6 +1926,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); vcol_opt_attribute_list vcol_attribute opt_serial_attribute opt_serial_attribute_list serial_attribute explainable_command + opt_lock_wait_timeout opt_delete_gtid_domain END_OF_INPUT @@ -1959,7 +1977,6 @@ END_OF_INPUT %type <spvar_definition> row_field_name row_field_definition %type <spvar_definition_list> row_field_definition_list row_type_body - %type <NONE> opt_window_clause window_def_list window_def window_spec %type <lex_str_ptr> window_name %type <NONE> opt_window_ref opt_window_frame_clause @@ -2558,7 +2575,7 @@ create: if (Lex->add_create_index($2, &$5, $6, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' normal_key_options + '(' key_list ')' opt_lock_wait_timeout normal_key_options opt_index_lock_algorithm { } | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2568,7 +2585,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' fulltext_key_options + '(' key_list ')' opt_lock_wait_timeout fulltext_key_options opt_index_lock_algorithm { } | create_or_replace spatial INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2578,7 +2595,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' spatial_key_options + '(' key_list ')' opt_lock_wait_timeout spatial_key_options opt_index_lock_algorithm { } | create_or_replace DATABASE opt_if_not_exists ident { @@ -2622,8 +2639,16 @@ create: event_tail { } | create_or_replace definer FUNCTION_SYM - { Lex->create_info.set($1); } - sf_tail + { + Lex->create_info.set($1); + } + sf_tail_not_aggregate + { } + | create_or_replace definer AGGREGATE_SYM FUNCTION_SYM + { + Lex->create_info.set($1); + } + sf_tail_aggregate { } | create_or_replace no_definer FUNCTION_SYM { Lex->create_info.set($1); } @@ -2632,9 +2657,8 @@ create: | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM { Lex->create_info.set($1); - Lex->udf.type= UDFTYPE_AGGREGATE; } - udf_tail + create_aggregate_function_tail { } | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list opt_require_clause opt_resource_options @@ -2661,11 +2685,36 @@ create: { } ; +sf_tail_not_aggregate: + sf_tail + { + if (Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR) + { + my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0), "")); + } + Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE); + } + +sf_tail_aggregate: + sf_tail + { + if (!(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)) + { + my_yyabort_error((ER_INVALID_AGGREGATE_FUNCTION, MYF(0), "")); + } + Lex->sphead->set_chistics_agg_type(GROUP_AGGREGATE); + } + create_function_tail: - sf_tail { } + sf_tail_not_aggregate { } | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; } ; +create_aggregate_function_tail: + sf_tail_aggregate + { } + | udf_tail { Lex->udf.type= UDFTYPE_AGGREGATE; } + ; opt_sequence: /* empty */ { } | sequence_defs @@ -3995,7 +4044,19 @@ sp_proc_stmt_fetch_head: ; sp_proc_stmt_fetch: - sp_proc_stmt_fetch_head sp_fetch_list { } + sp_proc_stmt_fetch_head sp_fetch_list { } + | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + lex->sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR; + sp_instr_agg_cfetch *i= + new (thd->mem_root) sp_instr_agg_cfetch(sp->instructions(), + lex->spcont); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } ; sp_proc_stmt_close: @@ -5072,8 +5133,15 @@ part_type_def: { Lex->part_info->part_type= RANGE_PARTITION; } | RANGE_SYM part_column_list { Lex->part_info->part_type= RANGE_PARTITION; } - | LIST_SYM part_func - { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM + { + Select->parsing_place= IN_PART_FUNC; + } + part_func + { + Lex->part_info->part_type= LIST_PARTITION; + Select->parsing_place= NO_MATTER; + } | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } ; @@ -5319,7 +5387,7 @@ opt_part_values: else part_info->part_type= HASH_PARTITION; } - | VALUES LESS_SYM THAN_SYM + | VALUES_LESS_SYM THAN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -5333,7 +5401,7 @@ opt_part_values: part_info->part_type= RANGE_PARTITION; } part_func_max {} - | VALUES IN_SYM + | VALUES_IN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -6357,6 +6425,10 @@ vcol_attribute: lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + | INVISIBLE_SYM + { + Lex->last_field->field_visibility= USER_DEFINED_INVISIBLE; + } ; parse_vcol_expr: @@ -6741,7 +6813,7 @@ attribute: } | AUTO_INC { Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } | SERIAL_SYM DEFAULT VALUE_SYM - { + { LEX *lex=Lex; lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_KEY_FLAG; lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; @@ -6753,9 +6825,19 @@ attribute: $2->name,Lex->charset->csname)); Lex->last_field->charset= $2; } + | COMPRESSED_SYM opt_compression_method + { + if (Lex->last_field->set_compressed($2)) + MYSQL_YYABORT; + } | serial_attribute ; +opt_compression_method: + /* empty */ { $$= NULL; } + | equal ident { $$= $2.str; } + ; + serial_attribute: not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } | opt_primary KEY_SYM @@ -7270,7 +7352,7 @@ alter: Lex->create_info.storage_media= HA_SM_DEFAULT; DBUG_ASSERT(!Lex->m_sql_cmd); } - alter_options TABLE_SYM table_ident + alter_options TABLE_SYM table_ident opt_lock_wait_timeout { if (!Lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, @@ -8385,7 +8467,7 @@ optimize: /* Will be overridden during execution. */ YYPS->m_lock_type= TL_UNLOCK; } - table_list + table_list opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -8435,13 +8517,13 @@ table_to_table_list: ; table_to_table: - table_ident TO_SYM table_ident + table_ident opt_lock_wait_timeout TO_SYM table_ident { LEX *lex=Lex; SELECT_LEX *sl= lex->current_select; if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE) || - !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING, + !sl->add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } @@ -8580,6 +8662,9 @@ select: select_init: SELECT_SYM select_options_and_item_list select_init3 + | table_value_constructor + | table_value_constructor union_list + | table_value_constructor union_order_or_limit | '(' select_paren ')' | '(' select_paren ')' union_list | '(' select_paren ')' union_order_or_limit @@ -8587,6 +8672,9 @@ select_init: union_list_part2: SELECT_SYM select_options_and_item_list select_init3_union_query_term + | table_value_constructor + | table_value_constructor union_list + | table_value_constructor union_order_or_limit | '(' select_paren_union_query_term ')' | '(' select_paren_union_query_term ')' union_list | '(' select_paren_union_query_term ')' union_order_or_limit @@ -8594,6 +8682,14 @@ union_list_part2: select_paren: { + Lex->current_select->set_braces(true); + } + table_value_constructor + { + DBUG_ASSERT(Lex->current_select->braces); + } + | + { /* In order to correctly parse UNION's global ORDER BY we need to set braces before parsing the clause. @@ -8645,6 +8741,15 @@ select_paren_derived: { Lex->current_select->set_braces(true); } + table_value_constructor + { + DBUG_ASSERT(Lex->current_select->braces); + $$= Lex->current_select->master_unit()->first_select(); + } + | + { + Lex->current_select->set_braces(true); + } SELECT_SYM select_part2_derived opt_table_expression opt_order_clause @@ -8848,14 +8953,14 @@ select_option: opt_select_lock_type: /* empty */ - | FOR_SYM UPDATE_SYM + | FOR_SYM UPDATE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_WRITE; lex->current_select->set_lock_for_tables(TL_WRITE); lex->safe_to_cache_query=0; } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; @@ -9477,6 +9582,7 @@ column_default_non_parenthesized_expr: | variable | sum_expr | window_func_expr + | inverse_distribution_function | ROW_SYM '(' expr ',' expr_list ')' { $5->push_front($3, thd->mem_root); @@ -9560,8 +9666,9 @@ column_default_non_parenthesized_expr: $3); if ($$ == NULL) MYSQL_YYABORT; + Lex->default_used= TRUE; } - | VALUES '(' simple_ident_nospvar ')' + | VALUE_SYM '(' simple_ident_nospvar ')' { $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(), $3); @@ -10335,6 +10442,11 @@ geometry_function: Geometry::wkb_polygon, Geometry::wkb_linestring)); } + | WITHIN '(' expr ',' expr ')' + { + $$= GEOM_NEW(thd, Item_func_spatial_precise_rel(thd, $3, $5, + Item_func::SP_WITHIN_FUNC)); + } ; /* @@ -10644,16 +10756,22 @@ sum_expr: | GROUP_CONCAT_SYM '(' opt_distinct { Select->in_sum_expr++; } expr_list opt_gorder_clause - opt_gconcat_separator + opt_gconcat_separator opt_glimit_clause ')' { SELECT_LEX *sel= Select; sel->in_sum_expr--; $$= new (thd->mem_root) - Item_func_group_concat(thd, Lex->current_context(), $3, $5, - sel->gorder_list, $7); + Item_func_group_concat(thd, Lex->current_context(), + $3, $5, + sel->gorder_list, $7, $8, + sel->select_limit, + sel->offset_limit); if ($$ == NULL) MYSQL_YYABORT; + sel->select_limit= NULL; + sel->offset_limit= NULL; + sel->explicit_limit= 0; $5->empty(); sel->gorder_list.empty(); } @@ -10796,6 +10914,71 @@ simple_window_func: } ; + + +inverse_distribution_function: + percentile_function OVER_SYM + '(' opt_window_partition_clause ')' + { + LEX *lex= Lex; + if (Select->add_window_spec(thd, lex->win_ref, + Select->group_list, + Select->order_list, + NULL)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, + thd->lex->win_spec); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + ; + +percentile_function: + inverse_distribution_function_def WITHIN GROUP_SYM '(' + { Select->prepare_add_window_spec(thd); } + order_by_single_element_list ')' + { + $$= $1; + } + | MEDIAN_SYM '(' expr ')' + { + Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3, + thd->charset()); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + if (add_order_to_list(thd, $3,FALSE)) MYSQL_YYABORT; + + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, args); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +inverse_distribution_function_def: + PERCENTILE_CONT_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | PERCENTILE_DISC_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_disc(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +order_by_single_element_list: + ORDER_SYM BY order_ident order_dir + { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + ; + + window_name: ident { @@ -10844,7 +11027,7 @@ variable_aux: thd->parse_error(); MYSQL_YYABORT; } - if (!($$= get_system_var(thd, $2, $3, $4))) + if (!($$= get_system_var(thd, $2, &$3, &$4))) MYSQL_YYABORT; if (!((Item_func_get_system_var*) $$)->is_written_to_binlog()) Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE); @@ -10878,6 +11061,48 @@ gorder_list: { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } ; +opt_glimit_clause: + /* empty */ { $$ = 0; } + | glimit_clause { $$ = 1; } + ; + +glimit_clause_init: + LIMIT{} + ; + +glimit_clause: + glimit_clause_init glimit_options + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +glimit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + + + in_sum_expr: opt_all { @@ -11357,9 +11582,9 @@ select_derived_union: } } union_list_derived_part2 - | derived_query_specification opt_select_lock_type - | derived_query_specification order_or_limit opt_select_lock_type - | derived_query_specification opt_select_lock_type union_list_derived + | derived_simple_table opt_select_lock_type + | derived_simple_table order_or_limit opt_select_lock_type + | derived_simple_table opt_select_lock_type union_list_derived ; union_list_derived_part2: @@ -11414,6 +11639,10 @@ select_derived: } ; +derived_simple_table: + derived_query_specification { $$= $1; } + | derived_table_value_constructor { $$= $1; } + ; /* Similar to query_specification, but for derived tables. Example: the inner parenthesized SELECT in this query: @@ -11428,6 +11657,41 @@ derived_query_specification: } ; +derived_table_value_constructor: + VALUES + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + } + values_list + { + LEX *lex= Lex; + lex->derived_tables|= DERIVED_SUBQUERY; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + thd->parse_error(); + MYSQL_YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1, NULL)) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->linkage= DERIVED_TABLE_TYPE; + + if (!(lex->current_select->tvc= + new (lex->thd->mem_root) table_value_constr(lex->many_values, + lex->current_select, + lex->current_select->options))) + MYSQL_YYABORT; + lex->many_values.empty(); + $$= NULL; + } + ; + + select_derived2: { LEX *lex= Lex; @@ -12338,9 +12602,9 @@ drop: YYPS->m_lock_type= TL_UNLOCK; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_list opt_restrict + table_list opt_lock_wait_timeout opt_restrict {} - | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {} + | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout { LEX *lex=Lex; Alter_drop *ad= (new (thd->mem_root) @@ -12733,7 +12997,14 @@ expr_or_default: opt_insert_update: /* empty */ | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } - KEY_SYM UPDATE_SYM insert_update_list + KEY_SYM UPDATE_SYM + { + Select->parsing_place= IN_UPDATE_ON_DUP_KEY; + } + insert_update_list + { + Select->parsing_place= NO_MATTER; + } ; /* Update rows in a table */ @@ -12925,7 +13196,7 @@ truncate: YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_name + table_name opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -13148,9 +13419,15 @@ show_param: lex->sql_command= SQLCOM_SHOW_PRIVILEGES; } | COUNT_SYM '(' '*' ')' WARNINGS - { (void) create_select_for_variable("warning_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("warning_count")}; + (void) create_select_for_variable(thd, &var); + } | COUNT_SYM '(' '*' ')' ERRORS - { (void) create_select_for_variable("error_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("error_count")}; + (void) create_select_for_variable(thd, &var); + } | WARNINGS opt_limit_clause { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause @@ -14707,6 +14984,7 @@ keyword_alias: | UNICODE_SYM {} | UNINSTALL_SYM {} | UNBOUNDED_SYM {} + | WITHIN {} | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} @@ -14890,6 +15168,7 @@ keyword_sp_not_data_type: | GOTO_SYM {} | HASH_SYM {} | HARD_SYM {} + | INVISIBLE_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | ID_SYM {} @@ -14971,6 +15250,7 @@ keyword_sp_not_data_type: | NOMINVALUE_SYM {} | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} + | NOWAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} | NOTFOUND_SYM {} @@ -15540,10 +15820,27 @@ lock: my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK")); lex->sql_command= SQLCOM_LOCK_TABLES; } - table_lock_list + table_lock_list opt_lock_wait_timeout {} ; +opt_lock_wait_timeout: + /* empty */ + {} + | WAIT_SYM ulong_num + { + if (set_statement_var_if_exists(thd, C_STRING_WITH_LEN("lock_wait_timeout"), $2) || + set_statement_var_if_exists(thd, C_STRING_WITH_LEN("innodb_lock_wait_timeout"), $2)) + MYSQL_YYABORT; + } + | NOWAIT_SYM + { + if (set_statement_var_if_exists(thd, C_STRING_WITH_LEN("lock_wait_timeout"), 0) || + set_statement_var_if_exists(thd, C_STRING_WITH_LEN("innodb_lock_wait_timeout"), 0)) + MYSQL_YYABORT; + } + ; + table_or_tables: TABLE_SYM { } | TABLES { } @@ -16353,6 +16650,31 @@ union_option: | ALL { $$=0; } ; +simple_table: + query_specification { $$= $1; } + | table_value_constructor { $$= $1; } + ; + +table_value_constructor: + VALUES + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + } + values_list + { + LEX *lex=Lex; + $$= lex->current_select; + mysql_init_select(Lex); + if (!($$->tvc= + new (lex->thd->mem_root) table_value_constr(lex->many_values, $$, $$->options))) + MYSQL_YYABORT; + lex->many_values.empty(); + } + ; + /* Corresponds to the SQL Standard <query specification> ::= @@ -16370,12 +16692,12 @@ query_specification: ; query_term_union_not_ready: - query_specification order_or_limit opt_select_lock_type { $$= $1; } + simple_table order_or_limit opt_select_lock_type { $$= $1; } | '(' select_paren_derived ')' union_order_or_limit { $$= $2; } ; query_term_union_ready: - query_specification opt_select_lock_type { $$= $1; } + simple_table opt_select_lock_type { $$= $1; } | '(' select_paren_derived ')' { $$= $2; } ; @@ -16586,6 +16908,9 @@ view_select: */ query_expression_body_view: SELECT_SYM select_options_and_item_list select_init3_view + | table_value_constructor + | table_value_constructor union_order_or_limit + | table_value_constructor union_list_view | '(' select_paren_view ')' | '(' select_paren_view ')' union_order_or_limit | '(' select_paren_view ')' union_list_view diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 961624b2ae1..2dcfa018dce 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -33,7 +33,7 @@ #define Lex (thd->lex) #define Select Lex->current_select -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" /* comp_*_creator */ #include "sql_table.h" /* primary_key_name */ @@ -537,6 +537,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token HEX_NUM %token HEX_STRING %token HIGH_PRIORITY +%token INVISIBLE_SYM %token HOST_SYM %token HOSTS_SYM %token HOUR_MICROSECOND_SYM @@ -646,6 +647,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM %token MAXVALUE_SYM /* SQL-2003-N */ +%token MEDIAN_SYM %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -695,6 +697,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOMAXVALUE_SYM %token NOMINVALUE_SYM %token NO_WAIT_SYM +%token NOWAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM %token NULL_SYM /* SQL-2003-R */ @@ -737,6 +740,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARTITIONING_SYM %token PASSWORD_SYM %token PERCENT_RANK_SYM +%token PERCENTILE_CONT_SYM +%token PERCENTILE_DISC_SYM %token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM @@ -963,6 +968,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UTC_TIMESTAMP_SYM %token UTC_TIME_SYM %token VALUES /* SQL-2003-R */ +%token VALUES_IN_SYM +%token VALUES_LESS_SYM %token VALUE_SYM /* SQL-2003-R */ %token VARBINARY %token VARCHAR /* SQL-2003-R */ @@ -983,6 +990,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WINDOW_SYM %token WHILE_SYM %token WITH /* SQL-2003-R */ +%token WITHIN %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ @@ -1085,7 +1093,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <num> order_dir lock_option udf_type opt_local opt_no_write_to_binlog - opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not select_derived_init transaction_access_mode_types @@ -1151,6 +1159,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); window_func_expr window_func simple_window_func + inverse_distribution_function + percentile_function + inverse_distribution_function_def explicit_cursor_attr function_call_keyword function_call_nonkeyword @@ -1332,7 +1343,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); parse_vcol_expr vcol_opt_specifier vcol_opt_attribute vcol_opt_attribute_list vcol_attribute opt_serial_attribute opt_serial_attribute_list serial_attribute - explainable_command + explainable_command opt_lock_wait_timeout set_assign sf_tail_standalone sp_tail_standalone @@ -1994,7 +2005,7 @@ create: if (Lex->add_create_index($2, &$5, $6, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' normal_key_options + '(' key_list ')' opt_lock_wait_timeout normal_key_options opt_index_lock_algorithm { } | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2004,7 +2015,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' fulltext_key_options + '(' key_list ')' opt_lock_wait_timeout fulltext_key_options opt_index_lock_algorithm { } | create_or_replace spatial INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2014,7 +2025,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' spatial_key_options + '(' key_list ')' opt_lock_wait_timeout spatial_key_options opt_index_lock_algorithm { } | create_or_replace DATABASE opt_if_not_exists ident { @@ -3621,7 +3632,7 @@ opt_parenthesized_cursor_actual_parameters: sp_proc_stmt_open: OPEN_SYM ident opt_parenthesized_cursor_actual_parameters { - if (Lex->sp_open_cursor(thd, &$2, $3)) + if (Lex->sp_open_cursor(thd, &$2, $3)) MYSQL_YYABORT; } ; @@ -4819,8 +4830,15 @@ part_type_def: { Lex->part_info->part_type= RANGE_PARTITION; } | RANGE_SYM part_column_list { Lex->part_info->part_type= RANGE_PARTITION; } - | LIST_SYM part_func - { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM + { + Select->parsing_place= IN_PART_FUNC; + } + part_func + { + Lex->part_info->part_type= LIST_PARTITION; + Select->parsing_place= NO_MATTER; + } | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } ; @@ -5070,7 +5088,7 @@ opt_part_values: else part_info->part_type= HASH_PARTITION; } - | VALUES LESS_SYM THAN_SYM + | VALUES_LESS_SYM THAN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -5084,7 +5102,7 @@ opt_part_values: part_info->part_type= RANGE_PARTITION; } part_func_max {} - | VALUES IN_SYM + | VALUES_IN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -6108,6 +6126,10 @@ vcol_attribute: lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + | INVISIBLE_SYM + { + Lex->last_field->field_visibility= USER_DEFINED_INVISIBLE; + } ; parse_vcol_expr: @@ -7130,7 +7152,7 @@ alter: Lex->create_info.storage_media= HA_SM_DEFAULT; DBUG_ASSERT(!Lex->m_sql_cmd); } - alter_options TABLE_SYM table_ident + alter_options TABLE_SYM table_ident opt_lock_wait_timeout { if (!Lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, @@ -8245,7 +8267,7 @@ optimize: /* Will be overridden during execution. */ YYPS->m_lock_type= TL_UNLOCK; } - table_list + table_list opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -8295,13 +8317,13 @@ table_to_table_list: ; table_to_table: - table_ident TO_SYM table_ident + table_ident opt_lock_wait_timeout TO_SYM table_ident { LEX *lex=Lex; SELECT_LEX *sl= lex->current_select; if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE) || - !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING, + !sl->add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } @@ -8708,14 +8730,14 @@ select_option: opt_select_lock_type: /* empty */ - | FOR_SYM UPDATE_SYM + | FOR_SYM UPDATE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_WRITE; lex->current_select->set_lock_for_tables(TL_WRITE); lex->safe_to_cache_query=0; } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; @@ -9376,6 +9398,7 @@ column_default_non_parenthesized_expr: | variable | sum_expr | window_func_expr + | inverse_distribution_function | ROW_SYM '(' expr ',' expr_list ')' { $5->push_front($3, thd->mem_root); @@ -9477,8 +9500,9 @@ column_default_non_parenthesized_expr: $3); if ($$ == NULL) MYSQL_YYABORT; + Lex->default_used= TRUE; } - | VALUES '(' simple_ident_nospvar ')' + | VALUE_SYM '(' simple_ident_nospvar ')' { $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(), $3); @@ -10553,16 +10577,22 @@ sum_expr: | GROUP_CONCAT_SYM '(' opt_distinct { Select->in_sum_expr++; } expr_list opt_gorder_clause - opt_gconcat_separator + opt_gconcat_separator opt_glimit_clause ')' { SELECT_LEX *sel= Select; sel->in_sum_expr--; $$= new (thd->mem_root) - Item_func_group_concat(thd, Lex->current_context(), $3, $5, - sel->gorder_list, $7); + Item_func_group_concat(thd, Lex->current_context(), + $3, $5, + sel->gorder_list, $7, $8, + sel->select_limit, + sel->offset_limit); if ($$ == NULL) MYSQL_YYABORT; + sel->select_limit= NULL; + sel->offset_limit= NULL; + sel->explicit_limit= 0; $5->empty(); sel->gorder_list.empty(); } @@ -10704,6 +10734,67 @@ simple_window_func: MYSQL_YYABORT; } ; +inverse_distribution_function: + percentile_function OVER_SYM + '(' opt_window_partition_clause ')' + { + LEX *lex= Lex; + if (Select->add_window_spec(thd, lex->win_ref, + Select->group_list, + Select->order_list, + NULL)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, + thd->lex->win_spec); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + ; + +percentile_function: + inverse_distribution_function_def WITHIN GROUP_SYM '(' + { Select->prepare_add_window_spec(thd); } + order_by_single_element_list ')' + { + $$= $1; + } + | MEDIAN_SYM '(' expr ')' + { + Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3, + thd->charset()); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + if (add_order_to_list(thd, $3,FALSE)) MYSQL_YYABORT; + + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, args); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +inverse_distribution_function_def: + PERCENTILE_CONT_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | PERCENTILE_DISC_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_disc(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +order_by_single_element_list: + ORDER_SYM BY order_ident order_dir + { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + ; window_name: ident @@ -10753,7 +10844,7 @@ variable_aux: thd->parse_error(); MYSQL_YYABORT; } - if (!($$= get_system_var(thd, $2, $3, $4))) + if (!($$= get_system_var(thd, $2, &$3, &$4))) MYSQL_YYABORT; if (!((Item_func_get_system_var*) $$)->is_written_to_binlog()) Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE); @@ -10787,6 +10878,46 @@ gorder_list: { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } ; +opt_glimit_clause: + /* empty */ { $$ = 0; } + | glimit_clause { $$ = 1; } + ; + +glimit_clause_init: + LIMIT{} + ; + +glimit_clause: + glimit_clause_init glimit_options + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +glimit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + in_sum_expr: opt_all { @@ -12273,9 +12404,9 @@ drop: YYPS->m_lock_type= TL_UNLOCK; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_list opt_restrict + table_list opt_lock_wait_timeout opt_restrict {} - | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {} + | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout { LEX *lex=Lex; Alter_drop *ad= (new (thd->mem_root) @@ -12668,7 +12799,14 @@ expr_or_default: opt_insert_update: /* empty */ | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } - KEY_SYM UPDATE_SYM insert_update_list + KEY_SYM UPDATE_SYM + { + Select->parsing_place= IN_UPDATE_ON_DUP_KEY; + } + insert_update_list + { + Select->parsing_place= NO_MATTER; + } ; /* Update rows in a table */ @@ -12860,7 +12998,7 @@ truncate: YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_name + table_name opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -13090,9 +13228,15 @@ show_param: lex->sql_command= SQLCOM_SHOW_PRIVILEGES; } | COUNT_SYM '(' '*' ')' WARNINGS - { (void) create_select_for_variable("warning_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("warning_count")}; + (void) create_select_for_variable(thd, &var); + } | COUNT_SYM '(' '*' ')' ERRORS - { (void) create_select_for_variable("error_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("error_count")}; + (void) create_select_for_variable(thd, &var); + } | WARNINGS opt_limit_clause { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause @@ -14684,6 +14828,7 @@ keyword_directly_assignable: | UNICODE_SYM {} | UNINSTALL_SYM {} | UNBOUNDED_SYM {} + | WITHIN {} | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} @@ -14907,6 +15052,7 @@ keyword_sp_not_data_type: | GLOBAL_SYM {} | HASH_SYM {} | HARD_SYM {} + | INVISIBLE_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | ID_SYM {} @@ -14988,6 +15134,7 @@ keyword_sp_not_data_type: | NOMINVALUE_SYM {} | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} + | NOWAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} | NOTFOUND_SYM {} @@ -15039,7 +15186,7 @@ keyword_sp_not_data_type: | RESUME_SYM {} | RETURNED_SQLSTATE_SYM {} | RETURNS_SYM {} - | REUSE_SYM {} /* Oracle-R */ + | REUSE_SYM {} | REVERSE_SYM {} | ROLE_SYM {} | ROLLUP_SYM {} @@ -15610,10 +15757,27 @@ lock: my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK")); lex->sql_command= SQLCOM_LOCK_TABLES; } - table_lock_list + table_lock_list opt_lock_wait_timeout {} ; +opt_lock_wait_timeout: + /* empty */ + {} + | WAIT_SYM ulong_num + { + if (set_statement_var_if_exists(thd, C_STRING_WITH_LEN("lock_wait_timeout"), $2) || + set_statement_var_if_exists(thd, C_STRING_WITH_LEN("innodb_lock_wait_timeout"), $2)) + MYSQL_YYABORT; + } + | NOWAIT_SYM + { + if (set_statement_var_if_exists(thd, C_STRING_WITH_LEN("lock_wait_timeout"), 0) || + set_statement_var_if_exists(thd, C_STRING_WITH_LEN("innodb_lock_wait_timeout"), 0)) + MYSQL_YYABORT; + } + ; + table_or_tables: TABLE_SYM { } | TABLES { } diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 1c0b672fbcc..45a0f8f6558 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -15,7 +15,7 @@ /* Some useful string utility functions used by the MySQL server */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "strfunc.h" diff --git a/sql/strfunc.h b/sql/strfunc.h index 7f3021e1a6d..1aba0bff422 100644 --- a/sql/strfunc.h +++ b/sql/strfunc.h @@ -16,8 +16,6 @@ #ifndef STRFUNC_INCLUDED #define STRFUNC_INCLUDED -#include "my_global.h" /* ulonglong, uint */ - typedef struct st_typelib TYPELIB; ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs, diff --git a/sql/structs.h b/sql/structs.h index 97702e5727b..793be462b26 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -109,9 +109,9 @@ typedef struct st_key { pk2 is explicitly present in idx1, it is not in the extension, so ext_key_part_map.is_set(1) == false */ + LEX_CSTRING name; key_part_map ext_key_part_map; uint block_size; - uint name_length; enum ha_key_alg algorithm; /* The flag is on if statistical data for the index prefixes @@ -128,7 +128,6 @@ typedef struct st_key { LEX_CSTRING *parser_name; /* Fulltext [pre]parser name */ }; KEY_PART_INFO *key_part; - const char *name; /* Name of key */ /* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */ uchar *cache_name; /* diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index a517ab0965e..0e5192d72ad 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -31,7 +31,7 @@ (for example in storage/myisam/ha_myisam.cc) ! */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" #include "sql_priv.h" #include "sql_class.h" // set_var.h: THD #include "sys_vars.ic" @@ -51,7 +51,6 @@ #include "sql_base.h" // close_cached_tables #include "hostname.h" // host_cache_size #include <myisam.h> -#include "log_slow.h" #include "debug_sync.h" // DEBUG_SYNC #include "sql_show.h" @@ -63,6 +62,8 @@ #include "sql_repl.h" #include "opt_range.h" #include "rpl_parallel.h" +#include "semisync_master.h" +#include "semisync_slave.h" #include <ssl_compat.h> /* @@ -384,6 +385,11 @@ static Sys_var_charptr Sys_basedir( READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'), IN_FS_CHARSET, DEFAULT(0)); +static Sys_var_charptr Sys_my_bind_addr( + "bind_address", "IP address to bind to.", + READ_ONLY GLOBAL_VAR(my_bind_addr_str), CMD_LINE(REQUIRED_ARG), + IN_FS_CHARSET, DEFAULT(0)); + static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " "updates to transactional engines for the binary log. " @@ -393,6 +399,13 @@ static Sys_var_ulonglong Sys_binlog_cache_size( CMD_LINE(REQUIRED_ARG), VALID_RANGE(IO_SIZE, SIZE_T_MAX), DEFAULT(32768), BLOCK_SIZE(IO_SIZE)); +static Sys_var_ulonglong Sys_binlog_file_cache_size( + "binlog_file_cache_size", + "The size of file cache for the binary log", + GLOBAL_VAR(binlog_file_cache_size), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(IO_SIZE*2, SIZE_T_MAX), DEFAULT(IO_SIZE*4), BLOCK_SIZE(IO_SIZE)); + static Sys_var_ulonglong Sys_binlog_stmt_cache_size( "binlog_stmt_cache_size", "The size of the statement cache for " "updates to non-transactional engines for the binary log. " @@ -563,8 +576,7 @@ static Sys_var_mybool Sys_explicit_defaults_for_timestamp( "explicit_defaults_for_timestamp", "This option causes CREATE TABLE to create all TIMESTAMP columns " "as NULL with DEFAULT NULL attribute, Without this option, " - "TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses. " - "The old behavior is deprecated.", + "TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses.", READ_ONLY GLOBAL_VAR(opt_explicit_defaults_for_timestamp), CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG); @@ -765,6 +777,53 @@ static Sys_var_struct Sys_collation_server( offsetof(CHARSET_INFO, name), DEFAULT(&default_charset_info), NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_collation_not_null)); +static Sys_var_uint Sys_column_compression_threshold( + "column_compression_threshold", + "Minimum column data length eligible for compression", + SESSION_VAR(column_compression_threshold), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, UINT_MAX), DEFAULT(100), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_column_compression_zlib_level( + "column_compression_zlib_level", + "zlib compression level (1 gives best speed, 9 gives best compression)", + SESSION_VAR(column_compression_zlib_level), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 9), DEFAULT(6), BLOCK_SIZE(1)); + +/* + Note that names must correspond to zlib strategy definition. So that we can + pass column_compression_zlib_strategy directly to deflateInit2(). +*/ +static const char *column_compression_zlib_strategy_names[]= +{ "DEFAULT_STRATEGY", "FILTERED", "HUFFMAN_ONLY", "RLE", "FIXED", 0 }; + +static Sys_var_enum Sys_column_compression_zlib_strategy( + "column_compression_zlib_strategy", + "The strategy parameter is used to tune the compression algorithm. Use " + "the value DEFAULT_STRATEGY for normal data, FILTERED for data produced " + "by a filter (or predictor), HUFFMAN_ONLY to force Huffman encoding " + "only (no string match), or RLE to limit match distances to one " + "(run-length encoding). Filtered data consists mostly of small values " + "with a somewhat random distribution. In this case, the compression " + "algorithm is tuned to compress them better. The effect of FILTERED is " + "to force more Huffman coding and less string matching; it is somewhat " + "intermediate between DEFAULT_STRATEGY and HUFFMAN_ONLY. RLE is " + "designed to be almost as fast as HUFFMAN_ONLY, but give better " + "compression for PNG image data. The strategy parameter only affects " + "the compression ratio but not the correctness of the compressed output " + "even if it is not set appropriately. FIXED prevents the use of dynamic " + "Huffman codes, allowing for a simpler decoder for special " + "applications.", + SESSION_VAR(column_compression_zlib_strategy), CMD_LINE(REQUIRED_ARG), + column_compression_zlib_strategy_names, DEFAULT(0)); + +static Sys_var_mybool Sys_column_compression_zlib_wrap( + "column_compression_zlib_wrap", + "Generate zlib header and trailer and compute adler32 check value. " + "It can be used with storage engines that don't provide data integrity " + "verification to detect data corruption.", + SESSION_VAR(column_compression_zlib_wrap), CMD_LINE(OPT_ARG), + DEFAULT(FALSE)); + static const char *concurrent_insert_names[]= {"NEVER", "AUTO", "ALWAYS", 0}; static Sys_var_enum Sys_concurrent_insert( "concurrent_insert", "Use concurrent insert with MyISAM", @@ -1166,7 +1225,7 @@ static Sys_var_ulong Sys_lock_wait_timeout( "lock_wait_timeout", "Timeout in seconds to wait for a lock before returning an error.", SESSION_VAR(lock_wait_timeout), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(24 * 60 * 60), BLOCK_SIZE(1)); + VALID_RANGE(0, LONG_TIMEOUT), DEFAULT(24 * 60 * 60), BLOCK_SIZE(1)); #ifdef HAVE_MLOCKALL static Sys_var_mybool Sys_locked_in_memory( @@ -1213,25 +1272,28 @@ static Sys_var_charptr Sys_log_error( CMD_LINE(OPT_ARG, OPT_LOG_ERROR), IN_FS_CHARSET, DEFAULT(disabled_my_option)); -static Sys_var_mybool Sys_log_queries_not_using_indexes( +static Sys_var_bit Sys_log_queries_not_using_indexes( "log_queries_not_using_indexes", "Log queries that are executed without benefit of any index to the " - "slow log if it is open", - GLOBAL_VAR(opt_log_queries_not_using_indexes), - CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + "slow log if it is open. Same as log_slow_filter='not_using_index'", + SESSION_VAR(log_slow_filter), CMD_LINE(OPT_ARG), QPLAN_NOT_USING_INDEX, + DEFAULT(FALSE)); -static Sys_var_mybool Sys_log_slow_admin_statements( +static Sys_var_bit Sys_log_slow_admin_statements( "log_slow_admin_statements", - "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements to " - "the slow log if it is open.", - GLOBAL_VAR(opt_log_slow_admin_statements), - CMD_LINE(OPT_ARG), DEFAULT(TRUE)); + "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements " + "to the slow log if it is open. Resets or sets the option 'admin' in " + "log_slow_disabled_statements", + SESSION_VAR(log_slow_disabled_statements), + CMD_LINE(OPT_ARG), REVERSE(LOG_SLOW_DISABLE_ADMIN), DEFAULT(TRUE)); -static Sys_var_mybool Sys_log_slow_slave_statements( +static Sys_var_bit Sys_log_slow_slave_statements( "log_slow_slave_statements", - "Log slow statements executed by slave thread to the slow log if it is open.", - GLOBAL_VAR(opt_log_slow_slave_statements), - CMD_LINE(OPT_ARG), DEFAULT(TRUE)); + "Log slow statements executed by slave thread to the slow log if it is " + "open. Resets or sets the option 'slave' in " + "log_slow_disabled_statements", + SESSION_VAR(log_slow_disabled_statements), + CMD_LINE(OPT_ARG), REVERSE(LOG_SLOW_DISABLE_SLAVE), DEFAULT(TRUE)); static Sys_var_ulong Sys_log_warnings( "log_warnings", @@ -2395,7 +2457,7 @@ export const char *optimizer_switch_names[]= "exists_to_in", "orderby_uses_equalities", "condition_pushdown_for_derived", - "split_grouping_derived", + "split_materialized", "default", NullS }; @@ -2980,8 +3042,174 @@ static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip "the slave).", GLOBAL_VAR(opt_replicate_events_marked_for_skip), CMD_LINE(REQUIRED_ARG), replicate_events_marked_for_skip_names, DEFAULT(RPL_SKIP_REPLICATE)); -#endif +/* new options for semisync */ + +static bool fix_rpl_semi_sync_master_enabled(sys_var *self, THD *thd, + enum_var_type type) +{ + if (rpl_semi_sync_master_enabled) + { + if (repl_semisync_master.enable_master() != 0) + rpl_semi_sync_master_enabled= false; + else if (ack_receiver.start()) + { + repl_semisync_master.disable_master(); + rpl_semi_sync_master_enabled= false; + } + } + else + { + if (repl_semisync_master.disable_master() != 0) + rpl_semi_sync_master_enabled= true; + if (!rpl_semi_sync_master_enabled) + ack_receiver.stop(); + } + return false; +} + +static bool fix_rpl_semi_sync_master_timeout(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.set_wait_timeout(rpl_semi_sync_master_timeout); + return false; +} + +static bool fix_rpl_semi_sync_master_trace_level(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.set_trace_level(rpl_semi_sync_master_trace_level); + ack_receiver.set_trace_level(rpl_semi_sync_master_trace_level); + return false; +} + +static bool fix_rpl_semi_sync_master_wait_point(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.set_wait_point(rpl_semi_sync_master_wait_point); + return false; +} + +static bool fix_rpl_semi_sync_master_wait_no_slave(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.check_and_switch(); + return false; +} + +static Sys_var_mybool Sys_semisync_master_enabled( + "rpl_semi_sync_master_enabled", + "Enable semi-synchronous replication master (disabled by default).", + GLOBAL_VAR(rpl_semi_sync_master_enabled), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_enabled)); + +static Sys_var_ulong Sys_semisync_master_timeout( + "rpl_semi_sync_master_timeout", + "The timeout value (in ms) for semi-synchronous replication in the " + "master", + GLOBAL_VAR(rpl_semi_sync_master_timeout), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,~0L),DEFAULT(10000),BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_timeout)); + +static Sys_var_mybool Sys_semisync_master_wait_no_slave( + "rpl_semi_sync_master_wait_no_slave", + "Wait until timeout when no semi-synchronous replication slave " + "available (enabled by default).", + GLOBAL_VAR(rpl_semi_sync_master_wait_no_slave), + CMD_LINE(OPT_ARG), DEFAULT(TRUE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_wait_no_slave)); + +static Sys_var_ulong Sys_semisync_master_trace_level( + "rpl_semi_sync_master_trace_level", + "The tracing level for semi-sync replication.", + GLOBAL_VAR(rpl_semi_sync_master_trace_level), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,~0L),DEFAULT(32),BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_trace_level)); + +static const char *repl_semisync_wait_point[]= +{"AFTER_SYNC", "AFTER_COMMIT", NullS}; + +static Sys_var_enum Sys_semisync_master_wait_point( + "rpl_semi_sync_master_wait_point", + "Should transaction wait for semi-sync ack after having synced binlog, " + "or after having committed in storage engine.", + GLOBAL_VAR(rpl_semi_sync_master_wait_point), CMD_LINE(REQUIRED_ARG), + repl_semisync_wait_point, DEFAULT(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG,ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_wait_point)); + +static bool fix_rpl_semi_sync_slave_enabled(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave.set_slave_enabled(rpl_semi_sync_slave_enabled != 0); + return false; +} + +static bool fix_rpl_semi_sync_slave_trace_level(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave.set_trace_level(rpl_semi_sync_slave_trace_level); + return false; +} + +static bool fix_rpl_semi_sync_slave_delay_master(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave.set_delay_master(rpl_semi_sync_slave_delay_master); + return false; +} + +static bool fix_rpl_semi_sync_slave_kill_conn_timeout(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave. + set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout); + return false; +} + +static Sys_var_mybool Sys_semisync_slave_enabled( + "rpl_semi_sync_slave_enabled", + "Enable semi-synchronous replication slave (disabled by default).", + GLOBAL_VAR(rpl_semi_sync_slave_enabled), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_enabled)); + +static Sys_var_ulong Sys_semisync_slave_trace_level( + "rpl_semi_sync_slave_trace_level", + "The tracing level for semi-sync replication.", + GLOBAL_VAR(rpl_semi_sync_slave_trace_level), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,~0L),DEFAULT(32),BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_trace_level)); + +static Sys_var_mybool Sys_semisync_slave_delay_master( + "rpl_semi_sync_slave_delay_master", + "Only write master info file when ack is needed.", + GLOBAL_VAR(rpl_semi_sync_slave_delay_master), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_delay_master)); + +static Sys_var_uint Sys_semisync_slave_kill_conn_timeout( + "rpl_semi_sync_slave_kill_conn_timeout", + "Timeout for the mysql connection used to kill the slave io_thread's " + "connection on master. This timeout comes into play when stop slave " + "is executed.", + GLOBAL_VAR(rpl_semi_sync_slave_kill_conn_timeout), + CMD_LINE(OPT_ARG), + VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_kill_conn_timeout)); +#endif /* HAVE_REPLICATION */ static Sys_var_ulong Sys_slow_launch_time( "slow_launch_time", @@ -3532,6 +3760,27 @@ static Sys_var_ulong Sys_net_wait_timeout( VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1)); +static Sys_var_uint Sys_idle_transaction_timeout( + "idle_transaction_timeout", + "The number of seconds the server waits for idle transaction", + SESSION_VAR(idle_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_idle_readonly_transaction_timeout( + "idle_readonly_transaction_timeout", + "The number of seconds the server waits for read-only idle transaction", + SESSION_VAR(idle_readonly_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_idle_write_transaction_timeout( + "idle_write_transaction_timeout", + "The number of seconds the server waits for write idle transaction", + SESSION_VAR(idle_write_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + static Sys_var_plugin Sys_default_storage_engine( "default_storage_engine", "The default storage engine for new tables", SESSION_VAR(table_plugin), NO_CMD_LINE, @@ -3556,6 +3805,45 @@ static Sys_var_plugin Sys_enforce_storage_engine( NO_CMD_LINE, MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super)); + +#ifdef HAVE_REPLICATION +/* + Check + 1. Value for gtid_pos_auto_engines is not NULL. + 2. No slave SQL thread is running. +*/ +static bool +check_gtid_pos_auto_engines(sys_var *self, THD *thd, set_var *var) +{ + bool running; + bool err= false; + + DBUG_ASSERT(var->type == OPT_GLOBAL); + if (var->value && var->value->is_null()) + err= true; + else + { + running= give_error_if_slave_running(false); + if (running) + err= true; + } + return err; +} + + +static Sys_var_pluginlist Sys_gtid_pos_auto_engines( + "gtid_pos_auto_engines", + "List of engines for which to automatically create a " + "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine " + "is replicated. This can be used to avoid introducing cross-engine " + "transactions, if engines are used different from that used by table " + "mysql.gtid_slave_pos", + GLOBAL_VAR(opt_gtid_pos_auto_plugins), NO_CMD_LINE, + DEFAULT(>id_pos_auto_engines), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_gtid_pos_auto_engines)); +#endif + + #if defined(ENABLED_DEBUG_SYNC) /* Variable can be set for the session only. @@ -4111,6 +4399,17 @@ static Sys_var_charptr Sys_license( READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(STRINGIFY_ARG(LICENSE))); +char *my_proxy_protocol_networks; +static Sys_var_charptr Sys_proxy_protocol_networks( + "proxy_protocol_networks", "Enable proxy protocol for these source " + "networks. The syntax is a comma separated list of IPv4 and IPv6 " + "networks. If the network doesn't contain mask, it is considered to be " + "a single host. \"*\" represents all networks and must the only " + "directive on the line. String \"localhost\" represents non-TCP " + "local connections (Unix domain socket, Windows named pipe or shared memory).", + READ_ONLY GLOBAL_VAR(my_proxy_protocol_networks), + CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT("")); + static bool check_log_path(sys_var *self, THD *thd, set_var *var) { if (!var->value) @@ -4347,6 +4646,7 @@ static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type) return res; } + static bool check_not_empty_set(sys_var *self, THD *thd, set_var *var) { return var->save_result.ulonglong_value == 0; @@ -4720,6 +5020,14 @@ static Sys_var_ulonglong Sys_read_binlog_speed_limit( GLOBAL_VAR(opt_read_binlog_speed_limit), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1)); +static Sys_var_charptr Sys_slave_transaction_retry_errors( + "slave_transaction_retry_errors", "Tells the slave thread to retry " + "transaction for replication when a query event returns an error from " + "the provided list. Deadlock and elapsed lock wait timeout errors are " + "automatically added to this list", + READ_ONLY GLOBAL_VAR(opt_slave_transaction_retry_errors), CMD_LINE(REQUIRED_ARG), + IN_SYSTEM_CHARSET, DEFAULT(0)); + static Sys_var_ulonglong Sys_relay_log_space_limit( "relay_log_space_limit", "Maximum space to use for all relay logs", READ_ONLY GLOBAL_VAR(relay_log_space_limit), CMD_LINE(REQUIRED_ARG), @@ -4754,10 +5062,19 @@ static Sys_var_uint Sys_sync_masterinfo_period( #ifdef HAVE_REPLICATION static Sys_var_ulong Sys_slave_trans_retries( "slave_transaction_retries", "Number of times the slave SQL " - "thread will retry a transaction in case it failed with a deadlock " - "or elapsed lock wait timeout, before giving up and stopping", + "thread will retry a transaction in case it failed with a deadlock, " + "elapsed lock wait timeout or listed in " + "slave_transaction_retry_errors, before giving up and stopping", GLOBAL_VAR(slave_trans_retries), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX), DEFAULT(10), BLOCK_SIZE(1)); + +static Sys_var_ulong Sys_slave_trans_retry_interval( + "slave_transaction_retry_interval", "Interval of the slave SQL " + "thread will retry a transaction in case it failed with a deadlock " + "or elapsed lock wait timeout or listed in " + "slave_transaction_retry_errors", + GLOBAL_VAR(slave_trans_retry_interval), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 3600), DEFAULT(0), BLOCK_SIZE(1)); #endif static bool check_locale(sys_var *self, THD *thd, set_var *var) @@ -5159,6 +5476,38 @@ static Sys_var_ulong Sys_host_cache_size( NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(fix_host_cache_size)); +vio_keepalive_opts opt_vio_keepalive; + +static Sys_var_int Sys_keepalive_time( + "tcp_keepalive_time", + "Timeout, in milliseconds, with no activity until the first TCP keep-alive packet is sent." + "If set to 0, system dependent default is used.", + AUTO_SET GLOBAL_VAR(opt_vio_keepalive.idle), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), + DEFAULT(0), + BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + +static Sys_var_int Sys_keepalive_interval( + "tcp_keepalive_interval", + "The interval, in seconds, between when successive keep-alive packets are sent if no acknowledgement is received." + "If set to 0, system dependent default is used.", + AUTO_SET GLOBAL_VAR(opt_vio_keepalive.interval), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), + DEFAULT(0), + BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + +static Sys_var_int Sys_keepalive_probes( + "tcp_keepalive_probes", + "The number of unacknowledged probes to send before considering the connection dead and notifying the application layer." + "If set to 0, system dependent default is used.", + AUTO_SET GLOBAL_VAR(opt_vio_keepalive.probes), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), + DEFAULT(0), + BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + static Sys_var_charptr Sys_ignore_db_dirs( "ignore_db_dirs", "Specifies a directory to add to the ignore list when collecting " @@ -5182,7 +5531,9 @@ static Sys_var_enum Sys_plugin_maturity( "The lowest desirable plugin maturity. " "Plugins less mature than that will not be installed or loaded", READ_ONLY GLOBAL_VAR(plugin_maturity), CMD_LINE(REQUIRED_ARG), - plugin_maturity_names, DEFAULT(MariaDB_PLUGIN_MATURITY_UNKNOWN)); + plugin_maturity_names, + DEFAULT(SERVER_MATURITY_LEVEL > 0 ? + SERVER_MATURITY_LEVEL - 1 : SERVER_MATURITY_LEVEL)); static Sys_var_ulong Sys_deadlock_search_depth_short( "deadlock_search_depth_short", @@ -5238,15 +5589,42 @@ static Sys_var_keycache Sys_key_cache_segments( ON_UPDATE(repartition_keycache)); static const char *log_slow_filter_names[]= -{ "admin", "filesort", "filesort_on_disk", "full_join", "full_scan", - "query_cache", "query_cache_miss", "tmp_table", "tmp_table_on_disk", 0 +{ + "admin", "filesort", "filesort_on_disk", "filesort_priority_queue", + "full_join", "full_scan", "not_using_index", "query_cache", + "query_cache_miss", "tmp_table", "tmp_table_on_disk", 0 }; + + static Sys_var_set Sys_log_slow_filter( "log_slow_filter", - "Log only certain types of queries", + "Log only certain types of queries to the slow log. If variable empty alll kind of queries are logged. All types are bound by slow_query_time, except 'not_using_index' which is always logged if enabled", SESSION_VAR(log_slow_filter), CMD_LINE(REQUIRED_ARG), log_slow_filter_names, - DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1))); + /* by default we log all queries except 'not_using_index' */ + DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1) & + ~QPLAN_NOT_USING_INDEX)); + +static const char *log_slow_disabled_statements_names[]= +{ "admin", "call", "slave", "sp", 0 }; + +static const char *log_disabled_statements_names[]= +{ "slave", "sp", 0 }; + +static Sys_var_set Sys_log_slow_disabled_statements( + "log_slow_disabled_statements", + "Don't log certain types of statements to slow log", + SESSION_VAR(log_slow_disabled_statements), CMD_LINE(REQUIRED_ARG), + log_slow_disabled_statements_names, + DEFAULT(LOG_SLOW_DISABLE_SP)); + +static Sys_var_set Sys_log_disabled_statements( + "log_disabled_statements", + "Don't log certain types of statements to general log", + SESSION_VAR(log_disabled_statements), CMD_LINE(REQUIRED_ARG), + log_disabled_statements_names, + DEFAULT(LOG_DISABLE_SP), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super)); static const char *default_regex_flags_names[]= { @@ -5551,12 +5929,10 @@ static Sys_var_ulonglong Sys_max_thread_mem( static Sys_var_sesvartrack Sys_track_session_sys_vars( "session_track_system_variables", - "Track changes in registered system variables. " - "For compatibility with MySQL defaults this variable should be set to " - "\"autocommit, character_set_client, character_set_connection, " - "character_set_results, time_zone\"", + "Track changes in registered system variables. ", CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, - DEFAULT(""), + DEFAULT("autocommit,character_set_client,character_set_connection," + "character_set_results,time_zone"), NO_MUTEX_GUARD); static bool update_session_track_schema(sys_var *self, THD *thd, @@ -5621,3 +5997,12 @@ static Sys_var_mybool Sys_session_track_state_change( ON_UPDATE(update_session_track_state_change)); #endif //EMBEDDED_LIBRARY + +#ifndef DBUG_OFF +static Sys_var_uint Sys_in_subquery_conversion_threshold( + "in_predicate_conversion_threshold", + "The minimum number of scalar elements in the value list of " + "IN predicate that triggers its conversion to IN subquery", + SESSION_VAR(in_subquery_conversion_threshold), CMD_LINE(OPT_ARG), + VALID_RANGE(0, UINT_MAX), DEFAULT(IN_SUBQUERY_CONVERSION_THRESHOLD), BLOCK_SIZE(1)); +#endif diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index ba58315b449..706240727c5 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -88,7 +88,7 @@ while(!(X)) \ { \ fprintf(stderr, "Sysvar '%s' failed '%s'\n", name_arg, #X); \ - DBUG_ABORT(); \ + DBUG_ASSERT(0); \ exit(255); \ } @@ -612,7 +612,7 @@ public: /* parse and feel list with default values */ if (thd) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool res= #endif sysvartrack_validate_value(thd, @@ -1535,6 +1535,116 @@ public: { return valptr(thd, get_default(thd)); } }; +/** + Class for variables that containg a list of plugins. + Currently this is used only for @@gtid_pos_auto_create_engines + + Backing store: plugin_ref + + @note + Currently this is only used for storage engine type plugins, and thus only + storage engine type plugin is implemented. It could be extended to other + plugin types later if needed, similar to Sys_var_plugin. + + These variables don't support command-line equivalents, any such + command-line options should be added manually to my_long_options in mysqld.cc + + Note on lifetimes of resources allocated: We allocate a zero-terminated array + of plugin_ref*, and lock the contained plugins. The list in the global + variable must be freed (with free_engine_list()). However, the way Sys_var + works, there is no place to explicitly free other lists, like the one + returned from get_default(). + + Therefore, the code needs to work with temporary lists, which are + registered in the THD to be automatically freed (and plugins similarly + automatically unlocked). This is why do_check() allocates a temporary + list, from which do_update() then makes a permanent copy. +*/ +class Sys_var_pluginlist: public sys_var +{ + int plugin_type; +public: + Sys_var_pluginlist(const char *name_arg, + const char *comment, int flag_args, ptrdiff_t off, size_t size, + CMD_LINE getopt, + char **def_val, PolyLock *lock=0, + enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG, + on_check_function on_check_func=0, + on_update_function on_update_func=0, + const char *substitute=0) + : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id, + getopt.arg_type, SHOW_CHAR, (intptr)def_val, + lock, binlog_status_arg, on_check_func, on_update_func, + substitute) + { + option.var_type|= GET_STR; + SYSVAR_ASSERT(size == sizeof(plugin_ref)); + SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE + } + bool do_check(THD *thd, set_var *var) + { + char buff[STRING_BUFFER_USUAL_SIZE]; + String str(buff,sizeof(buff), system_charset_info), *res; + plugin_ref *plugins; + + if (!(res=var->value->val_str(&str))) + plugins= resolve_engine_list(thd, "", 0, true, true); + else + plugins= resolve_engine_list(thd, res->ptr(), res->length(), true, true); + if (!plugins) + return true; + var->save_result.plugins= plugins; + return false; + } + void do_update(plugin_ref **valptr, plugin_ref* newval) + { + plugin_ref *oldval= *valptr; + *valptr= copy_engine_list(newval); + free_engine_list(oldval); + } + bool session_update(THD *thd, set_var *var) + { + do_update((plugin_ref**)session_var_ptr(thd), + var->save_result.plugins); + return false; + } + bool global_update(THD *thd, set_var *var) + { + do_update((plugin_ref**)global_var_ptr(), + var->save_result.plugins); + return false; + } + void session_save_default(THD *thd, set_var *var) + { + plugin_ref* plugins= global_var(plugin_ref *); + var->save_result.plugins= plugins ? temp_copy_engine_list(thd, plugins) : 0; + } + plugin_ref *get_default(THD *thd) + { + char *default_value= *reinterpret_cast<char**>(option.def_value); + if (!default_value) + return 0; + return resolve_engine_list(thd, default_value, strlen(default_value), + false, true); + } + + void global_save_default(THD *thd, set_var *var) + { + var->save_result.plugins= get_default(thd); + } + + uchar *valptr(THD *thd, plugin_ref *plugins) + { + return (uchar*)pretty_print_engine_list(thd, plugins); + } + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) + { return valptr(thd, session_var(thd, plugin_ref*)); } + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) + { return valptr(thd, global_var(plugin_ref*)); } + uchar *default_value_ptr(THD *thd) + { return valptr(thd, get_default(thd)); } +}; + #if defined(ENABLED_DEBUG_SYNC) #include "debug_sync.h" @@ -1654,12 +1764,12 @@ public: binlog_status_arg, on_check_func, on_update_func, substitute) { - option.var_type|= GET_BOOL; + option.var_type|= GET_BIT; reverse_semantics= my_count_bits(bitmask_arg) > 1; bitmask= reverse_semantics ? ~bitmask_arg : bitmask_arg; + option.block_size= reverse_semantics ? -(long) bitmask : (long)bitmask; set(global_var_ptr(), def_val); SYSVAR_ASSERT(def_val < 2); - SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE SYSVAR_ASSERT(size == sizeof(ulonglong)); } bool session_update(THD *thd, set_var *var) diff --git a/sql/table.cc b/sql/table.cc index aa85b296399..e6878c42ea2 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -17,7 +17,7 @@ /* Some general useful functions */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "table.h" #include "key.h" // find_ref_key @@ -43,6 +43,7 @@ #include "rpl_filter.h" #include "sql_cte.h" #include "ha_sequence.h" +#include "sql_show.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -217,31 +218,26 @@ static uchar *get_field_name(Field **buff, size_t *length, Returns pointer to '.frm' extension of the file name. SYNOPSIS - fn_rext() + fn_frm_ext() name file name DESCRIPTION Checks file name part starting with the rightmost '.' character, and returns it if it is equal to '.frm'. - TODO - It is a good idea to get rid of this function modifying the code - to garantee that the functions presently calling fn_rext() always - get arguments in the same format: either with '.frm' or without '.frm'. - RETURN VALUES - Pointer to the '.frm' extension. If there is no extension, - or extension is not '.frm', pointer at the end of file name. + Pointer to the '.frm' extension or NULL if not a .frm file */ -char *fn_rext(char *name) +const char *fn_frm_ext(const char *name) { - char *res= strrchr(name, '.'); + const char *res= strrchr(name, '.'); if (res && !strcmp(res, reg_ext)) return res; - return name + strlen(name); + return 0; } + TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, const LEX_CSTRING *name) { @@ -251,30 +247,18 @@ TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, if (is_infoschema_db(db->str, db->length)) return TABLE_CATEGORY_INFORMATION; - if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) && - (my_strcasecmp(system_charset_info, - PERFORMANCE_SCHEMA_DB_NAME.str, - db->str) == 0)) + if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db) == 0) return TABLE_CATEGORY_PERFORMANCE; - if ((db->length == MYSQL_SCHEMA_NAME.length) && - (my_strcasecmp(system_charset_info, - MYSQL_SCHEMA_NAME.str, - db->str) == 0)) + if (lex_string_eq(&MYSQL_SCHEMA_NAME, db) == 0) { if (is_system_table_name(name->str, name->length)) return TABLE_CATEGORY_SYSTEM; - if ((name->length == GENERAL_LOG_NAME.length) && - (my_strcasecmp(system_charset_info, - GENERAL_LOG_NAME.str, - name->str) == 0)) + if (lex_string_eq(&GENERAL_LOG_NAME, name) == 0) return TABLE_CATEGORY_LOG; - if ((name->length == SLOW_LOG_NAME.length) && - (my_strcasecmp(system_charset_info, - SLOW_LOG_NAME.str, - name->str) == 0)) + if (lex_string_eq(&SLOW_LOG_NAME, name) == 0) return TABLE_CATEGORY_LOG; } @@ -328,8 +312,13 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, share->normalized_path.length= path_length; share->table_category= get_table_category(& share->db, & share->table_name); share->open_errno= ENOENT; - /* The following will be fixed in open_table_from_share */ - share->cached_row_logging_check= 1; + /* The following will be updated in open_table_from_share */ + share->can_do_row_logging= 1; + if (share->table_category == TABLE_CATEGORY_LOG) + share->no_replicate= 1; + if (my_strnncoll(table_alias_charset, (uchar*) db, 6, + (const uchar*) "mysql", 6) == 0) + share->not_usable_by_query_cache= 1; init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); @@ -402,8 +391,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, share->normalized_path.str= (char*) path; share->path.length= share->normalized_path.length= strlen(path); share->frm_version= FRM_VER_CURRENT; - - share->cached_row_logging_check= 0; // No row logging + share->not_usable_by_query_cache= 1; + share->can_do_row_logging= 0; // No row logging /* table_map_id is also used for MERGE tables to suppress repeated @@ -1173,13 +1162,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint db_create_options, keys, key_parts, n_length; uint com_length, null_bit_pos, UNINIT_VAR(mysql57_vcol_null_bit_pos), bitmap_count; uint i; + uint field_additional_property_length= 0; bool use_hash, mysql57_null_bits= 0; char *keynames, *names, *comment_pos; const uchar *forminfo, *extra2; const uchar *frm_image_end = frm_image + frm_length; uchar *record, *null_flags, *null_pos, *mysql57_vcol_null_pos= 0; const uchar *disk_buff, *strpos; - ulong pos, record_offset; + const uchar *field_properties= NULL; + ulong pos, record_offset; ulong rec_buff_length; handler *handler_file= 0; KEY *keyinfo; @@ -1233,7 +1224,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (*extra2 != '/') // old frm had '/' there { const uchar *e2end= extra2 + len; - while (extra2 + 3 < e2end) + while (extra2 + 3 <= e2end) { uchar type= *extra2++; size_t length= *extra2++; @@ -1291,6 +1282,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #endif /*HAVE_SPATIAL*/ break; + case EXTRA2_FIELD_FLAGS: + field_properties = extra2; + field_additional_property_length= length; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -1607,8 +1602,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, memcpy(record, frm_image + record_offset, share->reclength); disk_buff= frm_image + pos + FRM_FORMINFO_SIZE; - share->fields= uint2korr(forminfo+258); + if (field_properties && field_additional_property_length != share->fields) + goto err; pos= uint2korr(forminfo+260); /* Length of all screens */ n_length= uint2korr(forminfo+268); interval_count= uint2korr(forminfo+270); @@ -1619,6 +1615,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_screen_length= uint2korr(forminfo+286); share->virtual_fields= share->default_expressions= share->field_check_constraints= share->default_fields= 0; + share->visible_fields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { @@ -1990,6 +1987,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, reg_field->field_index= i; reg_field->comment=comment; reg_field->vcol_info= vcol_info; + if(field_properties!=NULL) + { + uint temp= *field_properties++; + reg_field->field_visibility= f_visibility(temp); + } + if (reg_field->field_visibility == USER_DEFINED_INVISIBLE) + status_var_increment(thd->status_var.feature_invisible_columns); + if (reg_field->field_visibility == NOT_INVISIBLE) + share->visible_fields++; if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { null_bits_are_used= 1; @@ -2125,18 +2131,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, for (uint key=0 ; key < keys ; key++,keyinfo++) { uint usable_parts= 0; - keyinfo->name=(char*) share->keynames.type_names[key]; - keyinfo->name_length= strlen(keyinfo->name); + keyinfo->name.str= share->keynames.type_names[key]; + keyinfo->name.length= strlen(keyinfo->name.str); keyinfo->cache_name= (uchar*) alloc_root(&share->mem_root, share->table_cache_key.length+ - keyinfo->name_length + 1); + keyinfo->name.length + 1); if (keyinfo->cache_name) // If not out of memory { uchar *pos= keyinfo->cache_name; memcpy(pos, share->table_cache_key.str, share->table_cache_key.length); - memcpy(pos + share->table_cache_key.length, keyinfo->name, - keyinfo->name_length+1); + memcpy(pos + share->table_cache_key.length, keyinfo->name.str, + keyinfo->name.length+1); } if (ext_key_parts > share->key_parts && key) @@ -2239,6 +2245,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); + if (field->field_visibility > USER_DEFINED_INVISIBLE) + keyinfo->flags |= HA_INVISIBLE_KEY; if (field->null_ptr) { key_part->null_offset=(uint) ((uchar*) field->null_ptr - @@ -2955,6 +2963,14 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table, if (error) goto end; + if (lex.current_select->table_list.first[0].next_global) + { + /* We are using NEXT VALUE FOR sequence. Remember table name for open */ + TABLE_LIST *sequence= lex.current_select->table_list.first[0].next_global; + sequence->next_global= table->internal_tables; + table->internal_tables= sequence; + } + vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type()); vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db; vcol_storage.vcol_info->name= vcol->name; @@ -3377,24 +3393,20 @@ partititon_err: outparam->mark_columns_used_by_check_constraints(); - if (share->table_category == TABLE_CATEGORY_LOG) - { - outparam->no_replicate= TRUE; - } - else if (outparam->file) + if (db_stat) { + /* Set some flags in share on first open of the table */ handler::Table_flags flags= outparam->file->ha_table_flags(); - outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE - | HA_BINLOG_ROW_CAPABLE)) - || MY_TEST(flags & HA_HAS_OWN_BINLOGGING); - } - else - { - outparam->no_replicate= FALSE; + if (! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE | + HA_BINLOG_ROW_CAPABLE)) || + MY_TEST(flags & HA_HAS_OWN_BINLOGGING)) + share->no_replicate= TRUE; + if (outparam->file->table_cache_type() & HA_CACHE_TBL_NOCACHE) + share->not_usable_by_query_cache= TRUE; } - if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str)) - outparam->s->cached_row_logging_check= 0; // No row based replication + if (share->no_replicate || !binlog_filter->db_ok(share->db.str)) + share->can_do_row_logging= 0; // No row based replication /* Increment the opened_tables counter, only when open flags set. */ if (db_stat) @@ -5124,7 +5136,6 @@ int TABLE::verify_constraints(bool ignore_failure) return(VIEW_CHECK_OK); } - /* Find table in underlying tables by mask and check that only this table belong to given mask @@ -6289,11 +6300,6 @@ void TABLE::mark_columns_needed_for_delete() need_signal= true; } } - if (check_constraints) - { - mark_check_constraint_columns_for_read(); - need_signal= true; - } if (need_signal) file->column_bitmaps_signal(); @@ -6653,6 +6659,58 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl) DBUG_RETURN(bitmap_updated); } + +/** + Check if a virtual not stored column field is in read set + + @retval FALSE No virtual not stored column is used + @retval TRUE At least one virtual not stored column is used +*/ + +bool TABLE::check_virtual_columns_marked_for_read() +{ + if (vfield) + { + Field **vfield_ptr; + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + Field *tmp_vfield= *vfield_ptr; + if (bitmap_is_set(read_set, tmp_vfield->field_index) && + !tmp_vfield->vcol_info->stored_in_db) + return TRUE; + } + } + return FALSE; +} + + +/** + Check if a stored virtual column field is marked for write + + This can be used to check if any column that is part of a virtual + stored column is changed + + @retval FALSE No stored virtual column is used + @retval TRUE At least one stored virtual column is used +*/ + +bool TABLE::check_virtual_columns_marked_for_write() +{ + if (vfield) + { + Field **vfield_ptr; + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + Field *tmp_vfield= *vfield_ptr; + if (bitmap_is_set(write_set, tmp_vfield->field_index) && + tmp_vfield->vcol_info->stored_in_db) + return TRUE; + } + } + return FALSE; +} + + /* Mark fields used by check constraints. This is done once for the TABLE_SHARE the first time the table is opened. @@ -6710,6 +6768,7 @@ void TABLE::mark_default_fields_for_write(bool is_insert) DBUG_VOID_RETURN; } + void TABLE::move_fields(Field **ptr, const uchar *to, const uchar *from) { my_ptrdiff_t diff= to - from; @@ -6916,7 +6975,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, if (unique) keyinfo->flags|= HA_NOSAME; sprintf(buf, "key%i", key); - if (!(keyinfo->name= strdup_root(&mem_root, buf))) + keyinfo->name.length= strlen(buf); + if (!(keyinfo->name.str= strmake_root(&mem_root, buf, keyinfo->name.length))) return TRUE; keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root, sizeof(ulong)*key_parts); @@ -8260,8 +8320,8 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) if (!(item->used_tables() == tab_map)) continue; Item_func_eq *eq= 0; - Item *left_item_clone= left_item->build_clone(thd, thd->mem_root); - Item *right_item_clone= item->build_clone(thd, thd->mem_root); + Item *left_item_clone= left_item->build_clone(thd); + Item *right_item_clone= item->build_clone(thd); if (left_item_clone && right_item_clone) { left_item_clone->set_item_equal(NULL); @@ -8291,7 +8351,7 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) return new_cond; } else if (cond->get_extraction_flag() != NO_EXTRACTION_FL) - return cond->build_clone(thd, thd->mem_root); + return cond->build_clone(thd); return 0; } @@ -8316,7 +8376,7 @@ Field *TABLE::find_field_by_name(LEX_CSTRING *str) const for (Field **tmp= field; *tmp; tmp++) { if ((*tmp)->field_name.length == length && - !my_strcasecmp(system_charset_info, (*tmp)->field_name.str, str->str)) + !lex_string_cmp(system_charset_info, &(*tmp)->field_name, str)) return *tmp; } return NULL; diff --git a/sql/table.h b/sql/table.h index 8f83bfc5ef2..8e39964eea9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -17,7 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_plist.h" #include "sql_list.h" /* Sql_alloc */ #include "mdl.h" @@ -336,6 +335,16 @@ enum enum_vcol_update_mode VCOL_UPDATE_FOR_REPLACE }; +/* Field visibility enums */ + +enum field_visible_type{ + NOT_INVISIBLE= 0, + USER_DEFINED_INVISIBLE, + /* automatically added by the server. Can be queried explicitly + in SELECT, otherwise invisible from anything" */ + SYSTEM_INVISIBLE, + COMPLETELY_INVISIBLE +}; /** Category of table found in the table share. @@ -669,6 +678,7 @@ struct TABLE_SHARE uint blob_fields; /* number of blob fields */ uint varchar_fields; /* number of varchar fields */ uint default_fields; /* number of default fields */ + uint visible_fields; /* number of visible fields */ uint default_expressions; uint table_check_constraints, field_check_constraints; @@ -695,7 +705,8 @@ struct TABLE_SHARE bool use_ext_keys; /* Extended keys can be used */ bool null_field_first; bool system; /* Set if system table (one record) */ - bool crypted; /* If .frm file is crypted */ + bool not_usable_by_query_cache; + bool no_replicate; bool crashed; bool is_view; bool can_cmp_whole_record; @@ -704,6 +715,8 @@ struct TABLE_SHARE bool vcols_need_refixing; bool check_set_initialized; bool has_update_default_function; + bool can_do_row_logging; /* 1 if table supports RBR */ + ulong table_map_id; /* for row-based replication */ /* @@ -722,14 +735,6 @@ struct TABLE_SHARE /* For sequence tables, the current sequence state */ SEQUENCE *sequence; - /* - Cache for row-based replication table share checks that does not - need to be repeated. Possible values are: -1 when cache value is - not calculated yet, 0 when table *shall not* be replicated, 1 when - table *may* be replicated. - */ - int cached_row_logging_check; - /* Name of the tablespace used for this table */ char *tablespace; @@ -1028,6 +1033,8 @@ struct st_cond_statistic; /* Bitmap of table's fields */ typedef Bitmap<MAX_FIELDS> Field_map; +class SplM_opt_info; + struct TABLE { TABLE() {} /* Remove gcc warning */ @@ -1092,6 +1099,8 @@ public: TABLE_LIST *pos_in_table_list;/* Element referring to this table */ /* Position in thd->locked_table_list under LOCK TABLES */ TABLE_LIST *pos_in_locked_tables; + /* Tables used in DEFAULT and CHECK CONSTRAINT (normally sequence tables) */ + TABLE_LIST *internal_tables; /* Not-null for temporary tables only. Non-null values means this table is @@ -1256,7 +1265,6 @@ public: If set, indicate that the table is not replicated by the server. */ bool locked_by_logger; - bool no_replicate; bool locked_by_name; bool fulltext_searched; bool no_cache; @@ -1306,7 +1314,13 @@ public: bool stats_is_read; /* Persistent statistics is read for the table */ bool histograms_are_read; MDL_ticket *mdl_ticket; - List<Field> splitting_fields; + + /* + This is used only for potentially splittable materialized tables and it + points to the info used by the optimizer to apply splitting optimization + */ + SplM_opt_info *spl_opt_info; + key_map keys_usable_for_splitting; void init(THD *thd, TABLE_LIST *tl); bool fill_item_list(List<Item> *item_list) const; @@ -1326,6 +1340,8 @@ public: void mark_columns_per_binlog_row_image(void); bool mark_virtual_col(Field *field); bool mark_virtual_columns_for_write(bool insert_fl); + bool check_virtual_columns_marked_for_read(); + bool check_virtual_columns_marked_for_write(); void mark_default_fields_for_write(bool insert_fl); void mark_columns_used_by_check_constraints(void); void mark_check_constraint_columns_for_read(void); @@ -1449,6 +1465,10 @@ public: bool with_cleanup); Field *find_field_by_name(LEX_CSTRING *str) const; bool export_structure(THD *thd, class Row_definition_list *defs); + bool is_splittable() { return spl_opt_info != NULL; } + void set_spl_opt_info(SplM_opt_info *spl_info); + void deny_splitting(); + void add_splitting_info_for_key_field(struct KEY_FIELD *key_field); }; @@ -1742,6 +1762,11 @@ struct TABLE_LIST { TABLE_LIST() {} /* Remove gcc warning */ + enum prelocking_types + { + PRELOCK_NONE, PRELOCK_ROUTINE, PRELOCK_FK + }; + /** Prepare TABLE_LIST that consists of one table instance to use in open_and_lock_tables @@ -1772,7 +1797,7 @@ struct TABLE_LIST size_t table_name_length_arg, const char *alias_arg, enum thr_lock_type lock_type_arg, - bool routine, + prelocking_types prelocking_type, TABLE_LIST *belong_to_view_arg, uint8 trg_event_map_arg, TABLE_LIST ***last_ptr) @@ -1780,8 +1805,10 @@ struct TABLE_LIST init_one_table(db_name_arg, db_length_arg, table_name_arg, table_name_length_arg, alias_arg, lock_type_arg); cacheable_table= 1; - prelocking_placeholder= routine ? ROUTINE : FK; - open_type= routine ? OT_TEMPORARY_OR_BASE : OT_BASE_ONLY; + prelocking_placeholder= prelocking_type; + open_type= (prelocking_type == PRELOCK_ROUTINE ? + OT_TEMPORARY_OR_BASE : + OT_BASE_ONLY); belong_to_view= belong_to_view_arg; trg_event_map= trg_event_map_arg; @@ -2070,7 +2097,7 @@ struct TABLE_LIST This TABLE_LIST object is just placeholder for prelocking, it will be used for implicit LOCK TABLES only and won't be used in real statement. */ - enum { USER, ROUTINE, FK } prelocking_placeholder; + prelocking_types prelocking_placeholder; /** Indicates that if TABLE_LIST object corresponds to the table/view which requires special handling. @@ -2084,9 +2111,6 @@ struct TABLE_LIST /* Don't associate a table share. */ OPEN_STUB } open_strategy; - /* For transactional locking. */ - int lock_timeout; /* NOWAIT or WAIT [X] */ - bool lock_transactional; /* If transactional lock requested. */ /** TRUE if an alias for this table was specified in the SQL. */ bool is_alias; /** TRUE if the table is referred to in the statement using a fully @@ -2621,7 +2645,7 @@ static inline void tmp_restore_column_map(MY_BITMAP *bitmap, static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, MY_BITMAP *bitmap) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS return tmp_use_all_columns(table, bitmap); #else return 0; @@ -2631,7 +2655,7 @@ static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap, my_bitmap_map *old) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS tmp_restore_column_map(bitmap, old); #endif } @@ -2646,7 +2670,7 @@ static inline void dbug_tmp_use_all_columns(TABLE *table, MY_BITMAP *read_set, MY_BITMAP *write_set) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS save[0]= read_set->bitmap; save[1]= write_set->bitmap; (void) tmp_use_all_columns(table, read_set); @@ -2659,7 +2683,7 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, MY_BITMAP *write_set, my_bitmap_map **old) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS tmp_restore_column_map(read_set, old[0]); tmp_restore_column_map(write_set, old[1]); #endif @@ -2718,7 +2742,7 @@ ulong get_form_pos(File file, uchar *head, TYPELIB *save_names); void append_unescaped(String *res, const char *pos, uint length); void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, HA_CREATE_INFO *create_info, uint keys, KEY *key_info); -char *fn_rext(char *name); +const char *fn_frm_ext(const char *name); /* Check that the integer is in the internal */ static inline int set_zone(int nr,int min_zone,int max_zone) diff --git a/sql/table_cache.cc b/sql/table_cache.cc index be990543757..f55e24f3b04 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -46,7 +46,7 @@ TABLE_SHARE::tdc.flushed is true */ -#include "my_global.h" +#include "mariadb.h" #include "lf.h" #include "table.h" #include "sql_base.h" @@ -56,7 +56,7 @@ ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */ ulong tc_size; /**< Table cache threshold for LRU eviction. */ uint32 tc_instances; -static uint32 tc_active_instances= 1; +uint32 tc_active_instances= 1; static uint32 tc_contention_warning_reported; /** Data collections. */ @@ -369,18 +369,30 @@ void tc_add_table(THD *thd, TABLE *table) mysql_mutex_unlock(&element->LOCK_table_share); mysql_mutex_lock(&tc[i].LOCK_table_cache); - if (tc[i].records == tc_size && (LRU_table= tc[i].free_tables.pop_front())) + if (tc[i].records == tc_size) { - LRU_table->s->tdc->free_tables[i].list.remove(LRU_table); - /* Needed if MDL deadlock detector chimes in before tc_remove_table() */ - LRU_table->in_use= thd; + if ((LRU_table= tc[i].free_tables.pop_front())) + { + LRU_table->s->tdc->free_tables[i].list.remove(LRU_table); + /* Needed if MDL deadlock detector chimes in before tc_remove_table() */ + LRU_table->in_use= thd; + mysql_mutex_unlock(&tc[i].LOCK_table_cache); + /* Keep out of locked LOCK_table_cache */ + tc_remove_table(LRU_table); + } + else + { + tc[i].records++; + mysql_mutex_unlock(&tc[i].LOCK_table_cache); + } + /* Keep out of locked LOCK_table_cache */ + status_var_increment(thd->status_var.table_open_cache_overflows); } else + { tc[i].records++; - mysql_mutex_unlock(&tc[i].LOCK_table_cache); - - if (LRU_table) - tc_remove_table(LRU_table); + mysql_mutex_unlock(&tc[i].LOCK_table_cache); + } } @@ -841,7 +853,10 @@ retry: tdc_purge(false); if (out_table) + { + status_var_increment(thd->status_var.table_open_cache_misses); *out_table= 0; + } share->m_psi= PSI_CALL_get_table_share(false, share); goto end; } @@ -858,8 +873,10 @@ retry: DBUG_ASSERT(element->share); DBUG_ASSERT(!element->share->error); DBUG_ASSERT(!element->share->is_view); + status_var_increment(thd->status_var.table_open_cache_hits); DBUG_RETURN(element->share); } + status_var_increment(thd->status_var.table_open_cache_misses); } mysql_mutex_lock(&element->LOCK_table_share); diff --git a/sql/table_cache.h b/sql/table_cache.h index 2e5bb3428dc..b41665258c9 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -71,6 +71,7 @@ enum enum_tdc_remove_table_type extern ulong tdc_size; extern ulong tc_size; extern uint32 tc_instances; +extern uint32 tc_active_instances; extern bool tdc_init(void); extern void tdc_start_shutdown(void); diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 5e5a05084b3..a5088a012ae 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -19,6 +19,7 @@ All methods pertaining to temporary tables. */ +#include "mariadb.h" #include "sql_acl.h" /* TMP_TABLE_ACLS */ #include "sql_base.h" /* tdc_create_key */ #include "lock.h" /* mysql_lock_remove */ @@ -64,7 +65,8 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, const char *path, const char *db, const char *table_name, - bool open_in_engine) + bool open_in_engine, + bool open_internal_tables) { DBUG_ENTER("THD::create_and_open_tmp_table"); @@ -89,6 +91,15 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, /* Free the TMP_TABLE_SHARE. */ free_tmp_table_share(share, false); + DBUG_RETURN(0); + } + + /* Open any related tables */ + if (open_internal_tables && table->internal_tables && + open_and_lock_internal_tables(table, open_in_engine)) + { + drop_temporary_table(table, NULL, false); + DBUG_RETURN(0); } } @@ -1118,6 +1129,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, table->grant.privilege= TMP_TABLE_ACLS; share->tmp_table= (table->file->has_transaction_manager() ? TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); + share->not_usable_by_query_cache= 1; table->pos_in_table_list= 0; table->query_id= query_id; diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index cbed769a424..74763e81b15 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.cc @@ -17,7 +17,7 @@ /* Mallocs for used in threads */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "thr_malloc.h" diff --git a/sql/thr_malloc.h b/sql/thr_malloc.h index fc23c87c9fd..574dc7966b0 100644 --- a/sql/thr_malloc.h +++ b/sql/thr_malloc.h @@ -16,8 +16,6 @@ #ifndef THR_MALLOC_INCLUDED #define THR_MALLOC_INCLUDED -#include "my_global.h" // uint, size_t - typedef struct st_mem_root MEM_ROOT; void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size, diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 98b2dcd1fcd..fb9e61f9a05 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <violite.h> #include <sql_priv.h> #include <sql_class.h> @@ -199,7 +199,7 @@ void tp_callback(TP_connection *c) c->priority= get_priority(c); /* Read next command from client. */ - c->set_io_timeout(thd->variables.net_wait_timeout); + c->set_io_timeout(thd->get_net_wait_timeout()); c->state= TP_STATE_IDLE; if (c->start_io()) goto error; diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc index f6fdd97c6df..4ff6d5f2cfb 100644 --- a/sql/threadpool_generic.cc +++ b/sql/threadpool_generic.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <violite.h> #include <sql_priv.h> #include <sql_class.h> diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 855b9b38d78..adaca08982f 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -19,7 +19,7 @@ #define _WIN32_WINNT 0x0601 -#include <my_global.h> +#include "mariadb.h" #include <violite.h> #include <sql_priv.h> #include <sql_class.h> diff --git a/sql/transaction.cc b/sql/transaction.cc index 78cd3047f82..ec277e9c9c4 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -18,13 +18,12 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "transaction.h" -#include "rpl_handler.h" #include "debug_sync.h" // DEBUG_SYNC #include "sql_acl.h" - +#include "semisync_master.h" #ifndef EMBEDDED_LIBRARY /** @@ -318,9 +317,17 @@ bool trans_commit(THD *thd) transaction, so the hooks for rollback will be called. */ if (res) - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif + } else - (void) RUN_HOOK(transaction, after_commit, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_commit(thd, FALSE); +#endif + } thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.reset(); thd->lex->start_transaction_opt= 0; @@ -413,7 +420,9 @@ bool trans_rollback(THD *thd) ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY); DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); res= ha_rollback_trans(thd, TRUE); - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); /* Reset the binlog transaction marker */ thd->variables.option_bits&= ~OPTION_GTID_BEGIN; @@ -526,9 +535,17 @@ bool trans_commit_stmt(THD *thd) transaction, so the hooks for rollback will be called. */ if (res) - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif + } else - (void) RUN_HOOK(transaction, after_commit, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_commit(thd, FALSE); +#endif + } thd->transaction.stmt.reset(); @@ -567,7 +584,9 @@ bool trans_rollback_stmt(THD *thd) trans_reset_one_shot_chistics(thd); } - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif thd->transaction.stmt.reset(); diff --git a/sql/transaction.h b/sql/transaction.h index b6b22053150..7e34693a2eb 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -20,7 +20,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_global.h> #include <m_string.h> class THD; diff --git a/sql/tztime.cc b/sql/tztime.cc index a9db91668bb..4581461f8b3 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -29,22 +29,21 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #if !defined(TZINFO2SQL) && !defined(TESTTIME) #include "sql_priv.h" #include "unireg.h" -#include "tztime.h" #include "sql_time.h" // localtime_to_TIME #include "sql_base.h" // open_system_tables_for_read, // close_system_tables #else #include <my_time.h> -#include "tztime.h" #include <my_sys.h> #include <mysql_version.h> #include <my_getopt.h> #endif +#include "tztime.h" #include "tzfile.h" #include <m_string.h> #include <my_dir.h> diff --git a/sql/udf_example.c b/sql/udf_example.c index 98700953b2c..cc9a703373c 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -125,7 +125,7 @@ typedef unsigned long long ulonglong; typedef long long longlong; #endif /*__WIN__*/ #else -#include <my_global.h> +#include "mariadb.h" #include <my_sys.h> #if defined(MYSQL_SERVER) #include <m_string.h> /* To get strmov() */ diff --git a/sql/uniques.cc b/sql/uniques.cc index 7def2d79ad7..894e959cace 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -31,7 +31,7 @@ deletes in disk order. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_sort.h" @@ -367,7 +367,7 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size, Unique::~Unique() { close_cached_file(&file); - delete_tree(&tree); + delete_tree(&tree, 0); delete_dynamic(&file_ptrs); } @@ -387,7 +387,7 @@ bool Unique::flush() (void*) this, left_root_right) || insert_dynamic(&file_ptrs, (uchar*) &file_ptr)) return 1; - delete_tree(&tree); + delete_tree(&tree, 0); return 0; } diff --git a/sql/unireg.cc b/sql/unireg.cc index b49c3cfbb09..a0f42f68bf0 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -25,7 +25,7 @@ str is a (long) to record position where 0 is the first position. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_partition.h" // struct partition_info @@ -87,6 +87,21 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, return extra2_write(pos, type, reinterpret_cast<LEX_CSTRING *>(str)); } +static uchar *extra2_write_additional_field_properties(uchar *pos, + int number_of_fields,List_iterator<Create_field> * it) +{ + *pos++=EXTRA2_FIELD_FLAGS; + /* + always first 2 for field visibility + */ + pos= extra2_write_len(pos, number_of_fields); + Create_field *cf; + while((cf=(*it)++)) + *pos++= cf->field_visibility; + it->rewind(); + return pos; +} + /** Create a frm (table definition) file @@ -121,6 +136,19 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, StringBuffer<MAX_FIELD_WIDTH> vcols; DBUG_ENTER("build_frm_image"); + List_iterator<Create_field> it(create_fields); + Create_field *field; + bool have_additional_field_properties= false; + while ((field=it++)) + { + if (field->field_visibility != NOT_INVISIBLE) + { + have_additional_field_properties= true; + break; + } + } + it.rewind(); + /* If fixed row records, we need one bit to check for deleted rows */ if (!(create_info->table_options & HA_OPTION_PACK_RECORD)) create_info->null_bits++; @@ -218,7 +246,9 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (gis_extra2_len) extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len; - + if(have_additional_field_properties) + extra2_size+=1 + (create_fields.elements > 255 ? 3 : 1) + + create_fields.elements; key_buff_length= uint4korr(fileinfo+47); @@ -274,7 +304,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, pos+= gis_field_options_image(pos, create_fields); } #endif /*HAVE_SPATIAL*/ - + if (have_additional_field_properties) + pos=extra2_write_additional_field_properties(pos,create_fields.elements,&it); int4store(pos, filepos); // end of the extra2 segment pos+= 4; @@ -478,7 +509,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, *pos++=(uchar) NAMES_SEP_CHAR; for (key=keyinfo ; key != end ; key++) { - uchar *tmp=(uchar*) strmov((char*) pos,key->name); + uchar *tmp=(uchar*) strmov((char*) pos,key->name.str); *tmp++= (uchar) NAMES_SEP_CHAR; *tmp=0; pos=tmp; @@ -539,7 +570,7 @@ static bool pack_expression(String *buf, Virtual_column_info *vcol, size_t len_off= buf->length(); buf->q_append2b(0); // to be added later buf->q_append((char)vcol->name.length); - buf->q_append(vcol->name.str, vcol->name.length); + buf->q_append(&vcol->name); size_t expr_start= buf->length(); vcol->print(buf); size_t expr_len= buf->length() - expr_start; diff --git a/sql/unireg.h b/sql/unireg.h index b0cfb3841ef..36f985ee1bb 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -176,6 +176,7 @@ enum extra2_frm_value_type { #define EXTRA2_ENGINE_IMPORTANT 128 EXTRA2_ENGINE_TABLEOPTS=128, + EXTRA2_FIELD_FLAGS=129 }; int rea_create_table(THD *thd, LEX_CUSTRING *frm, diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 958add48b5a..a5c39887cbd 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -13,6 +13,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "mariadb.h" #include "wsrep_priv.h" #include "wsrep_binlog.h" // wsrep_dump_rbr_buf() #include "wsrep_xid.h" diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc index ad5ef9c5c55..bddbbb95f4d 100644 --- a/sql/wsrep_binlog.cc +++ b/sql/wsrep_binlog.cc @@ -13,6 +13,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "mariadb.h" #include "wsrep_binlog.h" #include "wsrep_priv.h" #include "log.h" diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc index 8da791b3429..bf4ce7c9d90 100644 --- a/sql/wsrep_check_opts.cc +++ b/sql/wsrep_check_opts.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "mysqld.h" #include "sys_vars_shared.h" #include "wsrep.h" diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index 0aa7f9b0aad..c83f64e9cb4 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -13,7 +13,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <my_global.h> +#include "mariadb.h" #include <sql_class.h> #include <mysql/service_wsrep.h> diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index 42eb92244ff..35a0020ef44 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include <mysqld.h> #include "sql_base.h" #include "rpl_filter.h" diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 6c5eaf4a60c..54313281625 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include <mysqld.h> #include <sql_class.h> #include <sql_parse.h> @@ -246,6 +247,17 @@ static void wsrep_log_cb(wsrep_log_level_t level, const char *msg) { } } +void wsrep_log(void (*fun)(const char *, ...), const char *format, ...) +{ + va_list args; + char msg[1024]; + va_start(args, format); + vsnprintf(msg, sizeof(msg) - 1, format, args); + va_end(args); + (fun)("WSREP: %s", msg); +} + + static void wsrep_log_states (wsrep_log_level_t const level, const wsrep_uuid_t* const group_uuid, wsrep_seqno_t const group_seqno, @@ -1320,22 +1332,17 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) SELECT_LEX *select_lex= &lex->select_lex; TABLE_LIST *first_table= select_lex->table_list.first; TABLE_LIST *views = first_table; - + LEX_USER *definer; String buff; const LEX_STRING command[3]= {{ C_STRING_WITH_LEN("CREATE ") }, { C_STRING_WITH_LEN("ALTER ") }, { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; - buff.append(command[thd->lex->create_view->mode].str, - command[thd->lex->create_view->mode].length); - - LEX_USER *definer; + buff.append(&command[thd->lex->create_view->mode]); if (lex->definer) - { definer= get_current_user(thd, lex->definer); - } else { /* @@ -1938,7 +1945,6 @@ pthread_handler_t start_wsrep_THD(void *arg) close_connection(thd, ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_status); MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0)); - goto error; } @@ -1961,7 +1967,6 @@ pthread_handler_t start_wsrep_THD(void *arg) close_connection(thd, ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_status); MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0)); - delete thd; goto error; } @@ -2005,13 +2010,8 @@ pthread_handler_t start_wsrep_THD(void *arg) // at server shutdown } - if (thread_handling > SCHEDULER_ONE_THREAD_PER_CONNECTION) - { - mysql_mutex_lock(&LOCK_thread_count); - thd->unlink(); - mysql_mutex_unlock(&LOCK_thread_count); - delete thd; - } + unlink_not_visible_thd(thd); + delete thd; my_thread_end(); return(NULL); @@ -2734,6 +2734,7 @@ void wsrep_unlock_rollback() my_bool wsrep_aborting_thd_contains(THD *thd) { + mysql_mutex_assert_owner(&LOCK_wsrep_rollback); wsrep_aborting_thd_t abortees = wsrep_aborting_thd; while (abortees) { @@ -2746,6 +2747,7 @@ my_bool wsrep_aborting_thd_contains(THD *thd) void wsrep_aborting_thd_enqueue(THD *thd) { + mysql_mutex_assert_owner(&LOCK_wsrep_rollback); wsrep_aborting_thd_t aborting = (wsrep_aborting_thd_t) my_malloc(sizeof(struct wsrep_aborting_thd), MYF(0)); aborting->aborting_thd = thd; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 0502a29527f..bed9b0dca48 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -193,13 +193,8 @@ extern wsrep_seqno_t wsrep_locked_seqno; ? wsrep_forced_binlog_format : (ulong)(my_format)) // prefix all messages with "WSREP" -#define WSREP_LOG(fun, ...) \ - do { \ - char msg[1024] = {'\0'}; \ - snprintf(msg, sizeof(msg) - 1, ## __VA_ARGS__); \ - fun("WSREP: %s", msg); \ - } while(0) - +void wsrep_log(void (*fun)(const char *, ...), const char *format, ...); +#define WSREP_LOG(fun, ...) wsrep_log(fun, ## __VA_ARGS__) #define WSREP_LOG_CONFLICT_THD(thd, role) \ WSREP_LOG(sql_print_information, \ "%s: \n " \ diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc index 92c685ba485..92bcc8eda43 100644 --- a/sql/wsrep_notify.cc +++ b/sql/wsrep_notify.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include <mysqld.h> #include "wsrep_priv.h" #include "wsrep_utils.h" diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index 8626cb07196..9c00f9fdaf6 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -13,11 +13,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "wsrep_sst.h" - #include <mysqld.h> #include <m_ctype.h> -#include <my_sys.h> #include <strfunc.h> #include <sql_class.h> #include <set_var.h> diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 061ff795fc3..32b812a9bb2 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -13,8 +13,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "mariadb.h" #include "wsrep_thd.h" - #include "transaction.h" #include "rpl_rli.h" #include "log_event.h" diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc index 1a358877a35..3c341e222b3 100644 --- a/sql/wsrep_utils.cc +++ b/sql/wsrep_utils.cc @@ -20,6 +20,7 @@ #define _GNU_SOURCE // POSIX_SPAWN_USEVFORK flag #endif +#include "mariadb.h" #include "wsrep_utils.h" #include "wsrep_mysqld.h" diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc index 2ff6ea0b32e..5af39bfc230 100644 --- a/sql/wsrep_xid.cc +++ b/sql/wsrep_xid.cc @@ -16,6 +16,7 @@ //! @file some utility functions and classes not directly related to replication +#include "mariadb.h" #include "wsrep_xid.h" #include "sql_class.h" #include "wsrep_mysqld.h" // for logging macros |