diff options
author | Nirbhay Choubey <nirbhay@skysql.com> | 2014-03-26 14:27:24 -0400 |
---|---|---|
committer | Nirbhay Choubey <nirbhay@skysql.com> | 2014-03-26 14:27:24 -0400 |
commit | 90e4f7f9d3f2669ac99f2817f884315bfc86b0a7 (patch) | |
tree | 60a10a515a9f5656e797d8142f97b94f89f4e797 /sql | |
parent | 586fab72f01e1c7f0f8bf363fa6b06a2f10965b4 (diff) | |
parent | 5b7cab82195268f7657504d0b53995654748cefa (diff) | |
download | mariadb-git-90e4f7f9d3f2669ac99f2817f884315bfc86b0a7.tar.gz |
* bzr merge -rtag:mariadb-10.0.9 maria/10.0
* Fix for post-merge build failures.
Diffstat (limited to 'sql')
170 files changed, 6151 insertions, 2649 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 697a4e0824f..75652c06649 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2011, Oracle and/or its affiliates. +# Copyright (c) 2006, 2013, Oracle and/or its affiliates. # # 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 @@ -20,8 +20,7 @@ ENDIF() INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql -${CMAKE_BINARY_DIR}/pcre -${CMAKE_SOURCE_DIR}/pcre +${PCRE_INCLUDES} ${ZLIB_INCLUDE_DIR} ${SSL_INCLUDE_DIRS} ${CMAKE_BINARY_DIR}/sql diff --git a/sql/custom_conf.h b/sql/custom_conf.h index afef0219857..62fdb619c27 100644 --- a/sql/custom_conf.h +++ b/sql/custom_conf.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (c) 2000, 2006 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 diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 34658ab51ac..b20269b8304 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -474,7 +474,7 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, end: event_table->file->ha_index_end(); - DBUG_RETURN(test(ret)); + DBUG_RETURN(MY_TEST(ret)); } @@ -746,7 +746,7 @@ end: thd->mdl_context.rollback_to_savepoint(mdl_savepoint); thd->variables.sql_mode= saved_mode; - DBUG_RETURN(test(ret)); + DBUG_RETURN(MY_TEST(ret)); } @@ -861,7 +861,7 @@ end: thd->mdl_context.rollback_to_savepoint(mdl_savepoint); thd->variables.sql_mode= saved_mode; - DBUG_RETURN(test(ret)); + DBUG_RETURN(MY_TEST(ret)); } @@ -921,7 +921,7 @@ end: close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); - DBUG_RETURN(test(ret)); + DBUG_RETURN(MY_TEST(ret)); } @@ -1159,7 +1159,7 @@ end: thd->restore_stmt_binlog_format(save_binlog_format); - DBUG_RETURN(test(ret)); + DBUG_RETURN(MY_TEST(ret)); } @@ -1237,7 +1237,7 @@ Event_db_repository::check_system_tables(THD *thd) close_mysql_tables(thd); } - DBUG_RETURN(test(ret)); + DBUG_RETURN(MY_TEST(ret)); } /** diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 92093e34b81..6091977cc8d 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. 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 diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index 4f6b9349162..6ec7dccefb9 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -1,6 +1,6 @@ #ifndef _EVENT_SCHEDULER_H_ #define _EVENT_SCHEDULER_H_ -/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2004, 2013, 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 diff --git a/sql/events.cc b/sql/events.cc index 082bd2ec485..1e4b56c08e0 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2005, 2011, Oracle and/or its affiliates. + Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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 diff --git a/sql/events.h b/sql/events.h index a6480e7241d..646fd257d52 100644 --- a/sql/events.h +++ b/sql/events.h @@ -1,6 +1,6 @@ #ifndef _EVENT_H_ #define _EVENT_H_ -/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2004, 2013, 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 diff --git a/sql/field.cc b/sql/field.cc index 755d2f8b625..922c9aba6c5 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1774,7 +1774,7 @@ int Field_num::store_decimal(const my_decimal *val) ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; longlong i= convert_decimal2longlong(val, unsigned_flag, &err); - return test(err | store(i, unsigned_flag)); + return MY_TEST(err | store(i, unsigned_flag)); } @@ -1926,7 +1926,7 @@ int Field::store_time_dec(MYSQL_TIME *ltime, uint dec) bool Field::optimize_range(uint idx, uint part) { - return test(table->file->index_flags(idx, part, 1) & HA_READ_RANGE); + return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE); } @@ -4636,14 +4636,24 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, } +static bool +copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) +{ + if (from->time_type == MYSQL_TIMESTAMP_TIME) + return time_to_datetime(thd, from, to); + *to= *from; + return false; +} + + int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec) { int unused; - MYSQL_TIME l_time= *ltime; ErrConvTime str(ltime); THD *thd= get_thd(); - - bool valid= !check_date(&l_time, pack_time(&l_time) != 0, + MYSQL_TIME l_time; + bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) && + !check_date(&l_time, pack_time(&l_time) != 0, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &unused); @@ -5201,15 +5211,28 @@ int Field_temporal_with_date::store(longlong nr, bool unsigned_val) int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec) { - int error = 0, have_smth_to_conv= 1; - MYSQL_TIME l_time= *ltime; + int error= 0, have_smth_to_conv= 1; ErrConvTime str(ltime); + MYSQL_TIME l_time; + + if (copy_or_convert_to_datetime(get_thd(), ltime, &l_time)) + { + /* + Set have_smth_to_conv and error in a way to have + store_TIME_with_warning do bzero(). + */ + have_smth_to_conv= false; + error= MYSQL_TIME_WARN_OUT_OF_RANGE; + goto store; + } + /* We don't perform range checking here since values stored in TIME structure always fit into DATETIME range. */ have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0, sql_mode_for_dates(current_thd), &error); +store: return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv); } @@ -8487,7 +8510,7 @@ int Field_bit::store_decimal(const my_decimal *val) { int err= 0; longlong i= convert_decimal2longlong(val, 1, &err); - return test(err | store(i, TRUE)); + return MY_TEST(err | store(i, TRUE)); } @@ -8776,7 +8799,7 @@ Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end, if (param_data == 0 || ((from_bit_len == bit_len) && (from_len == bytes_in_rec))) { - if (from + bytes_in_rec + test(bit_len) > from_end) + if (from + bytes_in_rec + MY_TEST(bit_len) > from_end) return 0; // Error in data if (bit_len > 0) @@ -8931,7 +8954,7 @@ void Create_field::create_length_to_internal_length(void) { pack_length= length / 8; /* We need one extra byte to store the bits we save among the null bits */ - key_length= pack_length + test(length & 7); + key_length= pack_length + MY_TEST(length & 7); } break; case MYSQL_TYPE_NEWDECIMAL: diff --git a/sql/field.h b/sql/field.h index 72be8e3519a..cbd9175f26c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,7 +1,7 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2011, Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2014, SkySQL 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 @@ -67,7 +67,7 @@ enum Derivation #define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII /* The length of the header part for each virtual column in the .frm file */ -#define FRM_VCOL_HEADER_SIZE(b) (3 + test(b)) +#define FRM_VCOL_HEADER_SIZE(b) (3 + MY_TEST(b)) class Count_distinct_field; @@ -75,6 +75,8 @@ struct ha_field_option_struct; struct st_cache_field; int field_conv(Field *to,Field *from); +int field_conv_incompatible(Field *to,Field *from); +bool memcpy_field_possible(Field *to, Field *from); int truncate_double(double *nr, uint field_length, uint dec, bool unsigned_flag, double max_value); longlong double_to_longlong(double nr, bool unsigned_flag, bool *error); @@ -616,7 +618,7 @@ public: null_ptr[row_offset]. */ return (table->null_row ? TRUE : - null_ptr ? test(null_ptr[row_offset] & null_bit) : 0); + null_ptr ? MY_TEST(null_ptr[row_offset] & null_bit) : 0); } inline bool is_real_null(my_ptrdiff_t row_offset= 0) const { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; } @@ -624,8 +626,7 @@ public: { if (!null_ptr) return 0; - return test(record[(uint) (null_ptr -table->record[0])] & - null_bit); + return MY_TEST(record[(uint) (null_ptr - table->record[0])] & null_bit); } inline void set_null(my_ptrdiff_t row_offset= 0) { if (null_ptr) null_ptr[row_offset]|= null_bit; } @@ -1054,7 +1055,7 @@ public: my_decimal *val_decimal(my_decimal *); virtual bool str_needs_quotes() { return TRUE; } uint is_equal(Create_field *new_field); - bool eq_cmp_as_binary() { return test(flags & BINARY_FLAG); } + bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); } virtual uint length_size() { return 0; } double pos_in_interval(Field *min, Field *max) { @@ -1654,7 +1655,7 @@ public: const char *field_name_arg, TABLE_SHARE *share, uint dec_arg) : Field_timestamp(ptr_arg, - MAX_DATETIME_WIDTH + dec_arg + test(dec_arg), null_ptr_arg, + MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, share), dec(dec_arg) { @@ -1865,8 +1866,8 @@ public: Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, uint dec_arg) - :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + test(dec_arg), null_ptr_arg, - null_bit_arg, unireg_check_arg, field_name_arg), + :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + MY_TEST(dec_arg), + null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), dec(dec_arg) { DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); @@ -2022,7 +2023,7 @@ public: Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, uint dec_arg) - :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + test(dec_arg), + :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), dec(dec_arg) { @@ -2401,18 +2402,6 @@ public: { store_length(ptr, packlength, number); } - - /** - Return the packed length plus the length of the data. - - This is used to determine the size of the data plus the - packed length portion in the row data. - - @returns The length in the row plus the size of the data. - */ - uint32 get_packed_size(const uchar *ptr_arg) - {return packlength + get_length(ptr_arg, packlength);} - inline uint32 get_length(uint row_offset= 0) { return get_length(ptr+row_offset, this->packlength); } uint32 get_length(const uchar *ptr, uint packlength); @@ -2464,7 +2453,7 @@ public: uint max_packed_col_length(uint max_length); void free() { value.free(); } inline void clear_temporary() { bzero((uchar*) &value,sizeof(value)); } - friend int field_conv(Field *to,Field *from); + friend int field_conv_incompatible(Field *to,Field *from); uint size_of() const { return sizeof(*this); } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } @@ -2649,9 +2638,9 @@ public: { DBUG_ASSERT(ptr == a || ptr == b); if (ptr == a) - return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len)); + return Field_bit::key_cmp(b, bytes_in_rec + MY_TEST(bit_len)); else - return Field_bit::key_cmp(a, bytes_in_rec+test(bit_len)) * -1; + return Field_bit::key_cmp(a, bytes_in_rec + MY_TEST(bit_len)) * -1; } int cmp_binary_offset(uint row_offset) { return cmp_offset(row_offset); } diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 71ab4265ee3..f13694e2a13 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -399,7 +399,7 @@ static void do_field_int(Copy_field *copy) { longlong value= copy->from_field->val_int(); copy->to_field->store(value, - test(copy->from_field->flags & UNSIGNED_FLAG)); + MY_TEST(copy->from_field->flags & UNSIGNED_FLAG)); } static void do_field_real(Copy_field *copy) @@ -823,40 +823,76 @@ Copy_field::get_copy_func(Field *to,Field *from) return do_field_eq; } +/** + Check if it is possible just copy value of the fields + + @param to The field to copy to + @param from The field to copy from + + @retval TRUE - it is possible to just copy value of 'from' to 'to'. + @retval FALSE - conversion is needed +*/ + +bool memcpy_field_possible(Field *to,Field *from) +{ + const enum_field_types to_real_type= to->real_type(); + const enum_field_types from_real_type= from->real_type(); + const enum_field_types to_type= from->type(); + return (to_real_type == from_real_type && + !(to->flags & BLOB_FLAG && to->table->copy_blobs) && + to->pack_length() == from->pack_length() && + !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && + to->decimals() == from->decimals() && + to_real_type != MYSQL_TYPE_ENUM && + to_real_type != MYSQL_TYPE_SET && + to_real_type != MYSQL_TYPE_BIT && + (to_real_type != MYSQL_TYPE_NEWDECIMAL || + to->field_length == from->field_length) && + from->charset() == to->charset() && + (!sql_mode_for_dates(to->table->in_use) || + (to_type != MYSQL_TYPE_DATE && + to_type != MYSQL_TYPE_DATETIME)) && + (from_real_type != MYSQL_TYPE_VARCHAR || + ((Field_varstring*)from)->length_bytes == + ((Field_varstring*)to)->length_bytes)); +} + /** Simple quick field convert that is called on insert. */ int field_conv(Field *to,Field *from) { - if (to->real_type() == from->real_type() && - !(to->flags & BLOB_FLAG && to->table->copy_blobs)) - { - if (to->pack_length() == from->pack_length() && - !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && - to->decimals() == from->decimals() && - to->real_type() != MYSQL_TYPE_ENUM && - to->real_type() != MYSQL_TYPE_SET && - to->real_type() != MYSQL_TYPE_BIT && - (to->real_type() != MYSQL_TYPE_NEWDECIMAL || - to->field_length == from->field_length) && - from->charset() == to->charset() && - (!sql_mode_for_dates(to->table->in_use) || - (to->type() != MYSQL_TYPE_DATE && - to->type() != MYSQL_TYPE_DATETIME)) && - (from->real_type() != MYSQL_TYPE_VARCHAR || - ((Field_varstring*)from)->length_bytes == - ((Field_varstring*)to)->length_bytes)) - { // Identical fields - /* - This may happen if one does 'UPDATE ... SET x=x' - The test is here mostly for valgrind, but can also be relevant - if memcpy() is implemented with prefetch-write - */ - if (to->ptr != from->ptr) - memcpy(to->ptr,from->ptr,to->pack_length()); - return 0; - } + if (memcpy_field_possible(to, from)) + { // Identical fields + /* + This may happen if one does 'UPDATE ... SET x=x' + The test is here mostly for valgrind, but can also be relevant + if memcpy() is implemented with prefetch-write + */ + if (to->ptr != from->ptr) + memcpy(to->ptr, from->ptr, to->pack_length()); + return 0; } + return field_conv_incompatible(to, from); +} + + +/** + Copy value of the field with conversion. + + @note Impossibility of simple copy should be checked before this call. + + @param to The field to copy to + @param from The field to copy from + + @retval TRUE ERROR + @retval FALSE OK +*/ + +int field_conv_incompatible(Field *to, Field *from) +{ + const enum_field_types to_real_type= to->real_type(); + const enum_field_types from_real_type= from->real_type(); if (to->flags & BLOB_FLAG) { // Be sure the value is stored Field_blob *blob=(Field_blob*) to; @@ -867,21 +903,22 @@ int field_conv(Field *to,Field *from) */ if (to->table->copy_blobs || (!blob->value.is_alloced() && - from->real_type() != MYSQL_TYPE_STRING && - from->real_type() != MYSQL_TYPE_VARCHAR)) + from_real_type != MYSQL_TYPE_STRING && + from_real_type != MYSQL_TYPE_VARCHAR)) blob->value.copy(); return blob->store(blob->value.ptr(),blob->value.length(),from->charset()); } - if (from->real_type() == MYSQL_TYPE_ENUM && - to->real_type() == MYSQL_TYPE_ENUM && + if (from_real_type == MYSQL_TYPE_ENUM && + to_real_type == MYSQL_TYPE_ENUM && from->val_int() == 0) { ((Field_enum *)(to))->store_type(0); return 0; } - if (from->result_type() == REAL_RESULT) + Item_result from_result_type= from->result_type(); + if (from_result_type == REAL_RESULT) return to->store(from->val_real()); - if (from->result_type() == DECIMAL_RESULT) + if (from_result_type == DECIMAL_RESULT) { my_decimal buff; return to->store_decimal(from->val_decimal(&buff)); @@ -894,10 +931,10 @@ int field_conv(Field *to,Field *from) else return to->store_time_dec(<ime, from->decimals()); } - if ((from->result_type() == STRING_RESULT && + if ((from_result_type == STRING_RESULT && (to->result_type() == STRING_RESULT || - (from->real_type() != MYSQL_TYPE_ENUM && - from->real_type() != MYSQL_TYPE_SET))) || + (from_real_type != MYSQL_TYPE_ENUM && + from_real_type != MYSQL_TYPE_SET))) || to->type() == MYSQL_TYPE_DECIMAL) { char buff[MAX_FIELD_WIDTH]; @@ -911,5 +948,5 @@ int field_conv(Field *to,Field *from) */ return to->store(result.c_ptr_quick(),result.length(),from->charset()); } - return to->store(from->val_int(), test(from->flags & UNSIGNED_FLAG)); + return to->store(from->val_int(), MY_TEST(from->flags & UNSIGNED_FLAG)); } diff --git a/sql/filesort.cc b/sql/filesort.cc index 2b0a91bf5d0..12b9bb5aadc 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -39,6 +39,7 @@ #include "sql_select.h" #include "log_slow.h" #include "debug_sync.h" +#include "sql_base.h" /// How to write record_ref. #define WRITE_REF(file,from) \ @@ -187,6 +188,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, my_b_clear(&buffpek_pointers); buffpek=0; error= 1; + *found_rows= HA_POS_ERROR; param.init_for_filesort(sortlength(thd, sortorder, s_length, &multi_byte_charset), @@ -386,6 +388,14 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, { int kill_errno= thd->killed_errno(); DBUG_ASSERT(thd->is_error() || kill_errno || thd->killed == ABORT_QUERY); + + /* + We replace the table->sort at the end. + Hence calling free_io_cache to make sure table->sort.io_cache + used for QUICK_INDEX_MERGE_SELECT is free. + */ + free_io_cache(table); + my_printf_error(ER_FILSORT_ABORT, "%s: %s", MYF(0), @@ -411,6 +421,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DBUG_POP(); /* Ok to DBUG */ #endif + /* table->sort.io_cache should be free by this time */ + DBUG_ASSERT(NULL == table->sort.io_cache); + // Assign the copy back! table->sort= table_sort; @@ -469,6 +482,84 @@ static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count, } #ifndef DBUG_OFF + +/* Buffer where record is returned */ +char dbug_print_row_buff[512]; + +/* Temporary buffer for printing a column */ +char dbug_print_row_buff_tmp[512]; + +/* + Print table's current row into a buffer and return a pointer to it. + + This is intended to be used from gdb: + + (gdb) p dbug_print_table_row(table) + $33 = "SUBQUERY2_t1(col_int_key,col_varchar_nokey)=(7,c)" + (gdb) + + Only columns in table->read_set are printed +*/ + +const char* dbug_print_table_row(TABLE *table) +{ + Field **pfield; + String tmp(dbug_print_row_buff_tmp, + sizeof(dbug_print_row_buff_tmp),&my_charset_bin); + + String output(dbug_print_row_buff, sizeof(dbug_print_row_buff), + &my_charset_bin); + + output.length(0); + output.append(table->alias); + output.append("("); + bool first= true; + + for (pfield= table->field; *pfield ; pfield++) + { + if (table->read_set && !bitmap_is_set(table->read_set, (*pfield)->field_index)) + continue; + + if (first) + first= false; + else + output.append(","); + + output.append((*pfield)->field_name? (*pfield)->field_name: "NULL"); + } + + output.append(")=("); + + first= true; + for (pfield= table->field; *pfield ; pfield++) + { + Field *field= *pfield; + + if (table->read_set && !bitmap_is_set(table->read_set, (*pfield)->field_index)) + continue; + + if (first) + first= false; + else + output.append(","); + + if (field->is_null()) + output.append("NULL"); + else + { + if (field->type() == MYSQL_TYPE_BIT) + (void) field->val_int_as_str(&tmp, 1); + else + field->val_str(&tmp); + output.append(tmp.ptr(), tmp.length()); + } + } + output.append(")"); + + return output.c_ptr_safe(); +} + + /* Print a text, SQL-like record representation into dbug trace. @@ -517,6 +608,7 @@ static void dbug_print_record(TABLE *table, bool print_rowid) fprintf(DBUG_FILE, "\n"); DBUG_UNLOCK_FILE; } + #endif @@ -595,7 +687,7 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select, ref_pos= ref_buff; quick_select=select && select->quick; record=0; - *found_rows= 0; + *found_rows= pq ? 0 : HA_POS_ERROR; // don't count unless pq is used flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select); if (flag) ref_pos= &file->ref[0]; @@ -640,6 +732,7 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select, DBUG_RETURN(HA_POS_ERROR); } + DEBUG_SYNC(thd, "after_index_merge_phase1"); for (;;) { if (quick_select) @@ -714,9 +807,14 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select, if (write_record) { - ++(*found_rows); if (pq) { + /* + only count rows when pq is used - otherwise there might be + other filters *after* the filesort, we don't know the final row + count here + */ + (*found_rows)++; pq->push(ref_pos); idx= pq->num_elements(); } @@ -732,12 +830,17 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select, make_sortkey(param, fs_info->get_record_buffer(idx++), ref_pos); } } - else - file->unlock_row(); /* It does not make sense to read more keys in case of a fatal error */ if (thd->is_error()) break; + + /* + We need to this after checking the error as the transaction may have + rolled back in case of a deadlock + */ + if (!write_record) + file->unlock_row(); } if (!quick_select) { @@ -880,13 +983,11 @@ static void make_sortkey(register Sort_param *param, { const CHARSET_INFO *cs=item->collation.collation; char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' '); - int diff; - uint sort_field_length; if (maybe_null) *to++=1; - /* All item->str() to use some extra byte for end null.. */ - String tmp((char*) to,sort_field->length+4,cs); + char *tmp_buffer= param->tmp_buffer ? param->tmp_buffer : (char*)to; + String tmp(tmp_buffer, param->sort_length, cs); String *res= item->str_result(&tmp); if (!res) { @@ -909,39 +1010,36 @@ static void make_sortkey(register Sort_param *param, break; } length= res->length(); - sort_field_length= sort_field->length - sort_field->suffix_length; - diff=(int) (sort_field_length - length); - if (diff < 0) - { - diff=0; - length= sort_field_length; - } - if (sort_field->suffix_length) - { - /* Store length last in result_string */ - store_length(to + sort_field_length, length, - sort_field->suffix_length); - } if (sort_field->need_strxnfrm) { - char *from=(char*) res->ptr(); uint tmp_length __attribute__((unused)); - if ((uchar*) from == to) - { - set_if_smaller(length,sort_field->length); - memcpy(param->tmp_buffer,from,length); - from=param->tmp_buffer; - } tmp_length= cs->coll->strnxfrm(cs, to, sort_field->length, item->max_char_length() * cs->strxfrm_multiply, - (uchar*) from, length, + (uchar*) res->ptr(), length, MY_STRXFRM_PAD_WITH_SPACE | MY_STRXFRM_PAD_TO_MAXLEN); DBUG_ASSERT(tmp_length == sort_field->length); } else { + uint diff; + uint sort_field_length= sort_field->length - + sort_field->suffix_length; + if (sort_field_length < length) + { + diff= 0; + length= sort_field_length; + } + else + diff= sort_field_length - length; + if (sort_field->suffix_length) + { + /* Store length last in result_string */ + store_length(to + sort_field_length, length, + sort_field->suffix_length); + } + /* apply cs->sort_order for case-insensitive comparison if needed */ my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length); cs->cset->fill(cs, (char *)to+length,diff,fill_char); } diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index fc53183ca7a..531211eb175 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -446,7 +446,7 @@ int ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP)); for (i= 0; i < no_nodes; i++) { - bitmap_init(&share->subscriber_bitmap[i], + my_bitmap_init(&share->subscriber_bitmap[i], (Uint32*)alloc_root(mem_root, max_ndb_nodes/8), max_ndb_nodes, FALSE); bitmap_clear_all(&share->subscriber_bitmap[i]); @@ -1119,7 +1119,7 @@ ndbcluster_update_slock(THD *thd, MY_BITMAP slock; uint32 bitbuf[SCHEMA_SLOCK_SIZE/4]; - bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false); + my_bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false); if (ndbtab == 0) { @@ -1370,7 +1370,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, { int i, updated= 0; int no_storage_nodes= g_ndb_cluster_connection->no_db_nodes(); - bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE); + my_bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE); bitmap_set_all(&schema_subscribers); /* begin protect ndb_schema_share */ @@ -1908,7 +1908,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, Cluster_schema *schema= (Cluster_schema *) sql_alloc(sizeof(Cluster_schema)); MY_BITMAP slock; - bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, FALSE); + my_bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, FALSE); uint node_id= g_ndb_cluster_connection->node_id(); { ndbcluster_get_schema(tmp_share, schema); @@ -3353,7 +3353,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, MY_BITMAP b; /* Potential buffer for the bitmap */ uint32 bitbuf[128 / (sizeof(uint32) * 8)]; - bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL, + my_bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL, n_fields, FALSE); bitmap_set_all(&b); @@ -3573,7 +3573,7 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key, break; } mysql_mutex_init(key_ndb_schema_object_mutex, &ndb_schema_object->mutex, MY_MUTEX_INIT_FAST); - bitmap_init(&ndb_schema_object->slock_bitmap, ndb_schema_object->slock, + my_bitmap_init(&ndb_schema_object->slock_bitmap, ndb_schema_object->slock, sizeof(ndb_schema_object->slock)*8, FALSE); bitmap_clear_all(&ndb_schema_object->slock_bitmap); break; diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc index 22a7dbe55f7..fd80304d400 100644 --- a/sql/ha_ndbcluster_cond.cc +++ b/sql/ha_ndbcluster_cond.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (c) 2000, 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 diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h index 27675588ed7..952b705bfc2 100644 --- a/sql/ha_ndbcluster_cond.h +++ b/sql/ha_ndbcluster_cond.h @@ -1,7 +1,7 @@ #ifndef HA_NDBCLUSTER_COND_INCLUDED #define HA_NDBCLUSTER_COND_INCLUDED -/* Copyright (C) 2000-2007 MySQL AB +/* Copyright (c) 2000, 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 @@ -350,18 +350,18 @@ class Ndb_cond_traverse_context : public Sql_alloc skip(0), collation(NULL), rewrite_stack(NULL) { // Allocate type checking bitmaps - bitmap_init(&expect_mask, 0, 512, FALSE); - bitmap_init(&expect_field_type_mask, 0, 512, FALSE); - bitmap_init(&expect_field_result_mask, 0, 512, FALSE); + my_bitmap_init(&expect_mask, 0, 512, FALSE); + my_bitmap_init(&expect_field_type_mask, 0, 512, FALSE); + my_bitmap_init(&expect_field_result_mask, 0, 512, FALSE); if (stack) cond_ptr= stack->ndb_cond; }; ~Ndb_cond_traverse_context() { - bitmap_free(&expect_mask); - bitmap_free(&expect_field_type_mask); - bitmap_free(&expect_field_result_mask); + my_bitmap_free(&expect_mask); + my_bitmap_free(&expect_field_type_mask); + my_bitmap_free(&expect_field_result_mask); if (rewrite_stack) delete rewrite_stack; } void expect(Item::Type type) diff --git a/sql/ha_ndbcluster_tables.h b/sql/ha_ndbcluster_tables.h index 6ed46123738..4d97ca2c254 100644 --- a/sql/ha_ndbcluster_tables.h +++ b/sql/ha_ndbcluster_tables.h @@ -1,7 +1,8 @@ #ifndef HA_NDBCLUSTER_TABLES_INCLUDED #define HA_NDBCLUSTER_TABLES_INCLUDED -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (c) 2000-2003, 2006, 2007 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 diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 603e0bf59dc..6f8d4b4af14 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -89,12 +89,13 @@ static handler *partition_create_handler(handlerton *hton, static uint partition_flags(); static uint alter_table_flags(uint flags); +extern "C" int cmp_key_then_part_id(void *key_p, 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 rename_table and delete_table method in handler.cc. */ - static const char *ha_partition_ext[]= { ha_par_ext, NullS @@ -1488,7 +1489,8 @@ bool ha_partition::is_crashed() const int ha_partition::prepare_new_partition(TABLE *tbl, HA_CREATE_INFO *create_info, handler *file, const char *part_name, - partition_element *p_elem) + partition_element *p_elem, + uint disable_non_uniq_indexes) { int error; DBUG_ENTER("prepare_new_partition"); @@ -1531,6 +1533,7 @@ int ha_partition::prepare_new_partition(TABLE *tbl, m_open_test_lock | HA_OPEN_NO_PSI_CALL))) goto error_open; DBUG_PRINT("info", ("partition %s opened", part_name)); + /* Note: if you plan to add another call that may return failure, better to do it before external_lock() as cleanup_new_partition() @@ -1541,6 +1544,9 @@ int ha_partition::prepare_new_partition(TABLE *tbl, goto error_external_lock; DBUG_PRINT("info", ("partition %s external locked", part_name)); + if (disable_non_uniq_indexes) + file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + DBUG_RETURN(0); error_external_lock: (void) file->ha_close(); @@ -1836,6 +1842,14 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, on them to prepare them for copy phase and also for later close calls */ + + /* + Before creating new partitions check whether indexes are disabled + in the partitions. + */ + + uint disable_non_uniq_indexes = indexes_are_disabled(); + i= 0; part_count= 0; part_it.rewind(); @@ -1870,11 +1884,13 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, if ((error= prepare_new_partition(table, create_info, new_file_array[part], (const char *)part_name_buff, - sub_elem))) + sub_elem, + disable_non_uniq_indexes))) { cleanup_new_partition(part_count); DBUG_RETURN(error); } + m_added_file[part_count++]= new_file_array[part]; } while (++j < num_subparts); } @@ -1887,11 +1903,13 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, if ((error= prepare_new_partition(table, create_info, new_file_array[i], (const char *)part_name_buff, - part_elem))) + part_elem, + disable_non_uniq_indexes))) { cleanup_new_partition(part_count); DBUG_RETURN(error); } + m_added_file[part_count++]= new_file_array[i]; } } @@ -3301,10 +3319,10 @@ err: void ha_partition::free_partition_bitmaps() { /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ - bitmap_free(&m_bulk_insert_started); - bitmap_free(&m_locked_partitions); - bitmap_free(&m_partitions_to_reset); - bitmap_free(&m_key_not_found_partitions); + my_bitmap_free(&m_bulk_insert_started); + my_bitmap_free(&m_locked_partitions); + my_bitmap_free(&m_partitions_to_reset); + my_bitmap_free(&m_key_not_found_partitions); } @@ -3316,14 +3334,14 @@ 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 (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) + 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 (bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE)) { - bitmap_free(&m_bulk_insert_started); + my_bitmap_free(&m_bulk_insert_started); DBUG_RETURN(true); } bitmap_clear_all(&m_locked_partitions); @@ -3332,10 +3350,10 @@ bool ha_partition::init_partition_bitmaps() Initialize the bitmap we use to keep track of partitions which may have something to reset in ha_reset(). */ - if (bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE)) { - bitmap_free(&m_bulk_insert_started); - bitmap_free(&m_locked_partitions); + my_bitmap_free(&m_bulk_insert_started); + my_bitmap_free(&m_locked_partitions); DBUG_RETURN(true); } bitmap_clear_all(&m_partitions_to_reset); @@ -3344,11 +3362,11 @@ bool ha_partition::init_partition_bitmaps() Initialize the bitmap we use to keep track of partitions which returned HA_ERR_KEY_NOT_FOUND from index_read_map. */ - if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) { - bitmap_free(&m_bulk_insert_started); - bitmap_free(&m_locked_partitions); - bitmap_free(&m_partitions_to_reset); + 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); @@ -3404,7 +3422,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_mode= mode; m_open_test_lock= test_if_locked; m_part_field_array= m_part_info->full_part_field_array; - if (get_from_handler_file(name, &table->mem_root, test(m_is_clone_of))) + if (get_from_handler_file(name, &table->mem_root, MY_TEST(m_is_clone_of))) DBUG_RETURN(error); name_buffer_ptr= m_name_buffer_ptr; if (populate_partition_name_hash()) @@ -5102,8 +5120,8 @@ bool ha_partition::init_record_priority_queue() } m_start_key.key= (const uchar*)ptr; /* Initialize priority queue, initialized to reading forward. */ - if (init_queue(&m_queue, used_parts, (uint) PARTITION_BYTES_IN_POS, - 0, key_rec_cmp, (void*)m_curr_key_info, 0, 0)) + if (init_queue(&m_queue, used_parts, 0, + 0, cmp_key_then_part_id, (void*)m_curr_key_info, 0, 0)) { my_free(m_ordered_rec_buffer); m_ordered_rec_buffer= NULL; @@ -5313,6 +5331,52 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key, } +/* + @brief + Provide ordering by (key_value, partition_id). + + @detail + Ordering by partition id is required so that key scans on key=const + return rows in rowid order (this is required for some variants of + index_merge to work). + + In ha_partition, rowid is a (partition_id, underlying_table_rowid). + handle_ordered_index_scan must return rows ordered by (key, rowid). + + If two rows have the same key value and come from different partitions, + it is sufficient to return them in the order of their partition_id. +*/ + +extern "C" int cmp_key_then_part_id(void *key_p, uchar *ref1, uchar *ref2) +{ + my_ptrdiff_t diff1, diff2; + int res; + + if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS, + ref2 + PARTITION_BYTES_IN_POS))) + { + return res; + } + + /* The following was taken from ha_partition::cmp_ref */ + diff1= ref2[1] - ref1[1]; + 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); +} + + /** Common routine for a number of index_read variants @@ -5677,7 +5741,7 @@ int ha_partition::read_range_first(const key_range *start_key, m_start_key.key= NULL; m_index_scan_type= partition_read_range; - error= common_index_read(m_rec0, test(start_key)); + error= common_index_read(m_rec0, MY_TEST(start_key)); DBUG_RETURN(error); } @@ -6134,7 +6198,7 @@ void ha_partition::return_top_record(uchar *buf) int ha_partition::handle_ordered_index_scan_key_not_found() { int error; - uint i; + uint i, old_elements= m_queue.elements; uchar *part_buf= m_ordered_rec_buffer; uchar *curr_rec_buf= NULL; DBUG_ENTER("ha_partition::handle_ordered_index_scan_key_not_found"); @@ -6168,9 +6232,12 @@ int ha_partition::handle_ordered_index_scan_key_not_found() bitmap_clear_all(&m_key_not_found_partitions); m_key_not_found= false; - /* Update m_top_entry, which may have changed. */ - uchar *key_buffer= queue_top(&m_queue); - m_top_entry= uint2korr(key_buffer); + if (m_queue.elements > old_elements) + { + /* Update m_top_entry, which may have changed. */ + uchar *key_buffer= queue_top(&m_queue); + m_top_entry= uint2korr(key_buffer); + } DBUG_RETURN(0); } @@ -7672,8 +7739,8 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array) ulong nr1= 1; ulong nr2= 4; bool use_51_hash; - use_51_hash= test((*field_array)->table->part_info->key_algorithm == - partition_info::KEY_ALGORITHM_51); + use_51_hash= MY_TEST((*field_array)->table->part_info->key_algorithm == + partition_info::KEY_ALGORITHM_51); do { @@ -7870,7 +7937,10 @@ void ha_partition::print_error(int error, myf errflag) if ((error == HA_ERR_NO_PARTITION_FOUND) && ! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION)) - m_part_info->print_no_partition_found(table); + { + m_part_info->print_no_partition_found(table, errflag); + DBUG_VOID_RETURN; + } else if (error == HA_ERR_ROW_IN_WRONG_PARTITION) { /* Should only happen on DELETE or UPDATE! */ @@ -8428,6 +8498,16 @@ uint ha_partition::min_record_length(uint options) const If they belong to different partitions we decide that they are not 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: + 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. + If they belong to different partitions we decide that they are not + the same record. Otherwise we use the particular handler to decide if + they are the same. Sort in partition id order if not equal. + + It is incorrect, MariaDB has an alternative fix. */ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 7407388b7b2..05c2bc7072f 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -343,7 +343,8 @@ private: void cleanup_new_partition(uint part_count); int prepare_new_partition(TABLE *table, HA_CREATE_INFO *create_info, handler *file, const char *part_name, - partition_element *p_elem); + partition_element *p_elem, + uint disable_non_uniq_indexes); /* delete_table and rename_table uses very similar logic which is packed into this routine. diff --git a/sql/handler.cc b/sql/handler.cc index 4c797259e2c..40d53170557 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -389,12 +389,13 @@ static int ha_finish_errors(void) static volatile int32 need_full_discover_for_existence= 0; static volatile int32 engines_with_discover_table_names= 0; +static volatile int32 engines_with_discover= 0; static int full_discover_for_existence(handlerton *, const char *, const char *) -{ return 1; } +{ return 0; } static int ext_based_existence(handlerton *, const char *, const char *) -{ return 1; } +{ return 0; } static int hton_ext_based_table_discovery(handlerton *hton, LEX_STRING *db, MY_DIR *dir, handlerton::discovered_list *result) @@ -414,6 +415,9 @@ static void update_discovery_counters(handlerton *hton, int val) if (hton->discover_table_names) my_atomic_add32(&engines_with_discover_table_names, val); + + if (hton->discover_table) + my_atomic_add32(&engines_with_discover, val); } int ha_finalize_handlerton(st_plugin_int *plugin) @@ -1254,7 +1258,8 @@ int ha_commit_trans(THD *thd, bool all) the changes are not durable as they might be rolled back if the enclosing 'all' transaction is rolled back. */ - bool is_real_trans= all || thd->transaction.all.ha_list == 0; + bool is_real_trans= ((all || thd->transaction.all.ha_list == 0) && + !(thd->variables.option_bits & OPTION_GTID_BEGIN)); Ha_trx_info *ha_info= trans->ha_list; bool need_prepare_ordered, need_commit_ordered; my_xid xid; @@ -1269,7 +1274,7 @@ int ha_commit_trans(THD *thd, bool all) ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));); DBUG_PRINT("info", - ("all: %d thd->in_sub_stmt: %d ha_info: %p is_real_trans: %d", + ("all: %d thd->in_sub_stmt: %d ha_info: %p is_real_trans: %d", all, thd->in_sub_stmt, ha_info, is_real_trans)); /* We must not commit the normal transaction if a statement @@ -1311,7 +1316,10 @@ int ha_commit_trans(THD *thd, bool all) Free resources and perform other cleanup even for 'empty' transactions. */ if (is_real_trans) - thd->transaction.cleanup(); + { + thd->transaction.cleanup(); + thd->wakeup_subsequent_commits(error); + } DBUG_RETURN(0); } @@ -1350,6 +1358,7 @@ int ha_commit_trans(THD *thd, bool all) thd->variables.lock_wait_timeout)) { ha_rollback_trans(thd, all); + thd->wakeup_subsequent_commits(1); DBUG_RETURN(1); } @@ -1461,6 +1470,7 @@ done: err: error= 1; /* Transaction was rolled back */ ha_rollback_trans(thd, all); + thd->wakeup_subsequent_commits(error); end: if (rw_trans && mdl_request.ticket) @@ -1503,11 +1513,16 @@ int ha_commit_one_phase(THD *thd, bool all) ha_commit_one_phase() can be called with an empty transaction.all.ha_list, see why in trans_register_ha()). */ - bool is_real_trans=all || thd->transaction.all.ha_list == 0; + bool is_real_trans= ((all || thd->transaction.all.ha_list == 0) && + !(thd->variables.option_bits & OPTION_GTID_BEGIN)); int res; DBUG_ENTER("ha_commit_one_phase"); - if (is_real_trans && (res= thd->wait_for_prior_commit())) - DBUG_RETURN(res); + if (is_real_trans) + { + DEBUG_SYNC(thd, "ha_commit_one_phase"); + if ((res= thd->wait_for_prior_commit())) + DBUG_RETURN(res); + } res= commit_one_phase_2(thd, all, trans, is_real_trans); DBUG_RETURN(res); } @@ -1519,7 +1534,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) int error= 0; Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; DBUG_ENTER("commit_one_phase_2"); - + if (is_real_trans) + DEBUG_SYNC(thd, "commit_one_phase_2"); if (ha_info) { for (; ha_info; ha_info= ha_info_next) @@ -1632,10 +1648,7 @@ int ha_rollback_trans(THD *thd, bool all) /* Always cleanup. Even if nht==0. There may be savepoints. */ if (is_real_trans) - { - thd->wakeup_subsequent_commits(error); thd->transaction.cleanup(); - } if (all) thd->transaction_rollback_request= FALSE; @@ -2498,7 +2511,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, cached_table_flags= table_flags(); } reset_statistics(); - internal_tmp_table= test(test_if_locked & HA_OPEN_INTERNAL_TABLE); + internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE); DBUG_RETURN(error); } @@ -3537,7 +3550,7 @@ void handler::print_error(int error, myf errflag) } } else - my_error(ER_GET_ERRNO, errflag, error, table_type()); + my_error(ER_GET_ERRNO, errflag, error, table_type()); DBUG_VOID_RETURN; } } @@ -4153,6 +4166,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH | Alter_inplace_info::ALTER_COLUMN_NAME | Alter_inplace_info::ALTER_COLUMN_DEFAULT | + Alter_inplace_info::ALTER_COLUMN_OPTION | Alter_inplace_info::CHANGE_CREATE_OPTION | Alter_inplace_info::ALTER_RENAME; @@ -4641,14 +4655,16 @@ int ha_create_table(THD *thd, const char *path, error= table.file->ha_create(name, &table, create_info); - (void) closefrm(&table, 0); - if (error) { - my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error); + if (!thd->is_error()) + my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error); + table.file->print_error(error, MYF(ME_JUST_WARNING)); PSI_CALL_drop_table_share(temp_table, share.db.str, share.db.length, share.table_name.str, share.table_name.length); } + + (void) closefrm(&table, 0); err: free_table_share(&share); @@ -4819,7 +4835,9 @@ int ha_discover_table(THD *thd, TABLE_SHARE *share) DBUG_ASSERT(share->error == OPEN_FRM_OPEN_ERROR); // share is not OK yet - if (share->db_plugin) + if (!engines_with_discover) + found= FALSE; + else if (share->db_plugin) found= discover_handlerton(thd, share->db_plugin, share); else found= plugin_foreach(thd, discover_handlerton, @@ -4843,6 +4861,7 @@ struct st_discover_existence_args size_t path_len; const char *db, *table_name; handlerton *hton; + bool frm_exists; }; static my_bool discover_existence(THD *thd, plugin_ref plugin, @@ -4851,7 +4870,7 @@ static my_bool discover_existence(THD *thd, plugin_ref plugin, st_discover_existence_args *args= (st_discover_existence_args*)arg; handlerton *ht= plugin_hton(plugin); if (ht->state != SHOW_OPTION_YES || !ht->discover_table_existence) - return FALSE; + return args->frm_exists; args->hton= ht; @@ -4906,40 +4925,36 @@ private: If the 'hton' is not NULL, it's set to the handlerton of the storage engine of this table, or to view_pseudo_hton if the frm belongs to a view. + This function takes discovery correctly into account. If frm is found, + it discovers the table to make sure it really exists in the engine. + If no frm is found it discovers the table, in case it still exists in + the engine. + + While it tries to cut corners (don't open .frm if no discovering engine is + enabled, no full discovery if all discovering engines support + discover_table_existence, etc), it still *may* be quite expensive + and must be used sparingly. @retval true Table exists (even if the error occurred, like bad frm) @retval false Table does not exist (one can do CREATE TABLE table_name) + + @note if frm exists and the table in engine doesn't, *hton will be set, + but the return value will be false. + + @note if frm file exists, but the table cannot be opened (engine not + loaded, frm is invalid), the return value will be true, but + *hton will be NULL. */ bool ha_table_exists(THD *thd, const char *db, const char *table_name, handlerton **hton) { + handlerton *dummy; DBUG_ENTER("ha_table_exists"); if (hton) *hton= 0; - - if (need_full_discover_for_existence) - { - TABLE_LIST table; - uint flags = GTS_TABLE | GTS_VIEW; - - if (!hton) - flags|= GTS_NOLOCK; - - Table_exists_error_handler no_such_table_handler; - thd->push_internal_handler(&no_such_table_handler); - TABLE_SHARE *share= tdc_acquire_share(thd, db, table_name, flags); - thd->pop_internal_handler(); - - if (hton && share) - { - *hton= share->db_type(); - tdc_release_share(share); - } - - // the table doesn't exist if we've caught ER_NO_SUCH_TABLE and nothing else - DBUG_RETURN(!no_such_table_handler.safely_trapped_errors()); - } + else if (engines_with_discover) + hton= &dummy; TABLE_SHARE *share= tdc_lock_share(db, table_name); if (share) @@ -4953,22 +4968,29 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, char path[FN_REFLEN + 1]; size_t path_len = build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); + st_discover_existence_args args= {path, path_len, db, table_name, 0, true}; if (file_ext_exists(path, path_len, reg_ext)) { + bool exists= true; if (hton) { enum legacy_db_type db_type; if (dd_frm_type(thd, path, &db_type) != FRMTYPE_VIEW) - *hton= ha_resolve_by_legacy_type(thd, db_type); + { + handlerton *ht= ha_resolve_by_legacy_type(thd, db_type); + if ((*hton= ht)) + // verify that the table really exists + exists= discover_existence(thd, + plugin_int_to_ref(hton2plugin[ht->slot]), &args); + } else *hton= view_pseudo_hton; } - DBUG_RETURN(TRUE); + DBUG_RETURN(exists); } - st_discover_existence_args args= {path, path_len, db, table_name, 0}; - + args.frm_exists= false; if (plugin_foreach(thd, discover_existence, MYSQL_STORAGE_ENGINE_PLUGIN, &args)) { @@ -4977,6 +4999,30 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, DBUG_RETURN(TRUE); } + + if (need_full_discover_for_existence) + { + TABLE_LIST table; + uint flags = GTS_TABLE | GTS_VIEW; + + if (!hton) + flags|= GTS_NOLOCK; + + Table_exists_error_handler no_such_table_handler; + thd->push_internal_handler(&no_such_table_handler); + TABLE_SHARE *share= tdc_acquire_share(thd, db, table_name, flags); + thd->pop_internal_handler(); + + if (hton && share) + { + *hton= share->db_type(); + tdc_release_share(share); + } + + // the table doesn't exist if we've caught ER_NO_SUCH_TABLE and nothing else + DBUG_RETURN(!no_such_table_handler.safely_trapped_errors()); + } + DBUG_RETURN(FALSE); } @@ -5606,8 +5652,10 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) "", 0, "DISABLED", 8) ? 1 : 0; } else + { result= db_type->show_status && db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0; + } } /* @@ -5767,7 +5815,7 @@ static int binlog_log_row(TABLE* table, the first row handled in this statement. In that case, we need to write table maps for all locked tables to the binary log. */ - if (likely(!(error= bitmap_init(&cols, + if (likely(!(error= my_bitmap_init(&cols, use_bitbuf ? bitbuf : NULL, (n_fields + 7) & ~7UL, FALSE)))) @@ -5789,7 +5837,7 @@ static int binlog_log_row(TABLE* table, before_record, after_record); } if (!use_bitbuf) - bitmap_free(&cols); + my_bitmap_free(&cols); } } return error ? HA_ERR_RBR_LOGGING_FAILED : 0; diff --git a/sql/handler.h b/sql/handler.h index eadee821303..e9491cbf88a 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -32,6 +32,7 @@ #include "sql_cache.h" #include "structs.h" /* SHOW_COMP_OPTION */ #include "sql_array.h" /* Dynamic_array<> */ +#include "mdl.h" #include <my_compare.h> #include <ft_global.h> @@ -248,6 +249,15 @@ enum enum_alter_inplace_result { #define HA_CAN_FULLTEXT_EXT (1LL << 44) /* + Storage engine supports table export using the + FLUSH TABLE <table_list> FOR EXPORT statement + (meaning, after this statement one can copy table files out of the + datadir and later "import" (somehow) in another MariaDB instance) + */ +#define HA_CAN_EXPORT (1LL << 45) + + +/* Set of all binlog flags. Currently only contain the capabilities flags. */ @@ -378,6 +388,7 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_IF_NOT_EXISTS 2 #define HA_LEX_CREATE_TABLE_LIKE 4 #define HA_CREATE_TMP_ALTER 8 +#define HA_LEX_CREATE_REPLACE 16 #define HA_MAX_REC_LENGTH 65535 /* Table caching type */ @@ -1581,9 +1592,15 @@ struct HA_CREATE_INFO ulong avg_row_length; ulong used_fields; ulong key_block_size; - uint stats_sample_pages; /* number of pages to sample during - stats estimation, if used, otherwise 0. */ - enum_stats_auto_recalc stats_auto_recalc; + /* + number of pages to sample during + stats estimation, if used, otherwise 0. + */ + uint stats_sample_pages; + uint null_bits; /* NULL bits at start of record */ + uint options; /* OR of HA_CREATE_ options */ + uint merge_insert_method; + uint extra_size; /* length of extra data segment */ SQL_I_List<TABLE_LIST> merge_list; handlerton *db_type; /** @@ -1596,21 +1613,23 @@ struct HA_CREATE_INFO If nothing speficied inherits the value of the original table (if present). */ enum row_type row_type; - uint null_bits; /* NULL bits at start of record */ - uint options; /* OR of HA_CREATE_ options */ - uint merge_insert_method; - uint extra_size; /* length of extra data segment */ enum ha_choice transactional; - bool varchar; ///< 1 if table has a VARCHAR enum ha_storage_media storage_media; ///< DEFAULT, DISK or MEMORY enum ha_choice page_checksum; ///< If we have page_checksums engine_option_value *option_list; ///< list of table create options + enum_stats_auto_recalc stats_auto_recalc; + bool varchar; ///< 1 if table has a VARCHAR /* the following three are only for ALTER TABLE, check_if_incompatible_data() */ ha_table_option_struct *option_struct; ///< structure with parsed table options ha_field_option_struct **fields_option_struct; ///< array of field option structures ha_index_option_struct **indexes_option_struct; ///< array of index option structures + /* The following is used to remember the old state for CREATE OR REPLACE */ + TABLE *table; + TABLE_LIST *pos_in_locked_tables; + MDL_ticket *mdl_ticket; + bool tmp_table() { return options & HA_LEX_CREATE_TMP_TABLE; } }; @@ -1729,8 +1748,11 @@ public: // Table is renamed static const HA_ALTER_FLAGS ALTER_RENAME = 1L << 18; - // Change the storage type of column - static const HA_ALTER_FLAGS ALTER_COLUMN_STORAGE_TYPE = 1L << 19; + // column's engine options changed, something in field->option_struct + static const HA_ALTER_FLAGS ALTER_COLUMN_OPTION = 1L << 19; + + // MySQL alias for the same thing: + static const HA_ALTER_FLAGS ALTER_COLUMN_STORAGE_TYPE = 1L << 19; // Change the column format of column static const HA_ALTER_FLAGS ALTER_COLUMN_COLUMN_FORMAT = 1L << 20; @@ -1759,7 +1781,7 @@ public: // Partition operation with ALL keyword static const HA_ALTER_FLAGS ALTER_ALL_PARTITION = 1L << 28; - // Partition operation with ALL keyword + // Virtual columns changed static const HA_ALTER_FLAGS ALTER_COLUMN_VCOL = 1L << 29; /** @@ -3932,7 +3954,7 @@ static inline const char *ha_resolve_storage_engine_name(const handlerton *db_ty static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint32 flag) { - return db_type == NULL ? FALSE : test(db_type->flags & flag); + return db_type == NULL ? FALSE : MY_TEST(db_type->flags & flag); } static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) diff --git a/sql/hostname.cc b/sql/hostname.cc index 1200dd2c185..8a4df816057 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2011, Monty Program Ab +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2011, 2014, SkySQL 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 @@ -149,7 +149,7 @@ bool hostname_cache_init() Host_entry tmp; uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp); - if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE, + if (!(hostname_cache= new hash_filo(host_cache_size, key_offset, HOST_ENTRY_KEY_SIZE, NULL, (my_hash_free_key) free, &my_charset_bin))) diff --git a/sql/item.cc b/sql/item.cc index 5bc9d5816eb..0fbd58567ef 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -234,6 +234,36 @@ bool Item::val_bool() } +/** + Get date/time/datetime. + Optionally extend TIME result to DATETIME. +*/ +bool Item::get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ + /* + Some TIME type items return error when trying to do get_date() + without TIME_TIME_ONLY set (e.g. Item_field for Field_time). + In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY. + In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY + and leave it to get_date() to check date. + */ + ulonglong time_flag= (field_type() == MYSQL_TYPE_TIME && + !(current_thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? + TIME_TIME_ONLY : 0; + if (get_date(ltime, fuzzydate | time_flag)) + return true; + if (ltime->time_type == MYSQL_TIMESTAMP_TIME && + !(fuzzydate & TIME_TIME_ONLY)) + { + MYSQL_TIME tmp; + if (time_to_datetime_with_warn(current_thd, ltime, &tmp, fuzzydate)) + return null_value= true; + *ltime= tmp; + } + return false; +} + + /* For the items which don't have its own fast val_str_ascii() implementation we provide a generic slower version, @@ -852,7 +882,7 @@ void Item_ident::cleanup() table_name= orig_table_name; field_name= orig_field_name; /* Store if this Item was depended */ - can_be_depended= test(depended_from); + can_be_depended= MY_TEST(depended_from); DBUG_VOID_RETURN; } @@ -1681,17 +1711,28 @@ bool Item_name_const::is_null() Item_name_const::Item_name_const(Item *name_arg, Item *val): value_item(val), name_item(name_arg) { - if (!(valid_args= name_item->basic_const_item() && - (value_item->basic_const_item() || - ((value_item->type() == FUNC_ITEM) && - ((((Item_func *) value_item)->functype() == - Item_func::COLLATE_FUNC) || - ((((Item_func *) value_item)->functype() == - Item_func::NEG_FUNC) && - (((Item_func *) value_item)->key_item()->type() != - FUNC_ITEM))))))) - my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); Item::maybe_null= TRUE; + valid_args= true; + if (!name_item->basic_const_item()) + goto err; + + if (value_item->basic_const_item()) + return; // ok + + if (value_item->type() == FUNC_ITEM) + { + Item_func *value_func= (Item_func *) value_item; + if (value_func->functype() != Item_func::COLLATE_FUNC && + value_func->functype() != Item_func::NEG_FUNC) + goto err; + + if (value_func->key_item()->basic_const_item()) + return; // ok + } + +err: + valid_args= false; + my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); } @@ -2428,7 +2469,7 @@ void Item_field::set_field(Field *field_par) field_name= field_par->field_name; db_name= field_par->table->s->db.str; alias_name_used= field_par->table->alias_name_used; - unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); + unsigned_flag= MY_TEST(field_par->flags & UNSIGNED_FLAG); collation.set(field_par->charset(), field_par->derivation(), field_par->repertoire()); fix_char_length(field_par->char_length()); @@ -4698,6 +4739,12 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select) /** Resolve the name of an outer select column reference. + @param[in] thd current thread + @param[in,out] from_field found field reference or (Field*)not_found_field + @param[in,out] reference view column if this item was resolved to a + view column + + @description The method resolves the column reference represented by 'this' as a column present in outer selects that contain current select. @@ -4707,10 +4754,16 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select) current select as dependent. The found reference of field should be provided in 'from_field'. - @param[in] thd current thread - @param[in,out] from_field found field reference or (Field*)not_found_field - @param[in,out] reference view column if this item was resolved to a - view column + The cache is critical for prepared statements of type: + + SELECT a FROM (SELECT a FROM test.t1) AS s1 NATURAL JOIN t2 AS s2; + + This is internally converted to a join similar to + + SELECT a FROM t1 AS s1,t2 AS s2 WHERE t2.a=t1.a; + + Without the cache, we would on re-prepare not know if 'a' did match + s1.a or s2.a. @note This is the inner loop of Item_field::fix_fields: @@ -4740,7 +4793,12 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) enum_parsing_place place= NO_MATTER; bool field_found= (*from_field != not_found_field); bool upward_lookup= FALSE; + TABLE_LIST *table_list; + /* Calulate the TABLE_LIST for the table */ + table_list= (cached_table ? cached_table : + field_found && (*from_field) != view_ref_found ? + (*from_field)->table->pos_in_table_list : 0); /* If there are outer contexts (outer selects, but current select is not derived table or view) try to resolve this reference in the @@ -4759,6 +4817,15 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (current_sel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE) outer_context= context->outer_context; + + /* + This assert is to ensure we have an outer contex when *from_field + is set. + If this would not be the case, we would assert in mark_as_dependent + as last_checked_countex == context + */ + DBUG_ASSERT(outer_context || !*from_field || + *from_field == not_found_field); for (; outer_context; outer_context= outer_context->outer_context) @@ -4775,7 +4842,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) to find_field_in_tables(). Only need to find appropriate context. */ if (field_found && outer_context->select_lex != - cached_table->select_lex) + table_list->select_lex) continue; /* In case of a view, find_field_in_tables() writes the pointer to @@ -4974,9 +5041,9 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (last_checked_context->select_lex->having_fix_field) { Item_ref *rf; - rf= new Item_ref(context, - (cached_table->db[0] ? cached_table->db : 0), - (char*) cached_table->alias, (char*) field_name); + rf= new Item_ref(context, (*from_field)->table->s->db.str, + (*from_field)->table->alias.c_ptr(), + (char*) field_name); if (!rf) return -1; thd->change_item_tree(reference, rf); @@ -5047,6 +5114,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (!field) // If field is not checked { + TABLE_LIST *table_list; /* In case of view, find_field_in_tables() write pointer to view field expression to 'reference', i.e. it substitute that expression instead @@ -5132,11 +5200,14 @@ bool Item_field::fix_fields(THD *thd, Item **reference) else if (!from_field) goto error; - if (!outer_fixed && cached_table && cached_table->select_lex && + table_list= (cached_table ? cached_table : + from_field != view_ref_found ? + from_field->table->pos_in_table_list : 0); + if (!outer_fixed && table_list && table_list->select_lex && context->select_lex && - cached_table->select_lex != context->select_lex && - !context->select_lex->is_merged_child_of(cached_table->select_lex) && - is_outer_table(cached_table, context->select_lex)) + table_list->select_lex != context->select_lex && + !context->select_lex->is_merged_child_of(table_list->select_lex) && + is_outer_table(table_list, context->select_lex)) { int ret; if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) @@ -5198,8 +5269,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (any_privileges) { char *db, *tab; - db= cached_table->get_db_name(); - tab= cached_table->get_table_name(); + db= field->table->s->db.str; + tab= field->table->s->table_name.str; if (!(have_privileges= (get_column_grant(thd, &field->table->grant, db, tab, field_name) & VIEW_ANY_ACL))) @@ -5220,7 +5291,12 @@ bool Item_field::fix_fields(THD *thd, Item **reference) marker= thd->lex->current_select->cur_pos_in_select_list; } mark_non_agg_field: - if (fixed && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + /* + table->pos_in_table_list can be 0 when fixing partition functions + or virtual fields. + */ + if (fixed && (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) && + field->table->pos_in_table_list) { /* Mark selects according to presence of non aggregated fields. @@ -5233,7 +5309,7 @@ mark_non_agg_field: (the current level) or a stub added by non-SELECT queries. */ SELECT_LEX *select_lex= cached_table ? - cached_table->select_lex : context->select_lex; + cached_table->select_lex : field->table->pos_in_table_list->select_lex; if (!thd->lex->in_sum_func) select_lex->set_non_agg_field_used(true); else @@ -5873,13 +5949,51 @@ static int save_field_in_field(Field *from, bool *null_value, } +static int memcpy_field_value(Field *to, Field *from) +{ + if (to->ptr != from->ptr) + memcpy(to->ptr,from->ptr, to->pack_length()); + return 0; +} + +fast_field_copier Item_field::setup_fast_field_copier(Field *to) +{ + DBUG_ENTER("Item_field::setup_fast_field_copier"); + DBUG_RETURN(memcpy_field_possible(to, field) ? + &memcpy_field_value : + &field_conv_incompatible); +} + + /** Set a field's value from a item. */ -void Item_field::save_org_in_field(Field *to) +void Item_field::save_org_in_field(Field *to, + fast_field_copier fast_field_copier_func) { - save_field_in_field(field, &null_value, to, TRUE); + DBUG_ENTER("Item_field::save_org_in_field"); + DBUG_PRINT("enter", ("setup: 0x%lx data: 0x%lx", + (ulong) to, (ulong) fast_field_copier_func)); + if (fast_field_copier_func) + { + if (field->is_null()) + { + null_value= TRUE; + set_field_to_null_with_conversions(to, TRUE); + DBUG_VOID_RETURN; + } + to->set_notnull(); + if (to == field) + { + null_value= 0; + DBUG_VOID_RETURN; + } + (*fast_field_copier_func)(to, field); + } + else + save_field_in_field(field, &null_value, to, TRUE); + DBUG_VOID_RETURN; } @@ -7400,9 +7514,9 @@ int Item_ref::save_in_field(Field *to, bool no_conversions) } -void Item_ref::save_org_in_field(Field *field) +void Item_ref::save_org_in_field(Field *field, fast_field_copier optimizer_data) { - (*ref)->save_org_in_field(field); + (*ref)->save_org_in_field(field, optimizer_data); } @@ -8273,7 +8387,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) if (context->error_processor == &view_error_processor) { - TABLE_LIST *view= cached_table->top_table(); + TABLE_LIST *view= field_arg->table->pos_in_table_list->top_table(); push_warning_printf(field_arg->table->in_use, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_VIEW_FIELD, @@ -8695,6 +8809,25 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) { Item_result res_type=item_cmp_type(field->result_type(), item->result_type()); + /* + We have to check field->cmp_type() instead of res_type, + as result_type() - and thus res_type - can never be TIME_RESULT (yet). + */ + if (field->cmp_type() == TIME_RESULT) + { + MYSQL_TIME field_time, item_time; + if (field->type() == MYSQL_TYPE_TIME) + { + field->get_time(&field_time); + item->get_time(&item_time); + } + else + { + field->get_date(&field_time, TIME_INVALID_DATES); + item->get_date(&item_time, TIME_INVALID_DATES); + } + return my_time_compare(&field_time, &item_time); + } if (res_type == STRING_RESULT) { char item_buff[MAX_FIELD_WIDTH]; @@ -8745,25 +8878,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) return my_decimal_cmp(field_val, item_val); } /* - We have to check field->cmp_type() instead of res_type, - as result_type() - and thus res_type - can never be TIME_RESULT (yet). - */ - if (field->cmp_type() == TIME_RESULT) - { - MYSQL_TIME field_time, item_time; - if (field->type() == MYSQL_TYPE_TIME) - { - field->get_time(&field_time); - item->get_time(&item_time); - } - else - { - field->get_date(&field_time, TIME_INVALID_DATES); - item->get_date(&item_time, TIME_INVALID_DATES); - } - return my_time_compare(&field_time, &item_time); - } - /* The patch for Bug#13463415 started using this function for comparing BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode. Prefixing the auto variables with volatile fixes the problem.... diff --git a/sql/item.h b/sql/item.h index 6afeca66f0a..e3ddf56511e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -332,6 +332,8 @@ struct Name_resolution_context: Sql_alloc */ TABLE_LIST *last_name_resolution_table; + /* Cache first_name_resolution_table in setup_natural_join_row_types */ + TABLE_LIST *natural_join_first_table; /* SELECT_LEX item belong to, in case of merged VIEW it can differ from SELECT_LEX where item was created, so we can't use table_list/field_list @@ -707,8 +709,12 @@ public: /* Function returns 1 on overflow and -1 on fatal errors */ int save_in_field_no_warnings(Field *field, bool no_conversions); virtual int save_in_field(Field *field, bool no_conversions); - virtual void save_org_in_field(Field *field) + virtual void save_org_in_field(Field *field, + fast_field_copier data + __attribute__ ((__unused__))) { (void) save_in_field(field, 1); } + virtual fast_field_copier setup_fast_field_copier(Field *field) + { return NULL; } virtual int save_safe_in_field(Field *field) { return save_in_field(field, 1); } virtual bool send(Protocol *protocol, String *str); @@ -944,7 +950,7 @@ public: save_val() is method of val_* family which stores value in the given field. */ - virtual void save_val(Field *to) { save_org_in_field(to); } + virtual void save_val(Field *to) { save_org_in_field(to, NULL); } /* save_result() is method of val*result() family which stores value in the given field. @@ -1069,6 +1075,8 @@ public: virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY | TIME_INVALID_DATES); } + // Get date with automatic TIME->DATETIME conversion + bool get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate); bool get_seconds(ulonglong *sec, ulong *sec_part); virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) { return get_date(ltime,fuzzydate); } @@ -1460,7 +1468,7 @@ public: { if (is_expensive_cache < 0) is_expensive_cache= walk(&Item::is_expensive_processor, 0, (uchar*)0); - return test(is_expensive_cache); + return MY_TEST(is_expensive_cache); } virtual Field::geometry_type get_geometry_type() const { return Field::GEOM_GEOMETRY; }; @@ -2068,7 +2076,8 @@ public: void fix_after_pullout(st_select_lex *new_parent, Item **ref); void make_field(Send_field *tmp_field); int save_in_field(Field *field,bool no_conversions); - void save_org_in_field(Field *field); + void save_org_in_field(Field *field, fast_field_copier optimizer_data); + fast_field_copier setup_fast_field_copier(Field *field); table_map used_tables() const; table_map all_used_tables() const; enum Item_result result_type () const @@ -2103,7 +2112,11 @@ public: tab->merge_keys.merge(field->part_of_key); if (tab->read_set) bitmap_fast_test_and_set(tab->read_set, field->field_index); - if (field->vcol_info) + /* + Do not mark a self-referecing virtual column. + Such virtual columns are reported as invalid. + */ + if (field->vcol_info && tab->vcol_set) tab->mark_virtual_col(field); } } @@ -2398,7 +2411,7 @@ public: virtual void print(String *str, enum_query_type query_type); Item_num *neg() { value= -value; return this; } uint decimal_precision() const - { return (uint)(max_length - test(value < 0)); } + { return (uint) (max_length - MY_TEST(value < 0)); } bool eq(const Item *, bool binary_cmp) const; bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} bool check_vcol_func_processor(uchar *arg) { return FALSE;} @@ -3097,7 +3110,9 @@ public: bool fix_fields(THD *, Item **); void fix_after_pullout(st_select_lex *new_parent, Item **ref); int save_in_field(Field *field, bool no_conversions); - void save_org_in_field(Field *field); + void save_org_in_field(Field *field, fast_field_copier optimizer_data); + fast_field_copier setup_fast_field_copier(Field *field) + { return (*ref)->setup_fast_field_copier(field); } enum Item_result result_type () const { return (*ref)->result_type(); } enum_field_types field_type() const { return (*ref)->field_type(); } Field *get_tmp_table_field() @@ -3326,7 +3341,8 @@ public: bool is_null(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); bool send(Protocol *protocol, String *buffer); - void save_org_in_field(Field *field) + void save_org_in_field(Field *field, + fast_field_copier data __attribute__ ((__unused__))) { save_val(field); } @@ -3523,7 +3539,8 @@ public: return Item_direct_ref::get_date(ltime, fuzzydate); } bool send(Protocol *protocol, String *buffer); - void save_org_in_field(Field *field) + void save_org_in_field(Field *field, + fast_field_copier data __attribute__ ((__unused__))) { if (check_null_ref()) field->set_null(); @@ -3541,6 +3558,7 @@ public: void cleanup() { null_ref_table= NULL; + item_equal= NULL; Item_direct_ref::cleanup(); } }; @@ -3589,7 +3607,7 @@ public: {} void save_in_result_field(bool no_conversions) { - outer_ref->save_org_in_field(result_field); + outer_ref->save_org_in_field(result_field, NULL); } bool fix_fields(THD *, Item **); void fix_after_pullout(st_select_lex *new_parent, Item **ref); @@ -4223,7 +4241,7 @@ public: virtual void store(Item *item); virtual bool cache_value()= 0; bool basic_const_item() const - { return test(example && example->basic_const_item());} + { return MY_TEST(example && example->basic_const_item()); } virtual void clear() { null_value= TRUE; value_cached= FALSE; } bool is_null() { return null_value; } virtual bool is_expensive() diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5852108dc43..4b9eb37488e 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -503,7 +503,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, if (0 == field_cmp) { Item *tmp= new Item_int_with_ref(field->val_int(), *item, - test(field->flags & UNSIGNED_FLAG)); + MY_TEST(field->flags & UNSIGNED_FLAG)); if (tmp) thd->change_item_tree(item, tmp); result= 1; // Item was replaced @@ -1044,8 +1044,8 @@ int Arg_comparator::compare_e_string() res1= (*a)->val_str(&value1); res2= (*b)->val_str(&value2); if (!res1 || !res2) - return test(res1 == res2); - return test(sortcmp(res1, res2, cmp_collation.collation) == 0); + return MY_TEST(res1 == res2); + return MY_TEST(sortcmp(res1, res2, cmp_collation.collation) == 0); } @@ -1055,8 +1055,8 @@ int Arg_comparator::compare_e_binary_string() res1= (*a)->val_str(&value1); res2= (*b)->val_str(&value2); if (!res1 || !res2) - return test(res1 == res2); - return test(stringcmp(res1, res2) == 0); + return MY_TEST(res1 == res2); + return MY_TEST(stringcmp(res1, res2) == 0); } @@ -1111,8 +1111,8 @@ int Arg_comparator::compare_e_real() double val1= (*a)->val_real(); double val2= (*b)->val_real(); if ((*a)->null_value || (*b)->null_value) - return test((*a)->null_value && (*b)->null_value); - return test(val1 == val2); + return MY_TEST((*a)->null_value && (*b)->null_value); + return MY_TEST(val1 == val2); } int Arg_comparator::compare_e_decimal() @@ -1121,8 +1121,8 @@ int Arg_comparator::compare_e_decimal() my_decimal *val1= (*a)->val_decimal(&decimal1); my_decimal *val2= (*b)->val_decimal(&decimal2); if ((*a)->null_value || (*b)->null_value) - return test((*a)->null_value && (*b)->null_value); - return test(my_decimal_cmp(val1, val2) == 0); + return MY_TEST((*a)->null_value && (*b)->null_value); + return MY_TEST(my_decimal_cmp(val1, val2) == 0); } @@ -1160,8 +1160,8 @@ int Arg_comparator::compare_e_real_fixed() double val1= (*a)->val_real(); double val2= (*b)->val_real(); if ((*a)->null_value || (*b)->null_value) - return test((*a)->null_value && (*b)->null_value); - return test(val1 == val2 || fabs(val1 - val2) < precision); + return MY_TEST((*a)->null_value && (*b)->null_value); + return MY_TEST(val1 == val2 || fabs(val1 - val2) < precision); } @@ -1272,8 +1272,8 @@ int Arg_comparator::compare_e_int() longlong val1= (*a)->val_int(); longlong val2= (*b)->val_int(); if ((*a)->null_value || (*b)->null_value) - return test((*a)->null_value && (*b)->null_value); - return test(val1 == val2); + return MY_TEST((*a)->null_value && (*b)->null_value); + return MY_TEST(val1 == val2); } /** @@ -1284,8 +1284,8 @@ int Arg_comparator::compare_e_int_diff_signedness() longlong val1= (*a)->val_int(); longlong val2= (*b)->val_int(); if ((*a)->null_value || (*b)->null_value) - return test((*a)->null_value && (*b)->null_value); - return (val1 >= 0) && test(val1 == val2); + return MY_TEST((*a)->null_value && (*b)->null_value); + return (val1 >= 0) && MY_TEST(val1 == val2); } int Arg_comparator::compare_row() diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index a0d9b7c20fa..4b3acf83f85 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1,7 +1,7 @@ #ifndef ITEM_CMPFUNC_INCLUDED #define ITEM_CMPFUNC_INCLUDED /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2009, 2011, Monty Program Ab + Copyright (c) 2009, 2011, Monty Program 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 @@ -388,7 +388,7 @@ public: Item_func::print_op(str, query_type); } - bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); } + bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); } bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; } uint decimal_precision() const { return 1; } @@ -874,7 +874,7 @@ public: /* Compare values number pos1 and pos2 for equality */ bool compare_elems(uint pos1, uint pos2) { - return test(compare(collation, base + pos1*size, base + pos2*size)); + return MY_TEST(compare(collation, base + pos1 * size, base + pos2 * size)); } virtual Item_result result_type()= 0; }; @@ -1838,7 +1838,7 @@ public: bool contains(Field *field); Item* get_first(struct st_join_table *context, Item *field); /** Get number of field items / references to field items in this object */ - uint n_field_items() { return equal_items.elements-test(with_const); } + uint n_field_items() { return equal_items.elements - MY_TEST(with_const); } void merge(Item_equal *item); bool merge_with_check(Item_equal *equal_item, bool save_merged); void merge_into_list(List<Item_equal> *list, bool save_merged, diff --git a/sql/item_create.cc b/sql/item_create.cc index fa8c016d61b..a3e0dc6012b 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -1797,6 +1797,19 @@ protected: }; +class Create_func_master_gtid_wait : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_master_gtid_wait s_singleton; + +protected: + Create_func_master_gtid_wait() {} + virtual ~Create_func_master_gtid_wait() {} +}; + + class Create_func_md5 : public Create_func_arg1 { public: @@ -3184,6 +3197,13 @@ Create_func_binlog_gtid_pos Create_func_binlog_gtid_pos::s_singleton; Item* Create_func_binlog_gtid_pos::create_2_arg(THD *thd, Item *arg1, Item *arg2) { +#ifdef HAVE_REPLICATION + if (!mysql_bin_log.is_open()) +#endif + { + my_error(ER_NO_BINARY_LOGGING, MYF(0)); + return NULL; + } thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); return new (thd->mem_root) Item_func_binlog_gtid_pos(arg1, arg2); } @@ -4614,6 +4634,47 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name, } +Create_func_master_gtid_wait Create_func_master_gtid_wait::s_singleton; + +Item* +Create_func_master_gtid_wait::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 1 || arg_count > 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return func; + } + + thd->lex->safe_to_cache_query= 0; + + Item *param_1= item_list->pop(); + switch (arg_count) { + case 1: + { + func= new (thd->mem_root) Item_master_gtid_wait(param_1); + break; + } + case 2: + { + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_master_gtid_wait(param_1, param_2); + break; + } + } + + return func; +} + + Create_func_md5 Create_func_md5::s_singleton; Item* @@ -5558,6 +5619,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)}, { { C_STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)}, { { C_STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)}, + { { C_STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)}, { { C_STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)}, { { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)}, { { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, diff --git a/sql/item_func.cc b/sql/item_func.cc index 02aae8085d6..8937c1c47e4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2615,7 +2615,8 @@ void Item_func_round::fix_length_and_dec() case INT_RESULT: if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS)) { - int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned); + int length_can_increase= MY_TEST(!truncate && (val1 < 0) && + !val1_unsigned); max_length= args[0]->max_length + length_can_increase; /* Here we can keep INT_RESULT */ cached_result_type= INT_RESULT; @@ -2955,11 +2956,6 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) } unpack_time(min_max, ltime); - if (!(fuzzy_date & TIME_TIME_ONLY) && - ((null_value= check_date_with_warn(ltime, fuzzy_date, - MYSQL_TIMESTAMP_ERROR)))) - return true; - if (compare_as_dates->field_type() == MYSQL_TYPE_DATE) { ltime->time_type= MYSQL_TIMESTAMP_DATE; @@ -2970,8 +2966,15 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) ltime->time_type= MYSQL_TIMESTAMP_TIME; ltime->hour+= (ltime->month * 32 + ltime->day) * 24; ltime->month= ltime->day= 0; + if (adjust_time_range_with_warn(ltime, + std::min<uint>(decimals, TIME_SECOND_PART_DIGITS))) + return (null_value= true); } + if (!(fuzzy_date & TIME_TIME_ONLY) && + ((null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)))) + return true; return (null_value= 0); } @@ -3997,6 +4000,34 @@ err: } +longlong Item_master_gtid_wait::val_int() +{ + DBUG_ASSERT(fixed == 1); + longlong result= 0; + + if (args[0]->null_value) + { + null_value= 1; + return 0; + } + + null_value=0; +#ifdef HAVE_REPLICATION + THD* thd= current_thd; + longlong timeout_us; + String *gtid_pos = args[0]->val_str(&value); + + if (arg_count==2 && !args[1]->null_value) + timeout_us= (longlong)(1e6*args[1]->val_real()); + else + timeout_us= (longlong)-1; + + result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us); +#endif + return result; +} + + /** Enables a session to wait on a condition until a timeout or a network disconnect occurs. @@ -4599,7 +4630,7 @@ longlong Item_func_sleep::val_int() mysql_cond_destroy(&cond); - return test(!error); // Return 1 killed + return MY_TEST(!error); // Return 1 killed } @@ -6226,6 +6257,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) return TRUE; } + bool allows_multi_table_search= true; const_item_cache=0; for (uint i=1 ; i < arg_count ; i++) { @@ -6237,7 +6269,10 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) my_error(ER_WRONG_ARGUMENTS, MYF(0), "AGAINST"); return TRUE; } + allows_multi_table_search &= + allows_search_on_non_indexed_columns(((Item_field *)item)->field->table); } + /* Check that all columns come from the same table. We've already checked that columns in MATCH are fields so @@ -6246,7 +6281,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables()) key=NO_SUCH_KEY; - if (key == NO_SUCH_KEY && !(flags & FT_BOOL)) + if (key == NO_SUCH_KEY && !allows_multi_table_search) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return TRUE; @@ -6344,7 +6379,7 @@ bool Item_func_match::fix_index() } err: - if (flags & FT_BOOL) + if (allows_search_on_non_indexed_columns(table)) { key=NO_SUCH_KEY; return 0; @@ -6662,7 +6697,7 @@ void Item_func_sp::fix_length_and_dec() max_length= sp_result_field->field_length; collation.set(sp_result_field->charset()); maybe_null= 1; - unsigned_flag= test(sp_result_field->flags & UNSIGNED_FLAG); + unsigned_flag= MY_TEST(sp_result_field->flags & UNSIGNED_FLAG); DBUG_VOID_RETURN; } diff --git a/sql/item_func.h b/sql/item_func.h index 4b1516dcc4d..69abecc5f39 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1,7 +1,7 @@ #ifndef ITEM_FUNC_INCLUDED #define ITEM_FUNC_INCLUDED -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2013, Monty Program Ab. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, SkySQL 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 @@ -160,7 +160,7 @@ public: void count_decimal_length(); inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { - return (null_value=args[0]->get_date(ltime, fuzzy_date)); + return (null_value=args[0]->get_date_with_conversion(ltime, fuzzy_date)); } void count_datetime_length(Item **item, uint nitems); bool count_string_result_length(enum_field_types field_type, @@ -1643,6 +1643,22 @@ public: }; +class Item_master_gtid_wait :public Item_int_func +{ + String value; +public: + Item_master_gtid_wait(Item *a) :Item_int_func(a) {} + Item_master_gtid_wait(Item *a,Item *b) :Item_int_func(a,b) {} + longlong val_int(); + const char *func_name() const { return "master_gtid_wait"; } + void fix_length_and_dec() { max_length=10+1+10+1+20+1; maybe_null=0;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } +}; + + /* Handling of user definable variables */ class user_var_entry; @@ -1724,7 +1740,9 @@ public: { return save_in_field(field, no_conversions, 1); } - void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); } + void save_org_in_field(Field *field, + fast_field_copier data __attribute__ ((__unused__))) + { (void)save_in_field(field, 1, 0); } bool register_field_in_read_map(uchar *arg); bool register_field_in_bitmap(uchar *arg); bool set_entry(THD *thd, bool create_if_not_exists); @@ -1909,6 +1927,41 @@ public: /* TODO: consider adding in support for the MATCH-based virtual columns */ return trace_unsupported_by_check_vcol_func_processor(func_name()); } +private: + /** + Check whether storage engine for given table, + allows FTS Boolean search on non-indexed columns. + + @todo A flag should be added to the extended fulltext API so that + it may be checked whether search on non-indexed columns are + supported. Currently, it is not possible to check for such a + flag since @c this->ft_handler is not yet set when this function is + called. The current hack is to assume that search on non-indexed + columns are supported for engines that does not support the extended + fulltext API (e.g., MyISAM), while it is not supported for other + engines (e.g., InnoDB) + + @param table_arg Table for which storage engine to check + + @retval true if BOOLEAN search on non-indexed columns is supported + @retval false otherwise + */ + bool allows_search_on_non_indexed_columns(TABLE* table_arg) + { + // Only Boolean search may support non_indexed columns + if (!(flags & FT_BOOL)) + return false; + + DBUG_ASSERT(table_arg && table_arg->file); + + // Assume that if extended fulltext API is not supported, + // non-indexed columns are allowed. This will be true for MyISAM. + if ((table_arg->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) == 0) + return true; + + return false; + } + }; diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 665c941414c..1deda83907c 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -244,7 +244,7 @@ String *Item_func_centroid::val_str(String *str) srid= uint4korr(swkb->ptr()); str->q_append(srid); - return (null_value= test(geom->centroid(str))) ? 0 : str; + return (null_value= MY_TEST(geom->centroid(str))) ? 0 : str; } @@ -859,7 +859,7 @@ String *Item_func_spatial_operation::val_str(String *str_value) str_value->length(0); str_value->q_append(srid); - if (!Geometry::create_from_opresult(&buffer1, str_value, res_receiver)) + if (Geometry::create_from_opresult(&buffer1, str_value, res_receiver)) goto exit; exit: @@ -1116,6 +1116,8 @@ int Item_func_buffer::Transporter::start_line() { if (buffer_op == Gcalc_function::op_difference) { + if (m_fn->reserve_op_buffer(1)) + return 1; m_fn->add_operation(Gcalc_function::op_false, 0); skip_line= TRUE; return 0; @@ -1317,7 +1319,7 @@ String *Item_func_buffer::val_str(String *str_value) str_value->length(0); str_value->q_append(srid); - if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver)) + if (Geometry::create_from_opresult(&buffer, str_value, res_receiver)) goto mem_error; null_value= 0; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index fc5dfe0994e..57af832c151 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. Copyright (c) 2009, 2013, Monty Program Ab. This program is free software; you can redistribute it and/or modify @@ -65,11 +65,6 @@ C_MODE_END size_t username_char_length= 80; -/** - @todo Remove this. It is not safe to use a shared String object. - */ -String my_empty_string("",default_charset_info); - /* For the Items which have only val_str_ascii() method and don't have their own "native" val_str(), @@ -1168,6 +1163,7 @@ String *Item_func_reverse::val_str(String *str) if ((l= my_ismbchar(res->charset(),ptr,end))) { tmp-= l; + DBUG_ASSERT(tmp >= tmp_value.ptr()); memcpy(tmp,ptr,l); ptr+= l; } @@ -2097,18 +2093,35 @@ String *Item_func_trim::val_str(String *str) ptr= (char*) res->ptr(); end= ptr+res->length(); r_ptr= remove_str->ptr(); - while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length)) - ptr+=remove_length; #ifdef USE_MB if (use_mb(res->charset())) { + while (ptr + remove_length <= end) + { + uint num_bytes= 0; + while (num_bytes < remove_length) + { + uint len; + if ((len= my_ismbchar(res->charset(), ptr + num_bytes, end))) + num_bytes+= len; + else + ++num_bytes; + } + if (num_bytes != remove_length) + break; + if (memcmp(ptr, r_ptr, remove_length)) + break; + ptr+= remove_length; + } char *p=ptr; register uint32 l; loop: while (ptr + remove_length < end) { - if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l; - else ++ptr; + if ((l= my_ismbchar(res->charset(), ptr,end))) + ptr+= l; + else + ++ptr; } if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length)) { @@ -2121,6 +2134,8 @@ String *Item_func_trim::val_str(String *str) else #endif /* USE_MB */ { + while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length)) + ptr+=remove_length; while (ptr + remove_length <= end && !memcmp(end-remove_length,r_ptr,remove_length)) end-=remove_length; @@ -2839,7 +2854,7 @@ String *Item_func_make_set::val_str(String *str) ulonglong bits; bool first_found=0; Item **ptr=args+1; - String *result=&my_empty_string; + String *result= make_empty_result(); bits=args[0]->val_int(); if ((null_value=args[0]->null_value)) @@ -3286,7 +3301,9 @@ String *Item_func_conv::val_str(String *str) int to_base= (int) args[2]->val_int(); int err; + // Note that abs(INT_MIN) is undefined. if (args[0]->null_value || args[1]->null_value || args[2]->null_value || + from_base == INT_MIN || to_base == INT_MIN || abs(to_base) > 36 || abs(to_base) < 2 || abs(from_base) > 36 || abs(from_base) < 2 || !(res->length())) { @@ -4787,7 +4804,7 @@ String *Item_dyncol_get::val_str(String *str_result) goto null; case DYN_COL_INT: case DYN_COL_UINT: - str_result->set_int(val.x.long_value, test(val.type == DYN_COL_UINT), + str_result->set_int(val.x.long_value, MY_TEST(val.type == DYN_COL_UINT), &my_charset_latin1); break; case DYN_COL_DOUBLE: diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 4b9ec50c164..7b2591e9346 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -1057,6 +1057,15 @@ public: const char *func_name() const { return "weight_string"; } String *val_str(String *); void fix_length_and_dec(); + bool eq(const Item *item, bool binary_cmp) const + { + if (!Item_str_func::eq(item, binary_cmp)) + return false; + Item_func_weight_string *that= (Item_func_weight_string *)item; + return this->flags == that->flags && + this->nweights == that->nweights && + this->result_length == that->result_length; + } }; class Item_func_crc32 :public Item_int_func @@ -1206,7 +1215,5 @@ public: String *val_str(String *); }; -extern String my_empty_string; - #endif /* ITEM_STRFUNC_INCLUDED */ diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 81c4f0f51e5..67af49c4f9f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -3341,7 +3341,7 @@ subselect_single_select_engine(THD *thd_arg, st_select_lex *select, select_result_interceptor *result_arg, Item_subselect *item_arg) :subselect_engine(thd_arg, item_arg, result_arg), - prepared(0), executed(0), optimize_error(0), + prepared(0), executed(0), select_lex(select), join(0) { select_lex->master_unit()->item= item_arg; @@ -3355,7 +3355,7 @@ int subselect_single_select_engine::get_identifier() void subselect_single_select_engine::cleanup() { DBUG_ENTER("subselect_single_select_engine::cleanup"); - prepared= executed= optimize_error= 0; + prepared= executed= 0; join= 0; result->cleanup(); select_lex->uncacheable&= ~UNCACHEABLE_DEPENDENT_INJECTED; @@ -3399,7 +3399,7 @@ bool subselect_union_engine::is_executed() const bool subselect_union_engine::no_rows() { /* Check if we got any rows when reading UNION result from temp. table: */ - return test(!unit->fake_select_lex->join->send_records); + return MY_TEST(!unit->fake_select_lex->join->send_records); } @@ -3589,9 +3589,6 @@ int subselect_single_select_engine::exec() { DBUG_ENTER("subselect_single_select_engine::exec"); - if (optimize_error) - DBUG_RETURN(1); - char const *save_where= thd->where; SELECT_LEX *save_select= thd->lex->current_select; thd->lex->current_select= select_lex; @@ -3604,7 +3601,7 @@ int subselect_single_select_engine::exec() if (join->optimize()) { thd->where= save_where; - executed= optimize_error= 1; + executed= 1; thd->lex->current_select= save_select; DBUG_RETURN(join->error ? join->error : 1); } @@ -4685,13 +4682,13 @@ ulonglong subselect_hash_sj_engine::rowid_merge_buff_size( */ static my_bool -bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root) +my_bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root) { my_bitmap_map *bitmap_buf; if (!(bitmap_buf= (my_bitmap_map*) alloc_root(mem_root, bitmap_buffer_size(n_bits))) || - bitmap_init(map, bitmap_buf, n_bits, FALSE)) + my_bitmap_init(map, bitmap_buf, n_bits, FALSE)) return TRUE; bitmap_clear_all(map); return FALSE; @@ -4729,9 +4726,9 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) DBUG_ENTER("subselect_hash_sj_engine::init"); - if (bitmap_init_memroot(&non_null_key_parts, tmp_columns->elements, + if (my_bitmap_init_memroot(&non_null_key_parts, tmp_columns->elements, thd->mem_root) || - bitmap_init_memroot(&partial_match_key_parts, tmp_columns->elements, + my_bitmap_init_memroot(&partial_match_key_parts, tmp_columns->elements, thd->mem_root)) DBUG_RETURN(TRUE); @@ -5241,8 +5238,8 @@ int subselect_hash_sj_engine::exec() /* The subquery should be optimized, and materialized only once. */ DBUG_ASSERT(materialize_join->optimized && !is_materialized); materialize_join->exec(); - if ((res= test(materialize_join->error || thd->is_fatal_error || - thd->is_error()))) + if ((res= MY_TEST(materialize_join->error || thd->is_fatal_error || + thd->is_error()))) goto err; /* @@ -5325,7 +5322,7 @@ int subselect_hash_sj_engine::exec() count_pm_keys= count_partial_match_columns - count_null_only_columns + (nn_key_parts ? 1 : 0); - choose_partial_match_strategy(test(nn_key_parts), + choose_partial_match_strategy(MY_TEST(nn_key_parts), has_covering_null_row, &partial_match_key_parts); DBUG_ASSERT(strategy == PARTIAL_MATCH_MERGE || @@ -5453,7 +5450,7 @@ Ordered_key::Ordered_key(uint keyid_arg, TABLE *tbl_arg, Item *search_key_arg, Ordered_key::~Ordered_key() { my_free(key_buff); - bitmap_free(&null_key); + my_bitmap_free(&null_key); } @@ -5563,7 +5560,7 @@ bool Ordered_key::alloc_keys_buffers() lookup offset. */ /* Notice that max_null_row is max array index, we need count, so +1. */ - if (bitmap_init(&null_key, NULL, (uint)(max_null_row + 1), FALSE)) + if (my_bitmap_init(&null_key, NULL, (uint)(max_null_row + 1), FALSE)) return TRUE; cur_key_idx= HA_POS_ERROR; @@ -6002,8 +5999,8 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, */ if (!has_covering_null_columns) { - if (bitmap_init_memroot(&matching_keys, merge_keys_count, thd->mem_root) || - bitmap_init_memroot(&matching_outer_cols, merge_keys_count, thd->mem_root)) + if (my_bitmap_init_memroot(&matching_keys, merge_keys_count, thd->mem_root) || + my_bitmap_init_memroot(&matching_outer_cols, merge_keys_count, thd->mem_root)) return TRUE; /* @@ -6300,7 +6297,7 @@ bool subselect_rowid_merge_engine::partial_match() Do not add the non_null_key, since it was already processed above. */ bitmap_clear_all(&matching_outer_cols); - for (uint i= test(non_null_key); i < merge_keys_count; i++) + for (uint i= MY_TEST(non_null_key); i < merge_keys_count; i++) { DBUG_ASSERT(merge_keys[i]->get_column_count() == 1); if (merge_keys[i]->get_search_key(0)->null_value) @@ -6317,7 +6314,7 @@ bool subselect_rowid_merge_engine::partial_match() nullable columns (above we guarantee there is a match for the non-null coumns), the result is UNKNOWN. */ - if (count_nulls_in_search_key == merge_keys_count - test(non_null_key)) + if (count_nulls_in_search_key == merge_keys_count - MY_TEST(non_null_key)) { res= TRUE; goto end; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index e806f45041a..92b269d02f1 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -566,7 +566,7 @@ public: if ( pushed_cond_guards) pushed_cond_guards[i]= v; } - bool have_guarded_conds() { return test(pushed_cond_guards); } + bool have_guarded_conds() { return MY_TEST(pushed_cond_guards); } Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery @@ -621,7 +621,7 @@ public: int get_identifier(); bool test_strategy(uchar strategy) - { return test(in_strategy & strategy); } + { return MY_TEST(in_strategy & strategy); } /** Test that the IN strategy was chosen for execution. This is so @@ -641,7 +641,7 @@ public: } bool is_set_strategy() - { return test(in_strategy & SUBS_STRATEGY_CHOSEN); } + { return MY_TEST(in_strategy & SUBS_STRATEGY_CHOSEN); } bool has_strategy() { return in_strategy != SUBS_NOT_TRANSFORMED; } @@ -815,7 +815,6 @@ class subselect_single_select_engine: public subselect_engine { bool prepared; /* simple subselect is prepared */ bool executed; /* simple subselect is executed */ - bool optimize_error; /* simple subselect optimization failed */ st_select_lex *select_lex; /* corresponding select_lex */ JOIN * join; /* corresponding JOIN structure */ public: diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 367ff39eab6..62db351150b 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2013 Monty Program Ab + Copyright (c) 2008, 2014, SkySQL 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 @@ -261,7 +261,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) List_iterator<Item_field> of(outer_fields); while ((field= of++)) { - SELECT_LEX *sel= field->cached_table->select_lex; + SELECT_LEX *sel= field->field->table->pos_in_table_list->select_lex; if (sel->nest_level < aggr_level) { if (in_sum_func) @@ -1110,18 +1110,19 @@ void Aggregator_distinct::endup() endup_done= TRUE; } } - else - { - /* - We don't have a tree only if 'setup()' hasn't been called; - this is the case of sql_select.cc:return_zero_rows. - */ - if (tree) - table->field[0]->set_notnull(); - } + /* + We don't have a tree only if 'setup()' hasn't been called; + this is the case of sql_executor.cc:return_zero_rows. + */ if (tree && !endup_done) { + /* + All tree's values are not NULL. + Note that value of field is changed as we walk the tree, in + Aggregator_distinct::unique_walk_function, but it's always not NULL. + */ + table->field[0]->set_notnull(); /* go over the tree of distinct keys and calculate the aggregate value */ use_distinct_values= TRUE; tree_walk_action func; @@ -1417,7 +1418,7 @@ bool Item_sum_sum::add() { my_decimal value; const my_decimal *val= aggr->arg_val_decimal(&value); - if (!aggr->arg_is_null()) + if (!aggr->arg_is_null(true)) { my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), val, dec_buffs + curr_dec_buff); @@ -1428,7 +1429,7 @@ bool Item_sum_sum::add() else { sum+= aggr->arg_val_real(); - if (!aggr->arg_is_null()) + if (!aggr->arg_is_null(true)) null_value= 0; } DBUG_RETURN(0); @@ -1554,9 +1555,27 @@ double Aggregator_simple::arg_val_real() } -bool Aggregator_simple::arg_is_null() +bool Aggregator_simple::arg_is_null(bool use_null_value) { - return item_sum->args[0]->null_value; + Item **item= item_sum->args; + const uint item_count= item_sum->arg_count; + if (use_null_value) + { + for (uint i= 0; i < item_count; i++) + { + if (item[i]->null_value) + return true; + } + } + else + { + for (uint i= 0; i < item_count; i++) + { + if (item[i]->maybe_null && item[i]->is_null()) + return true; + } + } + return false; } @@ -1574,10 +1593,17 @@ double Aggregator_distinct::arg_val_real() } -bool Aggregator_distinct::arg_is_null() +bool Aggregator_distinct::arg_is_null(bool use_null_value) { - return use_distinct_values ? table->field[0]->is_null() : - item_sum->args[0]->null_value; + if (use_distinct_values) + { + const bool rc= table->field[0]->is_null(); + DBUG_ASSERT(!rc); // NULLs are never stored in 'tree' + return rc; + } + return use_null_value ? + item_sum->args[0]->null_value : + (item_sum->args[0]->maybe_null && item_sum->args[0]->is_null()); } @@ -1595,8 +1621,9 @@ void Item_sum_count::clear() bool Item_sum_count::add() { - if (!args[0]->maybe_null || !args[0]->is_null()) - count++; + if (aggr->arg_is_null(false)) + return 0; + count++; return 0; } @@ -1687,7 +1714,7 @@ bool Item_sum_avg::add() { if (Item_sum_sum::add()) return TRUE; - if (!aggr->arg_is_null()) + if (!aggr->arg_is_null(true)) count++; return FALSE; } diff --git a/sql/item_sum.h b/sql/item_sum.h index e82e0ead1c2..d28c654c438 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -57,19 +57,8 @@ protected: /* the aggregate function class to act on */ Item_sum *item_sum; - /** - When feeding back the data in endup() from Unique/temp table back to - Item_sum::add() methods we must read the data from Unique (and not - recalculate the functions that are given as arguments to the aggregate - function. - This flag is to tell the add() methods to take the data from the Unique - instead by calling the relevant val_..() method - */ - - bool use_distinct_values; - public: - Aggregator (Item_sum *arg): item_sum(arg), use_distinct_values(FALSE) {} + Aggregator (Item_sum *arg): item_sum(arg) {} virtual ~Aggregator () {} /* Keep gcc happy */ enum Aggregator_type { SIMPLE_AGGREGATOR, DISTINCT_AGGREGATOR }; @@ -106,10 +95,16 @@ public: /** Floating point value of being-aggregated argument */ virtual double arg_val_real() = 0; /** - NULLness of being-aggregated argument; can be called only after - arg_val_decimal() or arg_val_real(). + NULLness of being-aggregated argument. + + @param use_null_value Optimization: to determine if the argument is NULL + we must, in the general case, call is_null() on it, which itself might + call val_*() on it, which might be costly. If you just have called + arg_val*(), you can pass use_null_value=true; this way, arg_is_null() + might avoid is_null() and instead do a cheap read of the Item's null_value + (updated by arg_val*()). */ - virtual bool arg_is_null() = 0; + virtual bool arg_is_null(bool use_null_value) = 0; }; @@ -495,7 +490,7 @@ public: Item *get_arg(uint i) { return args[i]; } Item *set_arg(uint i, THD *thd, Item *new_val); - uint get_arg_count() { return arg_count; } + uint get_arg_count() const { return arg_count; } /* Initialization of distinct related members */ void init_aggregator() @@ -626,10 +621,20 @@ class Aggregator_distinct : public Aggregator */ bool always_null; + /** + When feeding back the data in endup() from Unique/temp table back to + Item_sum::add() methods we must read the data from Unique (and not + recalculate the functions that are given as arguments to the aggregate + function. + This flag is to tell the arg_*() methods to take the data from the Unique + instead of calling the relevant val_..() method. + */ + bool use_distinct_values; + public: Aggregator_distinct (Item_sum *sum) : Aggregator(sum), table(NULL), tmp_table_param(NULL), tree(NULL), - always_null(FALSE) {} + always_null(false), use_distinct_values(false) {} virtual ~Aggregator_distinct (); Aggregator_type Aggrtype() { return DISTINCT_AGGREGATOR; } @@ -639,7 +644,7 @@ public: void endup(); virtual my_decimal *arg_val_decimal(my_decimal * value); virtual double arg_val_real(); - virtual bool arg_is_null(); + virtual bool arg_is_null(bool use_null_value); bool unique_walk_function(void *element); bool unique_walk_function_for_count(void *element); @@ -666,7 +671,7 @@ public: void endup() {}; virtual my_decimal *arg_val_decimal(my_decimal * value); virtual double arg_val_real(); - virtual bool arg_is_null(); + virtual bool arg_is_null(bool use_null_value); }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 6ffa8b2af46..4d261e7a7d9 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1077,7 +1077,7 @@ longlong Item_func_weekday::val_int() return (longlong) calc_weekday(calc_daynr(ltime.year, ltime.month, ltime.day), - odbc_type) + test(odbc_type); + odbc_type) + MY_TEST(odbc_type); } void Item_func_dayname::fix_length_and_dec() @@ -1480,12 +1480,67 @@ String *Item_temporal_func::val_str(String *str) } +bool Item_temporal_hybrid_func::fix_temporal_type(MYSQL_TIME *ltime) +{ + if (ltime->time_type < 0) /* MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_ERROR */ + return false; + + if (ltime->time_type != MYSQL_TIMESTAMP_TIME) + goto date_or_datetime_value; + + /* Convert TIME to DATE or DATETIME */ + switch (field_type()) + { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + { + MYSQL_TIME tmp; + if (time_to_datetime_with_warn(current_thd, ltime, &tmp, 0)) + return (null_value= true); + *ltime= tmp; + if (field_type() == MYSQL_TYPE_DATE) + datetime_to_date(ltime); + return false; + } + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */ + return false; + default: + DBUG_ASSERT(0); + return (null_value= true); + } + +date_or_datetime_value: + /* Convert DATE or DATETIME to TIME, DATE, or DATETIME */ + switch (field_type()) + { + case MYSQL_TYPE_TIME: + datetime_to_time(ltime); + return false; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + date_to_datetime(ltime); + return false; + case MYSQL_TYPE_DATE: + datetime_to_date(ltime); + return false; + case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */ + return false; + default: + DBUG_ASSERT(0); + return (null_value= true); + } + return false; +} + + String *Item_temporal_hybrid_func::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_date(<ime, 0) || + if (get_date(<ime, 0) || fix_temporal_type(<ime) || (null_value= my_TIME_to_str(<ime, str, decimals))) return (String *) 0; @@ -2160,8 +2215,10 @@ longlong Item_extract::val_int() long neg; int is_time_flag = date_value ? 0 : TIME_TIME_ONLY; - if (get_arg0_date(<ime, is_time_flag)) + // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion + if ((null_value= args[0]->get_date(<ime, is_time_flag))) return 0; + neg= ltime.neg ? -1 : 1; DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0); @@ -2482,26 +2539,7 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (decimals < TIME_SECOND_PART_DIGITS) my_time_trunc(ltime, decimals); - /* - ltime is valid MYSQL_TYPE_TIME (according to fuzzy_date). - But not every valid TIME value is a valid DATETIME value! - */ - if (ltime->time_type == MYSQL_TIMESTAMP_TIME) - { - if (ltime->neg) - { - ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_DATETIME, 0); - return (null_value= 1); - } - - uint day= ltime->hour/24; - ltime->hour %= 24; - ltime->month= day / 31; - ltime->day= day % 31; - } - + DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME); ltime->time_type= MYSQL_TIMESTAMP_DATETIME; return 0; } @@ -2597,18 +2635,20 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) bool is_time= 0; long days, microseconds; longlong seconds; - int l_sign= sign, was_cut= 0; + int l_sign= sign; - if (is_date) // TIMESTAMP function + if (cached_field_type == MYSQL_TYPE_DATETIME) { + // TIMESTAMP function OR the first argument is DATE/DATETIME/TIMESTAMP if (get_arg0_date(&l_time1, 0) || args[1]->get_time(&l_time2) || l_time1.time_type == MYSQL_TIMESTAMP_TIME || l_time2.time_type != MYSQL_TIMESTAMP_TIME) return (null_value= 1); } - else // ADDTIME function + else { + // ADDTIME function AND the first argument is TIME if (args[0]->get_time(&l_time1) || args[1]->get_time(&l_time2) || l_time2.time_type == MYSQL_TIMESTAMP_DATETIME) @@ -2633,9 +2673,9 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (!is_time && ltime->neg) return (null_value= 1); - days= (long)(seconds/86400L); + days= (long) (seconds / SECONDS_IN_24H); - calc_time_from_sec(ltime, (long)(seconds%86400L), microseconds); + calc_time_from_sec(ltime, (long)(seconds % SECONDS_IN_24H), microseconds); ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME; @@ -2648,16 +2688,7 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) } ltime->hour+= days*24; - - MYSQL_TIME copy= *ltime; - ErrConvTime str(©); - - check_time_range(ltime, decimals, &was_cut); - if (was_cut) - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_TIME, NullS); - - return (null_value= 0); + return (null_value= adjust_time_range_with_warn(ltime, decimals)); } @@ -2695,7 +2726,7 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) DBUG_ASSERT(fixed == 1); longlong seconds; long microseconds; - int l_sign= 1, was_cut= 0; + int l_sign= 1; MYSQL_TIME l_time1,l_time2,l_time3; ErrConvTime str(&l_time3); @@ -2739,12 +2770,7 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) return (null_value= 1); *ltime= l_time3; - check_time_range(ltime, decimals, &was_cut); - - if (was_cut) - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_TIME, NullS); - return (null_value= 0); + return (null_value= adjust_time_range_with_warn(ltime, decimals)); } /** @@ -2832,8 +2858,12 @@ longlong Item_func_timestamp_diff::val_int() int neg= 1; null_value= 0; - if (args[0]->get_date(<ime1, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE) || - args[1]->get_date(<ime2, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + if (args[0]->get_date_with_conversion(<ime1, + TIME_NO_ZERO_DATE | + TIME_NO_ZERO_IN_DATE) || + args[1]->get_date_with_conversion(<ime2, + TIME_NO_ZERO_DATE | + TIME_NO_ZERO_IN_DATE)) goto null_date; if (calc_time_diff(<ime2,<ime1, 1, @@ -2903,9 +2933,9 @@ longlong Item_func_timestamp_diff::val_int() case INTERVAL_MONTH: return months*neg; case INTERVAL_WEEK: - return seconds/86400L/7L*neg; + return seconds / SECONDS_IN_24H / 7L * neg; case INTERVAL_DAY: - return seconds/86400L*neg; + return seconds / SECONDS_IN_24H * neg; case INTERVAL_HOUR: return seconds/3600L*neg; case INTERVAL_MINUTE: diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index cdf03199a81..8f881487e21 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -542,6 +542,11 @@ public: collation.collation : &my_charset_bin; } /** + Fix the returned timestamp to match field_type(), + which is important for val_str(). + */ + bool fix_temporal_type(MYSQL_TIME *ltime); + /** Return string value in ASCII character set. */ String *val_str_ascii(String *str); diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 456779beec1..75f0ed9ef5a 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2005, 2011, Oracle and/or its affiliates. +/* Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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 @@ -226,6 +226,9 @@ public: { max_length= MAX_BLOB_WIDTH; collation.collation= pxml->charset(); + // To avoid premature evaluation, mark all nodeset functions as non-const. + used_tables_cache= RAND_TABLE_BIT; + const_item_cache= false; } const char *func_name() const { return "nodeset"; } bool check_vcol_func_processor(uchar *int_arg) diff --git a/sql/key.cc b/sql/key.cc index 8ba3d48e8dc..3556ecf82d7 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 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 @@ -126,8 +126,8 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, { if (key_part->null_bit) { - *to_key++= test(from_record[key_part->null_offset] & - key_part->null_bit); + *to_key++= MY_TEST(from_record[key_part->null_offset] & + key_part->null_bit); key_length--; if (to_key[-1]) { @@ -300,8 +300,8 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length) if (key_part->null_bit) { - if (*key != test(table->record[0][key_part->null_offset] & - key_part->null_bit)) + if (*key != MY_TEST(table->record[0][key_part->null_offset] & + key_part->null_bit)) return 1; if (*key) continue; @@ -438,7 +438,7 @@ void key_unpack(String *to, TABLE *table, KEY *key) } } field_unpack(to, key_part->field, table->record[0], key_part->length, - test(key_part->key_part_flag & HA_PART_KEY_SEG)); + MY_TEST(key_part->key_part_flag & HA_PART_KEY_SEG)); } dbug_tmp_restore_column_map(table->read_set, old_map); DBUG_VOID_RETURN; diff --git a/sql/key.h b/sql/key.h index ba3102947b3..47b981f5298 100644 --- a/sql/key.h +++ b/sql/key.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 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 diff --git a/sql/lex.h b/sql/lex.h index 88d73b9b169..10a52160cf0 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -223,6 +223,7 @@ static SYMBOL symbols[] = { { "EXISTS", SYM(EXISTS)}, { "EXIT", SYM(EXIT_SYM)}, { "EXPANSION", SYM(EXPANSION_SYM)}, + { "EXPORT", SYM(EXPORT_SYM)}, { "EXPLAIN", SYM(DESCRIBE)}, { "EXTENDED", SYM(EXTENDED_SYM)}, { "EXTENT_SIZE", SYM(EXTENT_SIZE_SYM)}, diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h index 5f3c70a50a4..d48ca57df85 100644 --- a/sql/lex_symbol.h +++ b/sql/lex_symbol.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2000-2001, 2004 MySQL AB +/* Copyright (c) 2000, 2001, 2004, 2006, 2007 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 diff --git a/sql/log.cc b/sql/log.cc index c0b3f36ee78..a0382d9ad1f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2013, Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, SkySQL 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 @@ -651,35 +651,59 @@ int wsrep_write_cache(IO_CACHE *cache, uchar **buf, int *buf_len) } #endif /* REMOVED */ #endif -/* Check if a given table is opened log table */ -int check_if_log_table(size_t db_len, const char *db, size_t table_name_len, - const char *table_name, bool check_if_opened) + + +/** + Check if a given table is opened log table + + @param table Table to check + @param check_if_opened Only fail if it's a log table in use + @param error_msg String to put in error message if not ok. + No error message if 0 + @return 0 ok + @return # Type of log file + */ + +int check_if_log_table(const TABLE_LIST *table, + bool check_if_opened, + const char *error_msg) { - if (db_len == 5 && + int result= 0; + if (table->db_length == 5 && !(lower_case_table_names ? - my_strcasecmp(system_charset_info, db, "mysql") : - strcmp(db, "mysql"))) + my_strcasecmp(system_charset_info, table->db, "mysql") : + strcmp(table->db, "mysql"))) { - if (table_name_len == 11 && !(lower_case_table_names ? - my_strcasecmp(system_charset_info, - table_name, "general_log") : - strcmp(table_name, "general_log"))) + const char *table_name= table->table_name; + + if (table->table_name_length == 11 && + !(lower_case_table_names ? + my_strcasecmp(system_charset_info, + table_name, "general_log") : + strcmp(table_name, "general_log"))) { - if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL)) - return QUERY_LOG_GENERAL; - return 0; + result= QUERY_LOG_GENERAL; + goto end; } - if (table_name_len == 8 && !(lower_case_table_names ? + if (table->table_name_length == 8 && !(lower_case_table_names ? my_strcasecmp(system_charset_info, table_name, "slow_log") : strcmp(table_name, "slow_log"))) { - if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_SLOW)) - return QUERY_LOG_SLOW; - return 0; + result= QUERY_LOG_SLOW; + goto end; } } return 0; + +end: + if (!check_if_opened || logger.is_log_table_enabled(result)) + { + if (error_msg) + my_error(ER_BAD_LOG_STATEMENT, MYF(0), error_msg); + return result; + } + return 0; } @@ -1796,6 +1820,7 @@ static int binlog_close_connection(handlerton *hton, THD *thd) contain updates to non-transactional tables. Or it can be a flush of a statement cache. */ + static int binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr, Log_event *end_ev, bool all, bool using_stmt, @@ -1803,6 +1828,7 @@ binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr, { int error= 0; DBUG_ENTER("binlog_flush_cache"); + DBUG_PRINT("enter", ("end_ev: %p", end_ev)); if ((using_stmt && !cache_mngr->stmt_cache.empty()) || (using_trx && !cache_mngr->trx_cache.empty())) @@ -1861,6 +1887,7 @@ static inline int binlog_commit_flush_stmt_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr) { + DBUG_ENTER("binlog_commit_flush_stmt_cache"); #ifdef WITH_WSREP if (thd->wsrep_mysql_replicated > 0) { @@ -1871,7 +1898,7 @@ binlog_commit_flush_stmt_cache(THD *thd, bool all, Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"), FALSE, TRUE, TRUE, 0); - return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE)); + DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE)); } /** @@ -1886,9 +1913,10 @@ binlog_commit_flush_stmt_cache(THD *thd, bool all, static inline int binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr) { + DBUG_ENTER("binlog_commit_flush_trx_cache"); Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"), TRUE, TRUE, TRUE, 0); - return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE)); + DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE)); } /** @@ -3116,7 +3144,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name, MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period) - :reset_master_pending(false), + :reset_master_pending(false), mark_xid_done_waiting(0), bytes_written(0), file_id(1), open_count(1), group_commit_queue(0), group_commit_queue_busy(FALSE), num_commits(0), num_group_commits(0), @@ -3932,22 +3960,11 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log, const char* save_name; DBUG_ENTER("reset_logs"); - if (thd) - ha_reset_logs(thd); - /* - We need to get both locks to be sure that no one is trying to - write to the index log file. - */ - mysql_mutex_lock(&LOCK_log); - mysql_mutex_lock(&LOCK_index); - if (!is_relay_log) { if (init_state && !is_empty_state()) { my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0)); - mysql_mutex_unlock(&LOCK_index); - mysql_mutex_unlock(&LOCK_log); DBUG_RETURN(1); } @@ -3956,11 +3973,29 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log, This ensures that a binlog checkpoint will not try to write binlog checkpoint events, which would be useless (as we are deleting the binlog anyway) and could deadlock, as we are holding LOCK_log. + + Wait for any mark_xid_done() calls that might be already running to + complete (mark_xid_done_waiting counter to drop to zero); we need to + do this before we take the LOCK_log to not deadlock. */ mysql_mutex_lock(&LOCK_xid_list); reset_master_pending= true; + while (mark_xid_done_waiting > 0) + mysql_cond_wait(&COND_xid_list, &LOCK_xid_list); mysql_mutex_unlock(&LOCK_xid_list); + } + + if (thd) + ha_reset_logs(thd); + /* + We need to get both locks to be sure that no one is trying to + write to the index log file. + */ + mysql_mutex_lock(&LOCK_log); + mysql_mutex_lock(&LOCK_index); + if (!is_relay_log) + { /* We are going to nuke all binary log files. Without binlog, we cannot XA recover prepared-but-not-committed @@ -5416,6 +5451,10 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, (long) table, table->s->table_name.str, table->s->table_map_id)); + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_transactional= 1; + /* Pre-conditions */ #ifdef WITH_WSREP DBUG_ASSERT(is_current_stmt_binlog_format_row() && @@ -5438,7 +5477,7 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional)); if (with_annotate && *with_annotate) { - Annotate_rows_log_event anno(current_thd, is_transactional, false); + Annotate_rows_log_event anno(table->in_use, is_transactional, false); /* Annotate event should be written not more than once */ *with_annotate= 0; if ((error= anno.write(file))) @@ -5605,6 +5644,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, /* Generate a new global transaction ID, and write it to the binlog */ + bool MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, bool is_transactional, uint64 commit_id) @@ -5614,6 +5654,16 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, uint32 server_id= thd->variables.server_id; uint64 seq_no= thd->variables.gtid_seq_no; int err; + DBUG_ENTER("write_gtid_event"); + DBUG_PRINT("enter", ("standalone: %d", standalone)); + + if (thd->variables.option_bits & OPTION_GTID_BEGIN) + { + DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. " + "Master and slave will have different GTID values")); + /* Reset the flag, as we will write out a GTID anyway */ + thd->variables.option_bits&= ~OPTION_GTID_BEGIN; + } /* Reset the session variable gtid_seq_no, to reduce the risk of accidentally @@ -5638,7 +5688,8 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, seq_no= gtid.seq_no; } if (err) - return true; + DBUG_RETURN(true); + thd->last_commit_gtid= gtid; Gtid_log_event gtid_event(thd, seq_no, domain_id, standalone, LOG_EVENT_SUPPRESS_USE_F, is_transactional, @@ -5646,10 +5697,10 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, /* Write the event to the binary log. */ if (gtid_event.write(&mysql_bin_log.log_file)) - return true; + DBUG_RETURN(true); status_var_add(thd->status_var.binlog_bytes_written, gtid_event.data_written); - return false; + DBUG_RETURN(false); } @@ -5831,14 +5882,22 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) { THD *thd= event_info->thd; bool error= 1; - DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)"); binlog_cache_data *cache_data= 0; bool is_trans_cache= FALSE; bool using_trans= event_info->use_trans_cache(); bool direct= event_info->use_direct_logging(); ulong prev_binlog_id; + DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)"); LINT_INIT(prev_binlog_id); + if (thd->variables.option_bits & OPTION_GTID_BEGIN) + { + DBUG_PRINT("info", ("OPTION_GTID_BEGIN was set")); + /* Wait for commit from binary log before we commit */ + direct= 0; + using_trans= 1; + } + if (thd->binlog_evt_union.do_union) { /* @@ -5892,6 +5951,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) if (direct) { + DBUG_PRINT("info", ("direct is set")); file= &log_file; my_org_b_tell= my_b_tell(file); mysql_mutex_lock(&LOCK_log); @@ -6824,16 +6884,17 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd, to commit. If so, we add those to the queue as well, transitively for all waiters. - @retval TRUE If queued as the first entry in the queue (meaning this - is the leader) - @retval FALSE Otherwise + @retval < 0 Error + @retval > 0 If queued as the first entry in the queue (meaning this + is the leader) + @retval 0 Otherwise (queued as participant, leader handles the commit) */ -bool +int MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) { group_commit_entry *entry, *orig_queue; - wait_for_commit *list, *cur, *last; + wait_for_commit *cur, *last; wait_for_commit *wfc; DBUG_ENTER("MYSQL_BIN_LOG::queue_for_group_commit"); @@ -6847,12 +6908,15 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) */ wfc= orig_entry->thd->wait_for_commit_ptr; orig_entry->queued_by_other= false; - if (wfc && wfc->waiting_for_commit) + if (wfc && wfc->waitee) { mysql_mutex_lock(&wfc->LOCK_wait_commit); /* Do an extra check here, this time safely under lock. */ - if (wfc->waiting_for_commit) + if (wfc->waitee) { + PSI_stage_info old_stage; + wait_for_commit *loc_waitee; + /* By setting wfc->opaque_pointer to our own entry, we mark that we are ready to commit, but waiting for another transaction to commit before @@ -6864,15 +6928,56 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) */ wfc->opaque_pointer= orig_entry; DEBUG_SYNC(orig_entry->thd, "group_commit_waiting_for_prior"); - do - { + orig_entry->thd->ENTER_COND(&wfc->COND_wait_commit, + &wfc->LOCK_wait_commit, + &stage_waiting_for_prior_transaction_to_commit, + &old_stage); + while ((loc_waitee= wfc->waitee) && !orig_entry->thd->check_killed()) mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit); - } while (wfc->waiting_for_commit); wfc->opaque_pointer= NULL; DBUG_PRINT("info", ("After waiting for prior commit, queued_by_other=%d", orig_entry->queued_by_other)); + + if (loc_waitee) + { + /* Wait terminated due to kill. */ + mysql_mutex_lock(&loc_waitee->LOCK_wait_commit); + if (loc_waitee->wakeup_subsequent_commits_running || + orig_entry->queued_by_other) + { + /* Our waitee is already waking us up, so ignore the kill. */ + mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit); + do + { + mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit); + } while (wfc->waitee); + } + else + { + /* We were killed, so remove us from the list of waitee. */ + wfc->remove_from_list(&loc_waitee->subsequent_commits_list); + mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit); + wfc->waitee= NULL; + + orig_entry->thd->EXIT_COND(&old_stage); + /* Interrupted by kill. */ + DEBUG_SYNC(orig_entry->thd, "group_commit_waiting_for_prior_killed"); + wfc->wakeup_error= orig_entry->thd->killed_errno(); + if (wfc->wakeup_error) + wfc->wakeup_error= ER_QUERY_INTERRUPTED; + my_message(wfc->wakeup_error, ER(wfc->wakeup_error), MYF(0)); + DBUG_RETURN(-1); + } + } + orig_entry->thd->EXIT_COND(&old_stage); } - mysql_mutex_unlock(&wfc->LOCK_wait_commit); + else + mysql_mutex_unlock(&wfc->LOCK_wait_commit); + } + if (wfc && wfc->wakeup_error) + { + my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); + DBUG_RETURN(-1); } /* @@ -6881,7 +6986,7 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) then there is nothing else to do. */ if (orig_entry->queued_by_other) - DBUG_RETURN(false); + DBUG_RETURN(0); /* Now enqueue ourselves in the group commit queue. */ DEBUG_SYNC(orig_entry->thd, "commit_before_enqueue"); @@ -6919,9 +7024,8 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) used by the caller or any other function. */ - list= wfc; - cur= list; - last= list; + cur= wfc; + last= wfc; entry= orig_entry; for (;;) { @@ -6947,11 +7051,11 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) */ if (cur->subsequent_commits_list) { - bool have_lock; wait_for_commit *waiter; + wait_for_commit *wakeup_list= NULL; + wait_for_commit **wakeup_next_ptr= &wakeup_list; mysql_mutex_lock(&cur->LOCK_wait_commit); - have_lock= true; /* Grab the list, now safely under lock, and process it if still non-empty. @@ -6992,18 +7096,68 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) For this, we need to set the "wakeup running" flag and release the waitee lock to avoid a deadlock, see comments on THD::wakeup_subsequent_commits2() for details. + + So we need to put these on a list and delay the wakeup until we + have released the lock. + */ + *wakeup_next_ptr= waiter; + wakeup_next_ptr= &waiter->next_subsequent_commit; + } + waiter= next; + } + if (wakeup_list) + { + /* Now release our lock and do the wakeups that were delayed above. */ + cur->wakeup_subsequent_commits_running= true; + mysql_mutex_unlock(&cur->LOCK_wait_commit); + for (;;) + { + wait_for_commit *next; + + /* + ToDo: We wakeup the waiter here, so that it can have the chance to + reach its own commit state and queue up for this same group commit, + if it is still pending. + + One problem with this is that if the waiter does not reach its own + commit state before this group commit starts, and then the group + commit fails (binlog write failure), we do not get to propagate + the error to the waiter. + + A solution for this could be to delay the wakeup until commit is + successful. But then we need to set a flag in the waitee that it is + already queued for group commit, so that the waiter can check this + flag and queue itself if it _does_ reach the commit state in time. + + (But error handling in case of binlog write failure is currently + broken in other ways, as well). */ - if (have_lock) + if (&wakeup_list->next_subsequent_commit == wakeup_next_ptr) { - have_lock= false; - cur->wakeup_subsequent_commits_running= true; - mysql_mutex_unlock(&cur->LOCK_wait_commit); + /* The last one in the list. */ + wakeup_list->wakeup(0); + break; } - waiter->wakeup(0); + /* + Important: don't access wakeup_list->next after the wakeup() call, + it may be invalidated by the other thread. + */ + next= wakeup_list->next_subsequent_commit; + wakeup_list->wakeup(0); + wakeup_list= next; } - waiter= next; + /* + We need a full memory barrier between walking the list and clearing + the flag wakeup_subsequent_commits_running. This barrier is needed + to ensure that no other thread will start to modify the list + pointers before we are done traversing the list. + + But wait_for_commit::wakeup(), which was called above, does a full + memory barrier already (it locks a mutex). + */ + cur->wakeup_subsequent_commits_running= false; } - if (have_lock) + else mysql_mutex_unlock(&cur->LOCK_wait_commit); } if (cur == last) @@ -7017,29 +7171,6 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) DBUG_ASSERT(entry != NULL); } - /* - Now we need to clear the wakeup_subsequent_commits_running flags. - - We need a full memory barrier between walking the list above, and clearing - the flag wakeup_subsequent_commits_running below. This barrier is needed - to ensure that no other thread will start to modify the list pointers - before we are done traversing the list. - - But wait_for_commit::wakeup(), which was called above for any other thread - that might modify the list in parallel, does a full memory barrier already - (it locks a mutex). - */ - if (list) - { - for (;;) - { - list->wakeup_subsequent_commits_running= false; - if (list == last) - break; - list= list->next_subsequent_commit; - } - } - if (opt_binlog_commit_wait_count > 0) mysql_cond_signal(&COND_prepare_ordered); mysql_mutex_unlock(&LOCK_prepare_ordered); @@ -7053,13 +7184,15 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) bool MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) { - bool is_leader= queue_for_group_commit(entry); + int is_leader= queue_for_group_commit(entry); /* The first in the queue handles group commit for all; the others just wait to be signalled when group commit is done. */ - if (is_leader) + if (is_leader < 0) + return true; /* Error */ + else if (is_leader) trx_group_commit_leader(entry); else if (!entry->queued_by_other) entry->thd->wait_for_wakeup_ready(); @@ -7423,16 +7556,17 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry, uint64 commit_id) { binlog_cache_mngr *mngr= entry->cache_mngr; + DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_or_stmt"); if (write_gtid_event(entry->thd, false, entry->using_trx_cache, commit_id)) - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); if (entry->using_stmt_cache && !mngr->stmt_cache.empty() && write_cache(entry->thd, mngr->get_binlog_cache_log(FALSE))) { entry->error_cache= &mngr->stmt_cache.cache_log; entry->commit_errno= errno; - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } if (entry->using_trx_cache && !mngr->trx_cache.empty()) @@ -7453,7 +7587,7 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry, { entry->error_cache= &mngr->trx_cache.cache_log; entry->commit_errno= errno; - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } } @@ -7461,7 +7595,7 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry, { entry->error_cache= NULL; entry->commit_errno= errno; - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } status_var_add(entry->thd->status_var.binlog_bytes_written, entry->end_event->data_written); @@ -7472,7 +7606,7 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry, { entry->error_cache= NULL; entry->commit_errno= errno; - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } } @@ -7480,16 +7614,16 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry, { entry->error_cache= &mngr->stmt_cache.cache_log; entry->commit_errno= errno; - return ER_ERROR_ON_READ; + DBUG_RETURN(ER_ERROR_ON_WRITE); } if (mngr->get_binlog_cache_log(TRUE)->error) // Error on read { entry->error_cache= &mngr->trx_cache.cache_log; entry->commit_errno= errno; - return ER_ERROR_ON_READ; + DBUG_RETURN(ER_ERROR_ON_WRITE); } - return 0; + DBUG_RETURN(0); } @@ -8979,9 +9113,13 @@ TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint) locks in the opposite order. */ + ++mark_xid_done_waiting; mysql_mutex_unlock(&LOCK_xid_list); mysql_mutex_lock(&LOCK_log); mysql_mutex_lock(&LOCK_xid_list); + --mark_xid_done_waiting; + if (unlikely(reset_master_pending)) + mysql_cond_signal(&COND_xid_list); /* We need to reload current_binlog_id due to release/re-take of lock. */ current= current_binlog_id; @@ -9178,7 +9316,7 @@ start_binlog_background_thread() array_elements(all_binlog_threads)); #endif - if (mysql_thread_create(key_thread_binlog, &th, NULL, + if (mysql_thread_create(key_thread_binlog, &th, &connection_attrib, binlog_background_thread, NULL)) return 1; diff --git a/sql/log.h b/sql/log.h index 928c1058b7f..fb430e4c206 100644 --- a/sql/log.h +++ b/sql/log.h @@ -471,6 +471,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG checkpoint arrives - when all have arrived, RESET MASTER will complete. */ bool reset_master_pending; + ulong mark_xid_done_waiting; /* LOCK_log and LOCK_index are inited by init_pthread_objects() */ mysql_mutex_t LOCK_index; @@ -540,7 +541,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG void do_checkpoint_request(ulong binlog_id); void purge(); int write_transaction_or_stmt(group_commit_entry *entry, uint64 commit_id); - bool queue_for_group_commit(group_commit_entry *entry); + int queue_for_group_commit(group_commit_entry *entry); bool write_transaction_to_binlog_events(group_commit_entry *entry); void trx_group_commit_leader(group_commit_entry *leader); bool is_xidlist_idle_nolock(); @@ -833,8 +834,8 @@ public: }; -int check_if_log_table(size_t db_len, const char *db, size_t table_name_len, - const char *table_name, bool check_if_opened); +int check_if_log_table(const TABLE_LIST *table, bool check_if_opened, + const char *errmsg); class Log_to_csv_event_handler: public Log_event_handler { diff --git a/sql/log_event.cc b/sql/log_event.cc index de53a26dd81..9a792ad82a0 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -17,6 +17,7 @@ #include "sql_priv.h" +#include "mysqld_error.h" #ifndef MYSQL_CLIENT #include "my_global.h" // REQUIRED by log_event.h > m_string.h > my_bitmap.h @@ -752,7 +753,7 @@ static void print_set_option(IO_CACHE* file, uint32 bits_changed, { if (*need_comma) my_b_write(file, ", ", 2); - my_b_printf(file,"%s=%d", name, test(flags & option)); + my_b_printf(file, "%s=%d", name, MY_TEST(flags & option)); *need_comma= 1; } } @@ -1092,7 +1093,7 @@ my_bool Log_event::need_checksum() (checksum_alg != BINLOG_CHECKSUM_ALG_OFF) : ((binlog_checksum_options != BINLOG_CHECKSUM_ALG_OFF) && (cache_type == Log_event::EVENT_NO_CACHE)) ? - test(binlog_checksum_options) : FALSE); + MY_TEST(binlog_checksum_options) : FALSE); /* FD calls the methods before data_written has been calculated. @@ -2468,6 +2469,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, else { my_b_printf(file, "### @%lu=", (ulong)i + 1); + size_t fsize= td->calc_field_size((uint)i, (uchar*) value); + if (value + fsize > m_rows_end) + { + my_b_printf(file, "***Corrupted replication event was detected." + " Not printing the value***\n"); + value+= fsize; + return 0; + } size_t size= log_event_print_value(file, value, td->type(i), td->field_metadata(i), typestr, sizeof(typestr)); @@ -3732,9 +3741,14 @@ Query_log_event::begin_event(String *packet, ulong ev_offset, DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF || checksum_alg == BINLOG_CHECKSUM_ALG_OFF); - /* Currently we only need to replace GTID event. */ - DBUG_ASSERT(data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN); - if (data_len != LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN) + /* + Currently we only need to replace GTID event. + The length of GTID differs depending on whether it contains commit id. + */ + DBUG_ASSERT(data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN || + data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN + 2); + if (data_len != LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN && + data_len != LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN + 2) return 1; flags= uint2korr(p + FLAGS_OFFSET); @@ -3747,9 +3761,22 @@ Query_log_event::begin_event(String *packet, ulong ev_offset, int4store(q + Q_EXEC_TIME_OFFSET, 0); q[Q_DB_LEN_OFFSET]= 0; int2store(q + Q_ERR_CODE_OFFSET, 0); - int2store(q + Q_STATUS_VARS_LEN_OFFSET, 0); - q[Q_DATA_OFFSET]= 0; /* Zero terminator for empty db */ - q+= Q_DATA_OFFSET + 1; + if (data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN) + { + int2store(q + Q_STATUS_VARS_LEN_OFFSET, 0); + q[Q_DATA_OFFSET]= 0; /* Zero terminator for empty db */ + q+= Q_DATA_OFFSET + 1; + } + else + { + DBUG_ASSERT(data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN + 2); + /* Put in an empty time_zone_str to take up the extra 2 bytes. */ + int2store(q + Q_STATUS_VARS_LEN_OFFSET, 2); + q[Q_DATA_OFFSET]= Q_TIME_ZONE_CODE; + q[Q_DATA_OFFSET+1]= 0; /* Zero length for empty time_zone_str */ + q[Q_DATA_OFFSET+2]= 0; /* Zero terminator for empty db */ + q+= Q_DATA_OFFSET + 3; + } memcpy(q, "BEGIN", 5); if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32) @@ -3989,6 +4016,8 @@ bool test_if_equal_repl_errors(int expected_error, int actual_error) case ER_AUTOINC_READ_FAILED: return (actual_error == ER_AUTOINC_READ_FAILED || actual_error == HA_ERR_AUTOINC_ERANGE); + case ER_UNKNOWN_TABLE: + return actual_error == ER_IT_IS_A_VIEW; default: break; } @@ -4027,6 +4056,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, #else Rpl_filter *rpl_filter= rli->mi->rpl_filter; #endif /* WITH_WSREP */ + bool current_stmt_is_commit; DBUG_ENTER("Query_log_event::do_apply_event"); /* @@ -4053,7 +4083,9 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); - if (strcmp("COMMIT", query) == 0 && rgi->tables_to_lock) + current_stmt_is_commit= is_commit(); + + if (current_stmt_is_commit && rgi->tables_to_lock) { /* Cleaning-up the last statement context: @@ -4102,9 +4134,11 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, thd->variables.pseudo_thread_id= thread_id; // for temp tables DBUG_PRINT("query",("%s", thd->query())); - if (ignored_error_code((expected_error= error_code)) || - !unexpected_error_code(expected_error)) + if (!(expected_error= error_code) || + ignored_error_code(expected_error) || + !unexpected_error_code(expected_error)) { + thd->slave_expected_error= expected_error; if (flags2_inited) /* all bits of thd->variables.option_bits which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG @@ -4206,12 +4240,13 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, Record any GTID in the same transaction, so slave state is transactionally consistent. */ - if (strcmp("COMMIT", query) == 0 && (sub_id= rgi->gtid_sub_id)) + if (current_stmt_is_commit && (sub_id= rgi->gtid_sub_id)) { /* Clear the GTID from the RLI so we don't accidentally reuse it. */ rgi->gtid_sub_id= 0; gtid= rgi->current_gtid; + thd->variables.option_bits&= ~OPTION_GTID_BEGIN; if (rpl_global_gtid_slave_state.record_gtid(thd, >id, sub_id, true, false)) { rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, @@ -4241,6 +4276,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, concurrency_error_code(expected_error))) { thd->variables.option_bits|= OPTION_MASTER_SQL_ERROR; + thd->variables.option_bits&= ~OPTION_GTID_BEGIN; } /* Execute the query (note that we bypass dispatch_command()) */ Parser_state parser_state; @@ -4404,8 +4440,7 @@ Default database: '%s'. Query: '%s'", to shutdown trying to finish incomplete events group. */ DBUG_EXECUTE_IF("stop_slave_middle_group", - if (strcmp("COMMIT", query) != 0 && - strcmp("BEGIN", query) != 0) + if (!current_stmt_is_commit && is_begin() == 0) { if (thd->transaction.all.modified_non_trans_table) const_cast<Relay_log_info*>(rli)->abort_slave= 1; @@ -4466,7 +4501,7 @@ Query_log_event::do_shall_skip(rpl_group_info *rgi) { Relay_log_info *rli= rgi->rli; DBUG_ENTER("Query_log_event::do_shall_skip"); - DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len)); + DBUG_PRINT("debug", ("query: '%s' q_len: %d", query, q_len)); DBUG_ASSERT(query && q_len > 0); DBUG_ASSERT(thd == rgi->thd); @@ -4482,13 +4517,13 @@ Query_log_event::do_shall_skip(rpl_group_info *rgi) { if (is_begin()) { - thd->variables.option_bits|= OPTION_BEGIN; + thd->variables.option_bits|= OPTION_BEGIN | OPTION_GTID_BEGIN; DBUG_RETURN(Log_event::continue_group(rgi)); } if (is_commit() || is_rollback()) { - thd->variables.option_bits&= ~OPTION_BEGIN; + thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN); DBUG_RETURN(Log_event::EVENT_SKIP_COUNT); } } @@ -5559,11 +5594,22 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, fields = (char*)field_lens + num_fields; table_name = fields + field_block_len; db = table_name + table_name_len + 1; + DBUG_EXECUTE_IF ("simulate_invalid_address", + db_len = data_len;); fname = db + db_len + 1; + if ((db_len > data_len) || (fname > buf_end)) + goto err; fname_len = (uint) strlen(fname); + if ((fname_len > data_len) || (fname + fname_len > buf_end)) + goto err; // null termination is accomplished by the caller doing buf[event_len]=0 DBUG_RETURN(0); + +err: + // Invalid event. + table_name = 0; + DBUG_RETURN(1); } @@ -5930,6 +5976,7 @@ error: thd->reset_query(); thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN); thd->get_stmt_da()->set_overwrite_status(false); close_thread_tables(thd); /* @@ -6232,6 +6279,16 @@ void Binlog_checkpoint_log_event::pack_info(THD *thd, Protocol *protocol) { protocol->store(binlog_file_name, binlog_file_len, &my_charset_bin); } + + +Log_event::enum_skip_reason +Binlog_checkpoint_log_event::do_shall_skip(rpl_group_info *rgi) +{ + enum_skip_reason reason= Log_event::do_shall_skip(rgi); + if (reason == EVENT_SKIP_COUNT) + reason= EVENT_SKIP_NOT; + return reason; +} #endif @@ -6432,8 +6489,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event, { if (*need_dummy_event) return Query_log_event::dummy_event(packet, ev_offset, checksum_alg); - else - return 0; + return 0; } *need_dummy_event= true; @@ -6480,24 +6536,28 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi) this->server_id, this->seq_no)) return 1; } + + DBUG_ASSERT((thd->variables.option_bits & OPTION_GTID_BEGIN) == 0); if (flags2 & FL_STANDALONE) return 0; /* Execute this like a BEGIN query event. */ + thd->variables.option_bits|= OPTION_GTID_BEGIN; + DBUG_PRINT("info", ("Set OPTION_GTID_BEGIN")); thd->set_query_and_id(gtid_begin_string, sizeof(gtid_begin_string)-1, &my_charset_bin, next_query_id()); - Parser_state parser_state; - if (!parser_state.init(thd, thd->query(), thd->query_length())) - { - mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); - /* Finalize server status flags after executing a statement. */ - thd->update_server_status(); - log_slow_statement(thd); - if (unlikely(thd->is_fatal_error)) - thd->is_slave_error= 1; - else if (likely(!thd->is_slave_error)) - general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); + thd->lex->sql_command= SQLCOM_BEGIN; + thd->is_slave_error= 0; + status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]); + if (trans_begin(thd, 0)) + { + DBUG_PRINT("error", ("trans_begin() failed")); + thd->is_slave_error= 1; } + thd->update_stats(); + + if (likely(!thd->is_slave_error)) + general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); thd->reset_query(); free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); @@ -6759,7 +6819,7 @@ Gtid_list_log_event::write(IO_CACHE *file) int Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) { - Relay_log_info const *rli= rgi->rli; + Relay_log_info *rli= const_cast<Relay_log_info*>(rgi->rli); int ret; if (gl_flags & FLAG_IGN_GTIDS) { @@ -6779,15 +6839,26 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) { char str_buf[128]; String str(str_buf, sizeof(str_buf), system_charset_info); - const_cast<Relay_log_info*>(rli)->until_gtid_pos.to_string(&str); + rli->until_gtid_pos.to_string(&str); sql_print_information("Slave SQL thread stops because it reached its" " UNTIL master_gtid_pos %s", str.c_ptr_safe()); - const_cast<Relay_log_info*>(rli)->abort_slave= true; + rli->abort_slave= true; + rli->stop_for_until= true; } return ret; } +Log_event::enum_skip_reason +Gtid_list_log_event::do_shall_skip(rpl_group_info *rgi) +{ + enum_skip_reason reason= Log_event::do_shall_skip(rgi); + if (reason == EVENT_SKIP_COUNT) + reason= EVENT_SKIP_NOT; + return reason; +} + + void Gtid_list_log_event::pack_info(THD *thd, Protocol *protocol) { @@ -7012,9 +7083,7 @@ int Intvar_log_event::do_apply_event(rpl_group_info *rgi) switch (type) { case LAST_INSERT_ID_EVENT: - thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1; - thd->first_successful_insert_id_in_prev_stmt_for_binlog= - thd->first_successful_insert_id_in_prev_stmt= val; + thd->first_successful_insert_id_in_prev_stmt= val; DBUG_PRINT("info",("last_insert_id_event: %ld", (long) val)); break; case INSERT_ID_EVENT: @@ -7276,10 +7345,11 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) /* For a slave Xid_log_event is COMMIT */ general_log_print(thd, COM_QUERY, "COMMIT /* implicit, from Xid_log_event */"); + thd->variables.option_bits&= ~OPTION_GTID_BEGIN; res= trans_commit(thd); /* Automatically rolls back on error. */ thd->mdl_context.release_transactional_locks(); - if (sub_id) + if (!res && sub_id) rpl_global_gtid_slave_state.update_state_hash(sub_id, >id); /* @@ -7297,7 +7367,7 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi) if (rgi->rli->slave_skip_counter > 0) { DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION)); - thd->variables.option_bits&= ~OPTION_BEGIN; + thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN); DBUG_RETURN(Log_event::EVENT_SKIP_COUNT); } #ifdef WITH_WSREP @@ -9168,8 +9238,8 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, set_flags(NO_FOREIGN_KEY_CHECKS_F); if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS) set_flags(RELAXED_UNIQUE_CHECKS_F); - /* if bitmap_init fails, caught in is_valid() */ - if (likely(!bitmap_init(&m_cols, + /* if my_bitmap_init fails, caught in is_valid() */ + if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, m_width, false))) @@ -9183,7 +9253,7 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, } else { - // Needed because bitmap_init() does not set it to null on failure + // Needed because my_bitmap_init() does not set it to null on failure m_cols.bitmap= 0; } } @@ -9284,8 +9354,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); m_width = net_field_length(&ptr_after_width); DBUG_PRINT("debug", ("m_width=%lu", m_width)); - /* if bitmap_init fails, catched in is_valid() */ - if (likely(!bitmap_init(&m_cols, + /* if my_bitmap_init fails, catched in is_valid() */ + if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, m_width, false))) @@ -9298,7 +9368,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, } else { - // Needed because bitmap_init() does not set it to null on failure + // Needed because my_bitmap_init() does not set it to null on failure m_cols.bitmap= NULL; DBUG_VOID_RETURN; } @@ -9310,8 +9380,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); - /* if bitmap_init fails, caught in is_valid() */ - if (likely(!bitmap_init(&m_cols_ai, + /* if my_bitmap_init fails, caught in is_valid() */ + if (likely(!my_bitmap_init(&m_cols_ai, m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL, m_width, false))) @@ -9325,7 +9395,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, } else { - // Needed because bitmap_init() does not set it to null on failure + // Needed because my_bitmap_init() does not set it to null on failure m_cols_ai.bitmap= 0; DBUG_VOID_RETURN; } @@ -9356,8 +9426,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, Rows_log_event::~Rows_log_event() { if (m_cols.bitmap == m_bitbuf) // no my_malloc happened - m_cols.bitmap= 0; // so no my_free in bitmap_free - bitmap_free(&m_cols); // To pair with bitmap_init(). + m_cols.bitmap= 0; // so no my_free in my_bitmap_free + my_bitmap_free(&m_cols); // To pair with my_bitmap_init(). my_free(m_rows_buf); my_free(m_extra_row_data); } @@ -12073,8 +12143,8 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg, void Update_rows_log_event::init(MY_BITMAP const *cols) { - /* if bitmap_init fails, caught in is_valid() */ - if (likely(!bitmap_init(&m_cols_ai, + /* if my_bitmap_init fails, caught in is_valid() */ + if (likely(!my_bitmap_init(&m_cols_ai, m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL, m_width, false))) @@ -12093,8 +12163,8 @@ void Update_rows_log_event::init(MY_BITMAP const *cols) Update_rows_log_event::~Update_rows_log_event() { if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened - m_cols_ai.bitmap= 0; // so no my_free in bitmap_free - bitmap_free(&m_cols_ai); // To pair with bitmap_init(). + m_cols_ai.bitmap= 0; // so no my_free in my_bitmap_free + my_bitmap_free(&m_cols_ai); // To pair with my_bitmap_init(). } diff --git a/sql/log_event.h b/sql/log_event.h index cd00b40f080..a9d1b08171f 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3075,6 +3075,7 @@ public: bool is_valid() const { return binlog_file_name != 0; } #ifdef MYSQL_SERVER bool write(IO_CACHE* file); + enum_skip_reason do_shall_skip(rpl_group_info *rgi); #endif }; @@ -3122,12 +3123,15 @@ public: <td>flags</td> <td>1 byte bitfield</td> <td>Bit 0 set indicates stand-alone event (no terminating COMMIT)</td> + <td>Bit 1 set indicates group commit, and that commit id exists</td> </tr> <tr> - <td>Reserved</td> - <td>6 bytes</td> - <td>Reserved bytes, set to 0. Maybe be used for future expansion.</td> + <td>Reserved (no group commit) / commit id (group commit) (see flags bit 1)</td> + <td>6 bytes / 8 bytes</td> + <td>Reserved bytes, set to 0. Maybe be used for future expansion (no + group commit). OR commit id, same for all GTIDs in the same group + commit (see flags bit 1).</td> </tr> </table> @@ -3292,6 +3296,7 @@ public: bool to_packet(String *packet); bool write(IO_CACHE *file); virtual int do_apply_event(rpl_group_info *rgi); + enum_skip_reason do_shall_skip(rpl_group_info *rgi); #endif static bool peek(const char *event_start, uint32 event_len, uint8 checksum_alg, @@ -4321,13 +4326,8 @@ protected: DBUG_ASSERT(m_table); ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT); - int const result= ::unpack_row(rgi, m_table, m_width, m_curr_row, - m_rows_end, &m_cols, - &m_curr_row_end, &m_master_reclength); - if (m_curr_row_end > m_rows_end) - my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0)); - ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT); - return result; + return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols, + &m_curr_row_end, &m_master_reclength, m_rows_end); } #endif diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 7b89d5bdf08..0cb78686243 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1244,8 +1244,8 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, set_flags(NO_FOREIGN_KEY_CHECKS_F); if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS) set_flags(RELAXED_UNIQUE_CHECKS_F); - /* if bitmap_init fails, caught in is_valid() */ - if (likely(!bitmap_init(&m_cols, + /* if my_bitmap_init fails, caught in is_valid() */ + if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, m_width, false))) @@ -1259,7 +1259,7 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, } else { - // Needed because bitmap_init() does not set it to null on failure + // Needed because my_bitmap_init() does not set it to null on failure m_cols.bitmap= 0; } } @@ -1313,8 +1313,8 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len, DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); m_width = net_field_length(&ptr_after_width); DBUG_PRINT("debug", ("m_width=%lu", m_width)); - /* if bitmap_init fails, catched in is_valid() */ - if (likely(!bitmap_init(&m_cols, + /* if my_bitmap_init fails, catched in is_valid() */ + if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, m_width, false))) @@ -1327,7 +1327,7 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len, } else { - // Needed because bitmap_init() does not set it to null on failure + // Needed because my_bitmap_init() does not set it to null on failure m_cols.bitmap= NULL; DBUG_VOID_RETURN; } @@ -1358,8 +1358,8 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len, Old_rows_log_event::~Old_rows_log_event() { if (m_cols.bitmap == m_bitbuf) // no my_malloc happened - m_cols.bitmap= 0; // so no my_free in bitmap_free - bitmap_free(&m_cols); // To pair with bitmap_init(). + m_cols.bitmap= 0; // so no my_free in my_bitmap_free + my_bitmap_free(&m_cols); // To pair with my_bitmap_init(). my_free(m_rows_buf); } diff --git a/sql/log_event_old.h b/sql/log_event_old.h index 97fc24a1bef..7408e121f96 100644 --- a/sql/log_event_old.h +++ b/sql/log_event_old.h @@ -1,4 +1,4 @@ -/* Copyright 2007 MySQL AB. +/* Copyright (c) 2007, 2013, Oracle and/or its affiliates. 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 @@ -207,11 +207,8 @@ protected: { DBUG_ASSERT(m_table); ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT); - int const result= ::unpack_row(rgi, m_table, m_width, m_curr_row, - m_rows_end, &m_cols, - &m_curr_row_end, &m_master_reclength); - ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT); - return result; + return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols, + &m_curr_row_end, &m_master_reclength, m_rows_end); } #endif diff --git a/sql/mdl.cc b/sql/mdl.cc index 2f00388a14b..7ef105b9269 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -136,15 +136,9 @@ class MDL_map_partition public: MDL_map_partition(); ~MDL_map_partition(); - inline MDL_lock *find_or_insert(const MDL_key *mdl_key, - my_hash_value_type hash_value); - unsigned long get_lock_owner(const MDL_key *key, - my_hash_value_type hash_value); + inline MDL_lock *find_or_insert(const MDL_key *mdl_key); + unsigned long get_lock_owner(const MDL_key *key); inline void remove(MDL_lock *lock); - my_hash_value_type get_key_hash(const MDL_key *mdl_key) const - { - return my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length()); - } private: bool move_from_hash_to_lock_mutex(MDL_lock *lock); /** A partition of all acquired locks in the server. */ @@ -778,13 +772,21 @@ void MDL_map::init() } +my_hash_value_type mdl_hash_function(const CHARSET_INFO *cs, + const uchar *key, size_t length) +{ + MDL_key *mdl_key= (MDL_key*) (key - offsetof(MDL_key, m_ptr)); + return mdl_key->hash_value(); +} + + /** Initialize the partition in the container with all MDL locks. */ MDL_map_partition::MDL_map_partition() { mysql_mutex_init(key_MDL_map_mutex, &m_mutex, NULL); - my_hash_init(&m_locks, &my_charset_bin, 16 /* FIXME */, 0, 0, - mdl_locks_key, 0, 0); + my_hash_init2(&m_locks, 0, &my_charset_bin, 16 /* FIXME */, 0, 0, + mdl_locks_key, mdl_hash_function, 0, 0); }; @@ -858,11 +860,10 @@ MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key) return lock; } - my_hash_value_type hash_value= m_partitions.at(0)->get_key_hash(mdl_key); - uint part_id= hash_value % mdl_locks_hash_partitions; + uint part_id= mdl_key->hash_value() % mdl_locks_hash_partitions; MDL_map_partition *part= m_partitions.at(part_id); - return part->find_or_insert(mdl_key, hash_value); + return part->find_or_insert(mdl_key); } @@ -875,15 +876,14 @@ MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key) @retval NULL - Failure (OOM). */ -MDL_lock* MDL_map_partition::find_or_insert(const MDL_key *mdl_key, - my_hash_value_type hash_value) +MDL_lock* MDL_map_partition::find_or_insert(const MDL_key *mdl_key) { MDL_lock *lock; retry: mysql_mutex_lock(&m_mutex); if (!(lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks, - hash_value, + mdl_key->hash_value(), mdl_key->ptr(), mdl_key->length()))) { @@ -1035,10 +1035,9 @@ MDL_map::get_lock_owner(const MDL_key *mdl_key) } else { - my_hash_value_type hash_value= m_partitions.at(0)->get_key_hash(mdl_key); - uint part_id= hash_value % mdl_locks_hash_partitions; + uint part_id= mdl_key->hash_value() % mdl_locks_hash_partitions; MDL_map_partition *part= m_partitions.at(part_id); - res= part->get_lock_owner(mdl_key, hash_value); + res= part->get_lock_owner(mdl_key); } return res; } @@ -1046,15 +1045,14 @@ MDL_map::get_lock_owner(const MDL_key *mdl_key) unsigned long -MDL_map_partition::get_lock_owner(const MDL_key *mdl_key, - my_hash_value_type hash_value) +MDL_map_partition::get_lock_owner(const MDL_key *mdl_key) { MDL_lock *lock; unsigned long res= 0; mysql_mutex_lock(&m_mutex); lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks, - hash_value, + mdl_key->hash_value(), mdl_key->ptr(), mdl_key->length()); if (lock) diff --git a/sql/mdl.h b/sql/mdl.h index b1e6bfa428a..13c098e26d8 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -28,6 +28,7 @@ #include <my_sys.h> #include <m_string.h> #include <mysql_com.h> +#include <hash.h> #include <algorithm> @@ -347,12 +348,15 @@ public: m_ptr - 1); m_length= static_cast<uint16>(strmake(m_ptr + m_db_name_length + 2, name, NAME_LEN) - m_ptr + 1); + m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1, + m_length - 1); } void mdl_key_init(const MDL_key *rhs) { memcpy(m_ptr, rhs->m_ptr, rhs->m_length); m_length= rhs->m_length; m_db_name_length= rhs->m_db_name_length; + m_hash_value= rhs->m_hash_value; } bool is_equal(const MDL_key *rhs) const { @@ -392,15 +396,26 @@ public: { return & m_namespace_to_wait_state_name[(int)mdl_namespace()]; } + my_hash_value_type hash_value() const + { + return m_hash_value + mdl_namespace(); + } + my_hash_value_type tc_hash_value() const + { + return m_hash_value; + } private: uint16 m_length; uint16 m_db_name_length; + my_hash_value_type m_hash_value; char m_ptr[MAX_MDLKEY_LENGTH]; static PSI_stage_info m_namespace_to_wait_state_name[NAMESPACE_END]; private: MDL_key(const MDL_key &); /* not implemented */ MDL_key &operator=(const MDL_key &); /* not implemented */ + friend my_hash_value_type mdl_hash_function(const CHARSET_INFO *, + const uchar *, size_t); }; diff --git a/sql/mem_root_array.h b/sql/mem_root_array.h index 9dc9638c13f..2dcc475cd7b 100644 --- a/sql/mem_root_array.h +++ b/sql/mem_root_array.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2011, 2012, 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 diff --git a/sql/message.h b/sql/message.h index dac0576d0c4..6641453a965 100644 --- a/sql/message.h +++ b/sql/message.h @@ -1,77 +1,77 @@ -#ifndef MESSAGE_INCLUDED
-#define MESSAGE_INCLUDED
-/* Copyright (c) 2008, 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 */
-
-/*
- To change or add messages mysqld writes to the Windows error log, run
- mc.exe message.mc
- and checkin generated messages.h, messages.rc and msg000001.bin under the
- source control.
- mc.exe can be installed with Windows SDK, some Visual Studio distributions
- do not include it.
-*/
-
-
-//
-// Values are 32 bit values layed out as follows:
-//
-// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
-// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
-// +---+-+-+-----------------------+-------------------------------+
-// |Sev|C|R| Facility | Code |
-// +---+-+-+-----------------------+-------------------------------+
-//
-// where
-//
-// Sev - is the severity code
-//
-// 00 - Success
-// 01 - Informational
-// 10 - Warning
-// 11 - Error
-//
-// C - is the Customer code flag
-//
-// R - is a reserved bit
-//
-// Facility - is the facility code
-//
-// Code - is the facility's status code
-//
-//
-// Define the facility codes
-//
-
-
-//
-// Define the severity codes
-//
-
-
-//
-// MessageId: MSG_DEFAULT
-//
-// MessageText:
-//
-// %1For more information, see Help and Support Center at http://www.mysql.com.
-//
-//
-//
-#define MSG_DEFAULT 0xC0000064L
-
-#endif /* MESSAGE_INCLUDED */
-
+#ifndef MESSAGE_INCLUDED +#define MESSAGE_INCLUDED +/* Copyright (c) 2008, 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 */ + +/* + To change or add messages mysqld writes to the Windows error log, run + mc.exe message.mc + and checkin generated messages.h, messages.rc and msg000001.bin under the + source control. + mc.exe can be installed with Windows SDK, some Visual Studio distributions + do not include it. +*/ + + +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// + + +// +// Define the severity codes +// + + +// +// MessageId: MSG_DEFAULT +// +// MessageText: +// +// %1For more information, see Help and Support Center at http://www.mysql.com. +// +// +// +#define MSG_DEFAULT 0xC0000064L + +#endif /* MESSAGE_INCLUDED */ + diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 03023927a1d..0c35ac5b029 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -225,7 +225,7 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param, DBUG_ENTER("handler::multi_range_read_init"); mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode); mrr_funcs= *seq_funcs; - mrr_is_output_sorted= test(mode & HA_MRR_SORTED); + mrr_is_output_sorted= MY_TEST(mode & HA_MRR_SORTED); mrr_have_range= FALSE; DBUG_RETURN(0); } @@ -292,7 +292,7 @@ scan_it_again: &mrr_cur_range.start_key : 0, mrr_cur_range.end_key.keypart_map ? &mrr_cur_range.end_key : 0, - test(mrr_cur_range.range_flag & EQ_RANGE), + MY_TEST(mrr_cur_range.range_flag & EQ_RANGE), mrr_is_output_sorted); if (result != HA_ERR_END_OF_FILE) break; @@ -549,12 +549,12 @@ int Mrr_ordered_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, keypar= *key_par_arg; KEY *key_info= &file->get_table()->key_info[file->active_index]; - keypar.index_ranges_unique= test(key_info->flags & HA_NOSAME && - key_info->user_defined_key_parts == - my_count_bits(keypar.key_tuple_map)); + keypar.index_ranges_unique= MY_TEST(key_info->flags & HA_NOSAME && + key_info->user_defined_key_parts == + my_count_bits(keypar.key_tuple_map)); mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode); - is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION); + is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION); mrr_funcs= *seq_funcs; source_exhausted= FALSE; if (support_scan_interruptions) @@ -578,7 +578,7 @@ int Mrr_ordered_rndpos_reader::init(handler *h_arg, file= h_arg; index_reader= index_reader_arg; rowid_buffer= buf; - is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION); + is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION); index_reader_exhausted= FALSE; index_reader_needs_refill= TRUE; return 0; @@ -817,7 +817,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, has not been called, so set the owner handler here as well. */ primary_file= h_arg; - is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION); + is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION); strategy_exhausted= FALSE; @@ -867,7 +867,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, if (do_sort_keys) { /* Pre-calculate some parameters of key sorting */ - keypar.use_key_pointers= test(mode & HA_MRR_MATERIALIZED_KEYS); + keypar.use_key_pointers= MY_TEST(mode & HA_MRR_MATERIALIZED_KEYS); seq_funcs->get_key_info(seq_init_param, &keypar.key_tuple_length, &keypar.key_tuple_map); keypar.key_size_in_keybuf= keypar.use_key_pointers? @@ -996,7 +996,7 @@ use_default_impl: so small that it can accomodate one rowid and one index tuple) */ if ((res= primary_file->ha_rnd_end()) || - (res= primary_file->ha_index_init(keyno, test(mode & HA_MRR_SORTED)))) + (res= primary_file->ha_index_init(keyno, MY_TEST(mode & HA_MRR_SORTED)))) { DBUG_RETURN(res); } @@ -1521,10 +1521,10 @@ bool key_uses_partial_cols(TABLE_SHARE *share, uint keyno) bool DsMrr_impl::check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno, uint mrr_flags) { - return test((mrr_flags & HA_MRR_SINGLE_POINT) && - keyno == share->primary_key && - primary_file->primary_key_is_clustered() && - optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS)); + return MY_TEST((mrr_flags & HA_MRR_SINGLE_POINT) && + keyno == share->primary_key && + primary_file->primary_key_is_clustered() && + optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS)); } @@ -1561,8 +1561,8 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, TABLE_SHARE *share= primary_file->get_table_share(); bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags); - bool using_cpk= test(keyno == share->primary_key && - primary_file->primary_key_is_clustered()); + bool using_cpk= MY_TEST(keyno == share->primary_key && + primary_file->primary_key_is_clustered()); *flags &= ~HA_MRR_IMPLEMENTATION_FLAGS; if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) || *flags & HA_MRR_INDEX_ONLY || @@ -1685,7 +1685,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags, double index_read_cost; elem_size= primary_file->ref_length + - sizeof(void*) * (!test(flags & HA_MRR_NO_ASSOCIATION)); + sizeof(void*) * (!MY_TEST(flags & HA_MRR_NO_ASSOCIATION)); max_buff_entries = *buffer_size / elem_size; if (!max_buff_entries) diff --git a/sql/my_apc.h b/sql/my_apc.h index 4643e641ff1..dfeef5eb8ac 100644 --- a/sql/my_apc.h +++ b/sql/my_apc.h @@ -62,7 +62,7 @@ public: */ inline bool have_apc_requests() { - return test(apc_calls); + return MY_TEST(apc_calls); } inline bool is_enabled() { return enabled; } diff --git a/sql/my_decimal.h b/sql/my_decimal.h index e561d180d12..fa85b41d70c 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2005, 2011, Oracle and/or its affiliates. - Copyright (c) 2011 Monty Program Ab +/* Copyright (c) 2005, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2014, SkySQL 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 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e486630f390..0997d30d88e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2014, SkySQL 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 @@ -380,7 +380,8 @@ static DYNAMIC_ARRAY all_options; ulong my_bind_addr; #endif /* WITH_WSREP */ bool opt_bin_log, opt_bin_log_used=0, opt_ignore_builtin_innodb= 0; -my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table= 0, opt_help= 0, opt_abort; +my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table= 0, opt_help= 0; +static my_bool opt_abort; ulonglong log_output_options; my_bool opt_userstat_running; my_bool opt_log_queries_not_using_indexes= 0; @@ -496,6 +497,7 @@ ulong open_files_limit, max_binlog_size; ulong slave_trans_retries; uint slave_net_timeout; ulong slave_exec_mode_options; +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; @@ -565,6 +567,7 @@ ulong rpl_recovery_rank=0; ulong stored_program_cache_size= 0; ulong opt_slave_parallel_threads= 0; +ulong opt_slave_domain_parallel_threads= 0; ulong opt_binlog_commit_wait_count= 0; ulong opt_binlog_commit_wait_usec= 0; ulong opt_slave_parallel_max_queued= 131072; @@ -905,6 +908,7 @@ PSI_mutex_key key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit; +PSI_mutex_key key_LOCK_gtid_waiting; PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered; PSI_mutex_key key_TABLE_SHARE_LOCK_share; @@ -950,6 +954,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_global_index_stats, "LOCK_global_index_stats", PSI_FLAG_GLOBAL}, { &key_LOCK_wakeup_ready, "THD::LOCK_wakeup_ready", 0}, { &key_LOCK_wait_commit, "wait_for_commit::LOCK_wait_commit", 0}, + { &key_LOCK_gtid_waiting, "gtid_waiting::LOCK_gtid_waiting", 0}, { &key_LOCK_thd_data, "THD::LOCK_thd_data", 0}, { &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL}, { &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL}, @@ -1036,8 +1041,11 @@ PSI_cond_key key_RELAYLOG_update_cond, 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; -PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_pool, - key_COND_parallel_entry, key_COND_prepare_ordered; +PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread, + key_COND_rpl_thread_pool, + key_COND_parallel_entry, key_COND_group_commit_orderer, + key_COND_prepare_ordered; +PSI_cond_key key_COND_wait_gtid; static PSI_cond_info all_server_conds[]= { @@ -1089,9 +1097,12 @@ static PSI_cond_info all_server_conds[]= #endif { &key_COND_flush_thread_cache, "COND_flush_thread_cache", PSI_FLAG_GLOBAL}, { &key_COND_rpl_thread, "COND_rpl_thread", 0}, + { &key_COND_rpl_thread_queue, "COND_rpl_thread_queue", 0}, { &key_COND_rpl_thread_pool, "COND_rpl_thread_pool", 0}, { &key_COND_parallel_entry, "COND_parallel_entry", 0}, - { &key_COND_prepare_ordered, "COND_prepare_ordered", 0} + { &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0}, + { &key_COND_prepare_ordered, "COND_prepare_ordered", 0}, + { &key_COND_wait_gtid, "COND_wait_gtid", 0} }; PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, @@ -2060,6 +2071,7 @@ static void mysqld_exit(int exit_code) but if a kill -15 signal was sent, the signal thread did spawn the kill_server_thread thread, which is running concurrently. */ + rpl_deinit_gtid_waiting(); rpl_deinit_gtid_slave_state(); wait_for_signal_thread_to_end(); mysql_audit_finalize(); @@ -2085,7 +2097,7 @@ void clean_up(bool print_message) // We must call end_slave() as clean_up may have been called during startup end_slave(); if (use_slave_mask) - bitmap_free(&slave_error_mask); + my_bitmap_free(&slave_error_mask); #endif stop_handle_manager(); release_ddl_log(); @@ -2139,7 +2151,7 @@ void clean_up(bool print_message) if (defaults_argv) free_defaults(defaults_argv); free_tmpdir(&mysql_tmpdir_list); - bitmap_free(&temp_pool); + my_bitmap_free(&temp_pool); free_max_user_conn(); free_global_user_stats(); free_global_client_stats(); @@ -3536,7 +3548,6 @@ void my_message_sql(uint error, const char *str, myf MyFlags) DBUG_ASSERT(str != NULL); DBUG_ASSERT(error != 0); - mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_ERROR, error, str); if (MyFlags & ME_JUST_INFO) { level= Sql_condition::WARN_LEVEL_NOTE; @@ -3559,6 +3570,8 @@ void my_message_sql(uint error, const char *str, myf MyFlags) thd->is_fatal_error= 1; (void) thd->raise_condition(error, NULL, level, str); } + else + mysql_audit_general(0, MYSQL_AUDIT_GENERAL_ERROR, error, str); /* When simulating OOM, skip writing to error log to avoid mtr errors */ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_VOID_RETURN;); @@ -4407,7 +4420,7 @@ static int init_common_variables() #endif /* defined(ENABLED_DEBUG_SYNC) */ #if (ENABLE_TEMP_POOL) - if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1)) + if (use_temp_pool && my_bitmap_init(&temp_pool,0,1024,1)) return 1; #else use_temp_pool= 0; @@ -4553,6 +4566,7 @@ static int init_thread_environment() #ifdef HAVE_REPLICATION rpl_init_gtid_slave_state(); + rpl_init_gtid_waiting(); #endif #ifdef WITH_WSREP @@ -6323,7 +6337,7 @@ default_service_handling(char **argv, /* We have to quote filename if it contains spaces */ pos= add_quoted_string(path_and_service, file_path, end); - if (*extra_opt) + if (extra_opt && *extra_opt) { /* Add option after file_path. There will be zero or one extra option. It's @@ -8742,7 +8756,6 @@ static int option_cmp(my_option *a, my_option *b) return 1; } } - DBUG_ASSERT(a->name == b->name); return 0; } @@ -8757,9 +8770,16 @@ static void print_help() sys_var_add_options(&all_options, sys_var::PARSE_EARLY); add_plugin_options(&all_options, &mem_root); sort_dynamic(&all_options, (qsort_cmp) option_cmp); + sort_dynamic(&all_options, (qsort_cmp) option_cmp); add_terminator(&all_options); my_print_help((my_option*) all_options.buffer); + + /* Add variables that can be shown but not changed, like version numbers */ + pop_dynamic(&all_options); + sys_var_add_options(&all_options, sys_var::SHOW_VALUE_IN_HELP); + sort_dynamic(&all_options, (qsort_cmp) option_cmp); + add_terminator(&all_options); my_print_variables((my_option*) all_options.buffer); free_root(&mem_root, MYF(0)); @@ -8901,7 +8921,7 @@ static int mysql_init_variables(void) my_atomic_rwlock_init(&thread_running_lock); my_atomic_rwlock_init(&thread_count_lock); my_atomic_rwlock_init(&statistics_lock); - my_atomic_rwlock_init(slave_executed_entries_lock); + my_atomic_rwlock_init(&slave_executed_entries_lock); strmov(server_version, MYSQL_SERVER_VERSION); threads.empty(); thread_cache.empty(); @@ -9125,7 +9145,7 @@ mysqld_get_one_option(int optid, opt_myisam_log=1; break; case (int) OPT_BIN_LOG: - opt_bin_log= test(argument != disabled_my_option); + opt_bin_log= MY_TEST(argument != disabled_my_option); opt_bin_log_used= 1; break; case (int) OPT_LOG_BASENAME: @@ -9639,7 +9659,7 @@ static int get_options(int *argc_ptr, char ***argv_ptr) Set some global variables from the global_system_variables In most cases the global variables will not be used */ - my_disable_locking= myisam_single_user= test(opt_external_locking == 0); + my_disable_locking= myisam_single_user= MY_TEST(opt_external_locking == 0); my_default_record_cache_size=global_system_variables.read_buff_size; /* @@ -9696,8 +9716,8 @@ static int get_options(int *argc_ptr, char ***argv_ptr) #endif global_system_variables.engine_condition_pushdown= - test(global_system_variables.optimizer_switch & - OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN); + MY_TEST(global_system_variables.optimizer_switch & + OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN); opt_readonly= read_only; @@ -9732,6 +9752,13 @@ static int get_options(int *argc_ptr, char ***argv_ptr) max_binlog_size_var->option.def_value; } } + + /* Ensure that some variables are not set higher than needed */ + if (back_log > max_connections) + back_log= max_connections; + if (thread_cache_size > max_connections) + thread_cache_size= max_connections; + return 0; } @@ -10207,6 +10234,10 @@ PSI_stage_info stage_binlog_waiting_background_tasks= { 0, "Waiting for backgrou PSI_stage_info stage_binlog_processing_checkpoint_notify= { 0, "Processing binlog checkpoint notification", 0}; PSI_stage_info stage_binlog_stopping_background_thread= { 0, "Stopping binlog background thread", 0}; PSI_stage_info stage_waiting_for_work_from_sql_thread= { 0, "Waiting for work from SQL thread", 0}; +PSI_stage_info stage_waiting_for_prior_transaction_to_commit= { 0, "Waiting for prior transaction to start commit before starting next transaction", 0}; +PSI_stage_info stage_waiting_for_room_in_worker_thread= { 0, "Waiting for room in worker thread event queue", 0}; +PSI_stage_info stage_master_gtid_wait_primary= { 0, "Waiting in MASTER_GTID_WAIT() (primary waiter)", 0}; +PSI_stage_info stage_master_gtid_wait= { 0, "Waiting in MASTER_GTID_WAIT()", 0}; #ifdef HAVE_PSI_INTERFACE @@ -10214,6 +10245,12 @@ PSI_stage_info *all_server_stages[]= { & stage_after_create, & stage_allocating_local_table, + & stage_alter_inplace, + & stage_alter_inplace_commit, + & stage_alter_inplace_prepare, + & stage_binlog_processing_checkpoint_notify, + & stage_binlog_stopping_background_thread, + & stage_binlog_waiting_background_tasks, & stage_changing_master, & stage_checking_master_version, & stage_checking_permissions, @@ -10223,9 +10260,9 @@ PSI_stage_info *all_server_stages[]= & stage_closing_tables, & stage_connecting_to_master, & stage_converting_heap_to_myisam, + & stage_copy_to_tmp_table, & stage_copying_to_group_table, & stage_copying_to_tmp_table, - & stage_copy_to_tmp_table, & stage_creating_delayed_handler, & stage_creating_sort_index, & stage_creating_table, @@ -10274,8 +10311,13 @@ PSI_stage_info *all_server_stages[]= & stage_sending_cached_result_to_client, & stage_sending_data, & stage_setup, - & stage_slave_has_read_all_relay_log, & stage_show_explain, + & stage_slave_has_read_all_relay_log, + & stage_slave_waiting_event_from_coordinator, + & stage_slave_waiting_worker_queue, + & stage_slave_waiting_worker_to_free_events, + & stage_slave_waiting_worker_to_release_partition, + & stage_slave_waiting_workers_to_exit, & stage_sorting, & stage_sorting_for_group, & stage_sorting_for_order, @@ -10294,20 +10336,27 @@ PSI_stage_info *all_server_stages[]= & stage_user_sleep, & stage_verifying_table, & stage_waiting_for_delay_list, + & stage_waiting_for_gtid_to_be_written_to_binary_log, & stage_waiting_for_handler_insert, & stage_waiting_for_handler_lock, & stage_waiting_for_handler_open, & stage_waiting_for_insert, & stage_waiting_for_master_to_send_event, & stage_waiting_for_master_update, + & stage_waiting_for_prior_transaction_to_commit, + & stage_waiting_for_query_cache_lock, + & stage_waiting_for_relay_log_space, + & stage_waiting_for_room_in_worker_thread, & stage_waiting_for_slave_mutex_on_exit, & stage_waiting_for_slave_thread_to_start, & stage_waiting_for_table_flush, - & stage_waiting_for_query_cache_lock, & stage_waiting_for_the_next_event_in_relay_log, & stage_waiting_for_the_slave_thread_to_advance_position, + & stage_waiting_for_work_from_sql_thread, & stage_waiting_to_finalize_termination, - & stage_waiting_to_get_readlock + & stage_waiting_to_get_readlock, + & stage_master_gtid_wait_primary, + & stage_master_gtid_wait }; PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection; diff --git a/sql/mysqld.h b/sql/mysqld.h index 3cd810f7040..c443d4eed62 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 2013, 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 @@ -100,7 +100,7 @@ extern uint connection_count; extern my_bool opt_safe_user_create; 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; +extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options; extern ulong slave_retried_transactions; extern ulonglong slave_type_conversions_options; extern my_bool read_only, opt_readonly; @@ -184,6 +184,7 @@ extern ulong opt_binlog_rows_event_max_size; extern ulong rpl_recovery_rank, thread_cache_size; extern ulong stored_program_cache_size; extern ulong opt_slave_parallel_threads; +extern ulong opt_slave_domain_parallel_threads; extern ulong opt_slave_parallel_max_queued; extern ulong opt_binlog_commit_wait_count; extern ulong opt_binlog_commit_wait_usec; @@ -276,6 +277,7 @@ extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit; +extern PSI_mutex_key key_LOCK_gtid_waiting; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, @@ -303,8 +305,10 @@ extern PSI_cond_key key_RELAYLOG_update_cond, 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; -extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_pool, - key_COND_parallel_entry; +extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue, + key_COND_rpl_thread_pool, + key_COND_parallel_entry, key_COND_group_commit_orderer; +extern PSI_cond_key key_COND_wait_gtid; extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, key_thread_handle_manager, key_thread_kill_server, key_thread_main, @@ -441,6 +445,11 @@ extern PSI_stage_info stage_binlog_waiting_background_tasks; extern PSI_stage_info stage_binlog_processing_checkpoint_notify; extern PSI_stage_info stage_binlog_stopping_background_thread; extern PSI_stage_info stage_waiting_for_work_from_sql_thread; +extern PSI_stage_info stage_waiting_for_prior_transaction_to_commit; +extern PSI_stage_info stage_waiting_for_room_in_worker_thread; +extern PSI_stage_info stage_master_gtid_wait_primary; +extern PSI_stage_info stage_master_gtid_wait; + #ifdef HAVE_PSI_STATEMENT_INTERFACE /** Statement instrumentation keys (sql). diff --git a/sql/net_serv.cc b/sql/net_serv.cc index fcb08bfbfc9..546542fa207 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2010, 2012, Monty Program Ab. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2014, SkySQL 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 @@ -144,7 +144,7 @@ my_bool my_net_init(NET *net, Vio* vio, uint my_flags) net->net_skip_rest_factor= 0; net->last_errno=0; net->unused= 0; - net->thread_specific_malloc= test(my_flags & MY_THREAD_SPECIFIC); + net->thread_specific_malloc= MY_TEST(my_flags & MY_THREAD_SPECIFIC); #ifdef MYSQL_SERVER net->extension= NULL; #endif @@ -267,7 +267,7 @@ static int net_data_is_ready(my_socket sd) if ((res= select((int) (sd + 1), &sfds, NULL, NULL, &tv)) < 0) return 0; else - return test(res ? FD_ISSET(sd, &sfds) : 0); + return MY_TEST(res ? FD_ISSET(sd, &sfds) : 0); #endif /* HAVE_POLL */ } @@ -360,8 +360,8 @@ my_bool net_flush(NET *net) DBUG_ENTER("net_flush"); if (net->buff != net->write_pos) { - error=test(net_real_write(net, net->buff, - (size_t) (net->write_pos - net->buff))); + error= MY_TEST(net_real_write(net, net->buff, + (size_t) (net->write_pos - net->buff))); net->write_pos= net->buff; } /* Sync packet number if using compression */ @@ -424,7 +424,7 @@ my_bool my_net_write(NET *net, const uchar *packet, size_t len) #ifndef DEBUG_DATA_PACKETS DBUG_DUMP("packet_header", buff, NET_HEADER_SIZE); #endif - rc= test(net_write_buff(net,packet,len)); + rc= MY_TEST(net_write_buff(net, packet, len)); MYSQL_NET_WRITE_DONE(rc); return rc; } @@ -498,9 +498,9 @@ net_write_command(NET *net,uchar command, } int3store(buff,length); buff[3]= (uchar) net->pkt_nr++; - rc= test(net_write_buff(net, buff, header_size) || - (head_len && net_write_buff(net, header, head_len)) || - net_write_buff(net, packet, len) || net_flush(net)); + rc= MY_TEST(net_write_buff(net, buff, header_size) || + (head_len && net_write_buff(net, header, head_len)) || + net_write_buff(net, packet, len) || net_flush(net)); MYSQL_NET_WRITE_DONE(rc); DBUG_RETURN(rc); } diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index fb55aea1968..be33e46bf94 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -205,7 +205,7 @@ Item *make_cond_for_index(Item *cond, TABLE *table, uint keyno, new_cond->argument_list()->push_back(fix); used_tables|= fix->used_tables(); } - if (test(item->marker == ICP_COND_USES_INDEX_ONLY)) + if (MY_TEST(item->marker == ICP_COND_USES_INDEX_ONLY)) { n_marked++; item->marker= 0; @@ -238,7 +238,7 @@ Item *make_cond_for_index(Item *cond, TABLE *table, uint keyno, if (!fix) return (COND*) 0; new_cond->argument_list()->push_back(fix); - if (test(item->marker == ICP_COND_USES_INDEX_ONLY)) + if (MY_TEST(item->marker == ICP_COND_USES_INDEX_ONLY)) { n_marked++; item->marker= 0; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 6982db898fb..b08ecc399bf 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -408,7 +408,7 @@ public: new_max=arg->max_value; flag_max=arg->max_flag; } return new SEL_ARG(field, part, new_min, new_max, flag_min, flag_max, - test(maybe_flag && arg->maybe_flag)); + MY_TEST(maybe_flag && arg->maybe_flag)); } SEL_ARG *clone_first(SEL_ARG *arg) { // min <= X < arg->min @@ -1823,7 +1823,7 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, *create_error= 1; } else - bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE); + my_bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE); DBUG_VOID_RETURN; } @@ -2847,7 +2847,7 @@ static int fill_used_fields_bitmap(PARAM *param) param->fields_bitmap_size= table->s->column_bitmap_size; if (!(tmp= (my_bitmap_map*) alloc_root(param->mem_root, param->fields_bitmap_size)) || - bitmap_init(¶m->needed_fields, tmp, table->s->fields, FALSE)) + my_bitmap_init(¶m->needed_fields, tmp, table->s->fields, FALSE)) return 1; bitmap_copy(¶m->needed_fields, table->read_set); @@ -3223,7 +3223,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, Assume that if the user is using 'limit' we will only need to scan limit rows if we are using a key */ - DBUG_RETURN(records ? test(quick) : -1); + DBUG_RETURN(records ? MY_TEST(quick) : -1); } /**************************************************************************** @@ -3460,6 +3460,11 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond) table->reginfo.impossible_range= 1; goto free_alloc; } + else if (tree->type == SEL_TREE::ALWAYS) + { + rows= table_records; + goto free_alloc; + } else if (tree->type == SEL_TREE::MAYBE) { rows= table_records; @@ -3782,8 +3787,8 @@ typedef struct st_part_prune_param int last_subpart_partno; /* Same as above for supartitioning */ /* - is_part_keypart[i] == test(keypart #i in partitioning index is a member - used in partitioning) + is_part_keypart[i] == MY_TEST(keypart #i in partitioning index is a member + used in partitioning) Used to maintain current values of cur_part_fields and cur_subpart_fields */ my_bool *is_part_keypart; @@ -3974,7 +3979,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) res == 1 => some used partitions => retval=FALSE res == -1 - we jump over this line to all_used: */ - retval= test(!res); + retval= MY_TEST(!res); goto end; all_used: @@ -4104,7 +4109,7 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar, */ return find_used_partitions_imerge(ppar, merges.head()); } - bitmap_init(&all_merges, bitmap_buf, n_bits, FALSE); + my_bitmap_init(&all_merges, bitmap_buf, n_bits, FALSE); bitmap_set_prefix(&all_merges, n_bits); List_iterator<SEL_IMERGE> it(merges); @@ -4607,7 +4612,7 @@ process_next_key_part: ppar->mark_full_partition_used(ppar->part_info, part_id); found= TRUE; } - res= test(found); + res= MY_TEST(found); } /* Restore the "used partitions iterator" to the default setting that @@ -4751,7 +4756,7 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) uint32 bufsize= bitmap_buffer_size(ppar->part_info->num_subparts); if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize))) return TRUE; - bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts, + my_bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts, FALSE); } range_par->key_parts= key_part; @@ -5511,7 +5516,7 @@ bool create_fields_bitmap(PARAM *param, MY_BITMAP *fields_bitmap) if (!(bitmap_buf= (my_bitmap_map *) alloc_root(param->mem_root, param->fields_bitmap_size))) return TRUE; - if (bitmap_init(fields_bitmap, bitmap_buf, param->table->s->fields, FALSE)) + if (my_bitmap_init(fields_bitmap, bitmap_buf, param->table->s->fields, FALSE)) return TRUE; return FALSE; @@ -5649,7 +5654,7 @@ bool prepare_search_best_index_intersect(PARAM *param, } } - i= n_index_scans - test(cpk_scan != NULL) + 1; + i= n_index_scans - MY_TEST(cpk_scan != NULL) + 1; if (!(common->search_scans = (INDEX_SCAN_INFO **) alloc_root (param->mem_root, @@ -5719,7 +5724,7 @@ bool prepare_search_best_index_intersect(PARAM *param, if (!(common->best_intersect= (INDEX_SCAN_INFO **) alloc_root (param->mem_root, sizeof(INDEX_SCAN_INFO *) * - (i + test(cpk_scan != NULL))))) + (i + MY_TEST(cpk_scan != NULL))))) return TRUE; size_t calc_cost_buff_size= @@ -6329,7 +6334,7 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) param->fields_bitmap_size))) DBUG_RETURN(NULL); - if (bitmap_init(&ror_scan->covered_fields, bitmap_buf, + if (my_bitmap_init(&ror_scan->covered_fields, bitmap_buf, param->table->s->fields, FALSE)) DBUG_RETURN(NULL); bitmap_clear_all(&ror_scan->covered_fields); @@ -6447,7 +6452,7 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param) if (!(buf= (my_bitmap_map*) alloc_root(param->mem_root, param->fields_bitmap_size))) return NULL; - if (bitmap_init(&info->covered_fields, buf, param->table->s->fields, + if (my_bitmap_init(&info->covered_fields, buf, param->table->s->fields, FALSE)) return NULL; info->is_covering= FALSE; @@ -6571,8 +6576,8 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info, SEL_ARG *sel_arg, *tuple_arg= NULL; key_part_map keypart_map= 0; bool cur_covered; - bool prev_covered= test(bitmap_is_set(&info->covered_fields, - key_part->fieldnr-1)); + bool prev_covered= MY_TEST(bitmap_is_set(&info->covered_fields, + key_part->fieldnr - 1)); key_range min_range; key_range max_range; min_range.key= key_val; @@ -6586,8 +6591,8 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info, sel_arg= sel_arg->next_key_part) { DBUG_PRINT("info",("sel_arg step")); - cur_covered= test(bitmap_is_set(&info->covered_fields, - key_part[sel_arg->part].fieldnr-1)); + cur_covered= MY_TEST(bitmap_is_set(&info->covered_fields, + key_part[sel_arg->part].fieldnr - 1)); if (cur_covered != prev_covered) { /* create (part1val, ..., part{n-1}val) tuple. */ @@ -7024,7 +7029,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, covered_fields->bitmap= (my_bitmap_map*)alloc_root(param->mem_root, param->fields_bitmap_size); if (!covered_fields->bitmap || - bitmap_init(covered_fields, covered_fields->bitmap, + my_bitmap_init(covered_fields, covered_fields->bitmap, param->table->s->fields, FALSE)) DBUG_RETURN(0); bitmap_clear_all(covered_fields); @@ -7684,7 +7689,8 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, param PARAM from SQL_SELECT::test_quick_select cond_func item for the predicate field_item field in the predicate - value constant in the predicate + value constant in the predicate (or a field already read from + a table in the case of dynamic range access) (for BETWEEN it contains the number of the field argument, for IN it's always 0) inv TRUE <> NOT cond_func is considered @@ -7953,24 +7959,43 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond) DBUG_RETURN(ftree); } default: + + DBUG_ASSERT (!ftree); if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM) { field_item= (Item_field*) (cond_func->arguments()[0]->real_item()); - value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0; + value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : NULL; + if (value && value->is_expensive()) + DBUG_RETURN(0); + if (!cond_func->arguments()[0]->real_item()->const_item()) + ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv); } - else if (cond_func->have_rev_func() && - cond_func->arguments()[1]->real_item()->type() == - Item::FIELD_ITEM) + /* + Even if get_full_func_mm_tree() was executed above and did not + return a range predicate it may still be possible to create one + by reversing the order of the operands. Note that this only + applies to predicates where both operands are fields. Example: A + query of the form + + WHERE t1.a OP t2.b + + In this case, arguments()[0] == t1.a and arguments()[1] == t2.b. + When creating range predicates for t2, get_full_func_mm_tree() + above will return NULL because 'field' belongs to t1 and only + predicates that applies to t2 are of interest. In this case a + call to get_full_func_mm_tree() with reversed operands (see + below) may succeed. + */ + if (!ftree && cond_func->have_rev_func() && + cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM) { field_item= (Item_field*) (cond_func->arguments()[1]->real_item()); value= cond_func->arguments()[0]; + if (value && value->is_expensive()) + DBUG_RETURN(0); + if (!cond_func->arguments()[1]->real_item()->const_item()) + ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv); } - else - DBUG_RETURN(0); - if (value && value->is_expensive()) - DBUG_RETURN(0); - - ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv); } DBUG_RETURN(ftree); @@ -10606,8 +10631,13 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts) if (param->table->field[fieldnr]->key_length() != kp->length) return FALSE; } - - if (key_part == key_part_end) + + /* + If there are equalities for all key parts, it is a ROR scan. If there are + equalities all keyparts and even some of key parts from "Extended Key" + index suffix, it is a ROR-scan, too. + */ + if (key_part >= key_part_end) return TRUE; key_part= table_key->key_part + nparts; @@ -10663,12 +10693,12 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, uint mrr_flags, if (param->table->key_info[param->real_keynr[idx]].flags & HA_SPATIAL) quick=new QUICK_RANGE_SELECT_GEOM(param->thd, param->table, param->real_keynr[idx], - test(parent_alloc), + MY_TEST(parent_alloc), parent_alloc, &create_err); else quick=new QUICK_RANGE_SELECT(param->thd, param->table, param->real_keynr[idx], - test(parent_alloc), NULL, &create_err); + MY_TEST(parent_alloc), NULL, &create_err); if (quick) { @@ -10778,15 +10808,16 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, { KEY *table_key=quick->head->key_info+quick->index; flag=EQ_RANGE; - if ((table_key->flags & HA_NOSAME) && key->part == table_key->user_defined_key_parts-1) + if ((table_key->flags & HA_NOSAME) && + key_tree->part == table_key->user_defined_key_parts-1) { - if (!(table_key->flags & HA_NULL_PART_KEY) || - !null_part_in_key(key, - param->min_key, - (uint) (tmp_min_key - param->min_key))) - flag|= UNIQUE_RANGE; - else - flag|= NULL_RANGE; + if ((table_key->flags & HA_NULL_PART_KEY) && + null_part_in_key(key, + param->min_key, + (uint) (tmp_min_key - param->min_key))) + flag|= NULL_RANGE; + else + flag|= UNIQUE_RANGE; } } } @@ -10816,7 +10847,7 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, } /* - Return 1 if there is only one range and this uses the whole primary key + Return 1 if there is only one range and this uses the whole unique key */ bool QUICK_RANGE_SELECT::unique_key_range() @@ -11662,7 +11693,7 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, result= file->read_range_first(last_range->min_keypart_map ? &start_key : 0, last_range->max_keypart_map ? &end_key : 0, - test(last_range->flag & EQ_RANGE), + MY_TEST(last_range->flag & EQ_RANGE), TRUE); if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE)) last_range= 0; // Stop searching diff --git a/sql/opt_range.h b/sql/opt_range.h index 4f2ab6df60d..1ca245ea420 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -1004,7 +1004,7 @@ class SQL_SELECT :public Sql_alloc { */ inline int skip_record(THD *thd) { - int rc= test(!cond || cond->val_int()); + int rc= MY_TEST(!cond || cond->val_int()); if (thd->is_error()) rc= -1; return rc; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a9bff4b37bf..e00083c4b8b 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1082,7 +1082,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) if (convert_join_subqueries_to_semijoins(child_join)) DBUG_RETURN(TRUE); in_subq->sj_convert_priority= - test(in_subq->emb_on_expr_nest != NO_JOIN_NEST) * MAX_TABLES * 2 + + MY_TEST(in_subq->emb_on_expr_nest != NO_JOIN_NEST) * MAX_TABLES * 2 + in_subq->is_correlated * MAX_TABLES + child_join->outer_tables; } @@ -1529,7 +1529,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) for (tl= (TABLE_LIST*)(parent_lex->table_list.first); tl->next_local; tl= tl->next_local) {} - tl->next_local= subq_lex->leaf_tables.head(); + tl->next_local= subq_lex->join->tables_list; /* A theory: no need to re-connect the next_global chain */ @@ -2384,7 +2384,7 @@ bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables) if (!is_excluded_key) { keyinfo= table->key_info + key; - is_excluded_key= !test(keyinfo->flags & HA_NOSAME); + is_excluded_key= !MY_TEST(keyinfo->flags & HA_NOSAME); } if (!is_excluded_key) { @@ -2471,8 +2471,8 @@ bool is_multiple_semi_joins(JOIN *join, POSITION *prefix, uint idx, table_map in if ((emb_sj_nest= prefix[i].table->emb_sj_nest)) { if (inner_tables & emb_sj_nest->sj_inner_tables) - return !test(inner_tables == (emb_sj_nest->sj_inner_tables & - ~join->const_table_map)); + return !MY_TEST(inner_tables == (emb_sj_nest->sj_inner_tables & + ~join->const_table_map)); } } return FALSE; @@ -3206,9 +3206,9 @@ at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, if (join->positions[idx - i].table->emb_sj_nest != tab->emb_sj_nest) return NULL; } - *loose_scan= test(remaining_tables & ~tab->table->map & - (emb_sj_nest->sj_inner_tables | - emb_sj_nest->nested_join->sj_depends_on)); + *loose_scan= MY_TEST(remaining_tables & ~tab->table->map & + (emb_sj_nest->sj_inner_tables | + emb_sj_nest->nested_join->sj_depends_on)); if (*loose_scan && !emb_sj_nest->sj_subq_pred->sjm_scan_allowed) return NULL; else @@ -3594,12 +3594,12 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) for (i= 0; i < tmp_key_parts; i++, cur_key_part++, ref_key++) { tab_ref->items[i]= emb_sj_nest->sj_subq_pred->left_expr->element_index(i); - int null_count= test(cur_key_part->field->real_maybe_null()); + int null_count= MY_TEST(cur_key_part->field->real_maybe_null()); *ref_key= new store_key_item(thd, cur_key_part->field, /* TODO: the NULL byte is taken into account in cur_key_part->store_length, so instead of - cur_ref_buff + test(maybe_null), we could + cur_ref_buff + MY_TEST(maybe_null), we could use that information instead. */ cur_ref_buff + null_count, @@ -3828,7 +3828,7 @@ static bool is_cond_sj_in_equality(Item *item) ((Item_func*)item)->functype()== Item_func::EQ_FUNC) { Item_func_eq *item_eq= (Item_func_eq*)item; - return test(item_eq->in_equality_no != UINT_MAX); + return MY_TEST(item_eq->in_equality_no != UINT_MAX); } return FALSE; } @@ -4100,7 +4100,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) { DBUG_PRINT("info",("Creating group key in temporary table")); share->keys=1; - share->uniques= test(using_unique_constraint); + share->uniques= MY_TEST(using_unique_constraint); table->key_info=keyinfo; keyinfo->key_part=key_part_info; keyinfo->flags=HA_NOSAME; @@ -5288,6 +5288,7 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, if (!(*join_where)->fixed) (*join_where)->fix_fields(join->thd, join_where); } + table->table->maybe_null= MY_TEST(join->mixed_implicit_grouping); } if ((nested_join= table->nested_join)) @@ -5318,9 +5319,9 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, through Item::cleanup() calls). */ -void cleanup_empty_jtbm_semi_joins(JOIN *join) +void cleanup_empty_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list) { - List_iterator<TABLE_LIST> li(*join->join_list); + List_iterator<TABLE_LIST> li(*join_list); TABLE_LIST *table; while ((table= li++)) { @@ -5332,6 +5333,10 @@ void cleanup_empty_jtbm_semi_joins(JOIN *join) table->table= NULL; } } + else if (table->nested_join && table->sj_subq_pred) + { + cleanup_empty_jtbm_semi_joins(join, &table->nested_join->join_list); + } } } diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 6ed077dcfcb..acfbebed6b3 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -28,7 +28,7 @@ int pull_out_semijoin_tables(JOIN *join); bool optimize_semijoin_nests(JOIN *join, table_map all_table_map); bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where); -void cleanup_empty_jtbm_semi_joins(JOIN *join); +void cleanup_empty_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list); // used by Loose_scan_opt ulonglong get_bound_sj_equalities(TABLE_LIST *sj_nest, @@ -168,7 +168,7 @@ public: } } - bool have_a_case() { return test(handled_sj_equalities); } + bool have_a_case() { return MY_TEST(handled_sj_equalities); } void check_ref_access_part1(JOIN_TAB *s, uint key, KEYUSE *start_key, table_map found_part) diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index d4fc458c948..4e8fcefa6d2 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -295,9 +295,9 @@ int opt_sum_query(THD *thd, if (!(tl->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) || tl->schema_table) { - maybe_exact_count&= test(!tl->schema_table && - (tl->table->file->ha_table_flags() & - HA_HAS_RECORDS)); + maybe_exact_count&= MY_TEST(!tl->schema_table && + (tl->table->file->ha_table_flags() & + HA_HAS_RECORDS)); is_exact_count= FALSE; count= 1; // ensure count != 0 } @@ -361,7 +361,7 @@ int opt_sum_query(THD *thd, case Item_sum::MIN_FUNC: case Item_sum::MAX_FUNC: { - int is_max= test(item_sum->sum_func() == Item_sum::MAX_FUNC); + int is_max= MY_TEST(item_sum->sum_func() == Item_sum::MAX_FUNC); /* If MIN/MAX(expr) is the first part of a key or if all previous parts of the key is found in the COND, then we can use @@ -658,7 +658,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, DBUG_RETURN(FALSE); } if (!(cond->used_tables() & field->table->map) && - test(cond->used_tables() & ~PSEUDO_TABLE_BITS)) + MY_TEST(cond->used_tables() & ~PSEUDO_TABLE_BITS)) { /* Condition doesn't restrict the used table */ DBUG_RETURN(!cond->const_item()); @@ -811,7 +811,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, Item *value= args[between && max_fl ? 2 : 1]; value->save_in_field_no_warnings(part->field, 1); if (part->null_bit) - *key_ptr++= (uchar) test(part->field->is_null()); + *key_ptr++= (uchar) MY_TEST(part->field->is_null()); part->field->get_key_image(key_ptr, part->length, Field::itRAW); } if (is_field_part) @@ -831,7 +831,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, else if (eq_type) { if ((!is_null && !cond->val_int()) || - (is_null && !test(part->field->is_null()))) + (is_null && !MY_TEST(part->field->is_null()))) DBUG_RETURN(FALSE); // Impossible test } else if (is_field_part) diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index 46bed0e60e7..2ef565517b5 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -356,7 +356,7 @@ public: bound. */ void touch() { unbound_args--; } - bool is_applicable() { return !test(unbound_args); } + bool is_applicable() { return !MY_TEST(unbound_args); } /* Iteration over values that */ typedef char *Iterator; @@ -1042,7 +1042,7 @@ bool Dep_analysis_context::setup_equality_modules_deps(List<Dep_module> void *buf; if (!(buf= current_thd->alloc(bitmap_buffer_size(offset))) || - bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset, FALSE)) + my_bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset, FALSE)) { DBUG_RETURN(TRUE); /* purecov: inspected */ } @@ -1072,7 +1072,7 @@ bool Dep_analysis_context::setup_equality_modules_deps(List<Dep_module> else { /* It's a multi-equality */ - eq_mod->unbound_args= !test(eq_mod->expr); + eq_mod->unbound_args= !MY_TEST(eq_mod->expr); List_iterator<Dep_value_field> it(*eq_mod->mult_equal_fields); Dep_value_field* field_val; while ((field_val= it++)) @@ -1398,7 +1398,7 @@ Dep_module_expr *merge_eq_mods(Dep_module_expr *start, } } - if (fv->elements + test(old->expr) > 1) + if (fv->elements + MY_TEST(old->expr) > 1) { old->mult_equal_fields= fv; old->level= and_level; diff --git a/sql/partition_element.h b/sql/partition_element.h index d0a67c6af3e..308a4d6ddd2 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -1,7 +1,7 @@ #ifndef PARTITION_ELEMENT_INCLUDED #define PARTITION_ELEMENT_INCLUDED -/* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2005, 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 diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 44407bd737f..1ae62011b43 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -385,7 +385,7 @@ bool partition_info::can_prune_insert(THD* thd, DBUG_RETURN(true); } /* Also clears all bits. */ - if (bitmap_init(used_partitions, bitmap_buf, num_partitions, false)) + if (my_bitmap_init(used_partitions, bitmap_buf, num_partitions, false)) { /* purecov: begin deadcode */ /* Cannot happen, due to pre-alloc. */ @@ -1852,7 +1852,7 @@ end: RETURN VALUES */ -void partition_info::print_no_partition_found(TABLE *table_arg) +void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag) { char buf[100]; char *buf_ptr= (char*)&buf; @@ -1866,7 +1866,7 @@ void partition_info::print_no_partition_found(TABLE *table_arg) SELECT_ACL, &table_list, TRUE)) { my_message(ER_NO_PARTITION_FOR_GIVEN_VALUE, - ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT), MYF(0)); + ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT), errflag); } else { @@ -1882,7 +1882,7 @@ void partition_info::print_no_partition_found(TABLE *table_arg) part_expr->unsigned_flag ? 10 : -10); dbug_tmp_restore_column_map(table_arg->read_set, old_map); } - my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), buf_ptr); + my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, errflag, buf_ptr); } } diff --git a/sql/partition_info.h b/sql/partition_info.h index 777cd6065eb..8ad7b1fd1fd 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -1,7 +1,7 @@ #ifndef PARTITION_INFO_INCLUDED #define PARTITION_INFO_INCLUDED -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 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 @@ -317,7 +317,7 @@ public: bool check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, bool check_partition_function); - void print_no_partition_found(TABLE *table); + void print_no_partition_found(TABLE *table, myf errflag); void print_debug(const char *str, uint*); Item* get_column_item(Item *item, Field *field); int fix_partition_values(THD *thd, diff --git a/sql/password.c b/sql/password.c index 22e0060abd2..7a3d8aafde3 100644 --- a/sql/password.c +++ b/sql/password.c @@ -510,7 +510,7 @@ check_scramble(const uchar *scramble_arg, const char *message, /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ compute_sha1_hash(hash_stage2_reassured, (const char *) buf, SHA1_HASH_SIZE); - return test(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE)); + return MY_TEST(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE)); } /* diff --git a/sql/records.cc b/sql/records.cc index e534c04935a..1b230c41156 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -189,7 +189,8 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, info->table=table; info->forms= &info->table; /* Only one table */ - if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE && + if ((table->s->tmp_table == INTERNAL_TMP_TABLE || + table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) && !table->sort.addon_field) (void) table->file->extra(HA_EXTRA_MMAP); diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index 72e7770b6ee..2b4a3093e0f 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2013, 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 diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index 3f79a0cb528..e51dee20c19 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -43,9 +43,9 @@ rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid) there will not be an attempt to delete the corresponding table row before it is even committed. */ - lock(); + mysql_mutex_lock(&LOCK_slave_state); err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no); - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); if (err) { sql_print_warning("Slave: Out of memory during slave state maintenance. " @@ -82,11 +82,20 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi) } +static void +rpl_slave_state_free_element(void *arg) +{ + struct rpl_slave_state::element *elem= (struct rpl_slave_state::element *)arg; + mysql_cond_destroy(&elem->COND_wait_gtid); + my_free(elem); +} + + rpl_slave_state::rpl_slave_state() : last_sub_id(0), inited(false), loaded(false) { my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id), - sizeof(uint32), NULL, my_free, HASH_UNIQUE); + sizeof(uint32), NULL, rpl_slave_state_free_element, HASH_UNIQUE); } @@ -146,6 +155,21 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, if (!(elem= get_element(domain_id))) return 1; + if (seq_no > elem->highest_seq_no) + elem->highest_seq_no= seq_no; + if (elem->gtid_waiter && elem->min_wait_seq_no <= seq_no) + { + /* + Someone was waiting in MASTER_GTID_WAIT() for this GTID to appear. + Signal (and remove) them. The waiter will handle all the processing + of all pending MASTER_GTID_WAIT(), so we do not slow down the + replication SQL thread. + */ + mysql_mutex_assert_owner(&LOCK_slave_state); + elem->gtid_waiter= NULL; + mysql_cond_broadcast(&elem->COND_wait_gtid); + } + if (!(list_elem= (list_element *)my_malloc(sizeof(*list_elem), MYF(MY_WME)))) return 1; list_elem->server_id= server_id; @@ -173,6 +197,9 @@ rpl_slave_state::get_element(uint32 domain_id) return NULL; elem->list= NULL; elem->domain_id= domain_id; + elem->highest_seq_no= 0; + elem->gtid_waiter= NULL; + mysql_cond_init(key_COND_wait_gtid, &elem->COND_wait_gtid, 0); if (my_hash_insert(&hash, (uchar *)elem)) { my_free(elem); @@ -352,7 +379,8 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, { DBUG_PRINT("info", ("resetting OPTION_BEGIN")); thd->variables.option_bits&= - ~(ulonglong)(OPTION_NOT_AUTOCOMMIT|OPTION_BEGIN|OPTION_BIN_LOG); + ~(ulonglong)(OPTION_NOT_AUTOCOMMIT |OPTION_BEGIN |OPTION_BIN_LOG | + OPTION_GTID_BEGIN); } else thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG; @@ -378,10 +406,10 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, goto end; } - lock(); + mysql_mutex_lock(&LOCK_slave_state); if ((elem= get_element(gtid->domain_id)) == NULL) { - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); my_error(ER_OUT_OF_RESOURCES, MYF(0)); err= 1; goto end; @@ -410,7 +438,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, cur->next= NULL; elem->list= cur; } - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); if (!elist) goto end; @@ -470,9 +498,9 @@ end: */ if (elist) { - lock(); + mysql_mutex_lock(&LOCK_slave_state); put_back_list(gtid->domain_id, elist); - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); } ha_rollback_trans(thd, FALSE); @@ -499,9 +527,9 @@ rpl_slave_state::next_sub_id(uint32 domain_id) { uint64 sub_id= 0; - lock(); + mysql_mutex_lock(&LOCK_slave_state); sub_id= ++last_sub_id; - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); return sub_id; } @@ -541,7 +569,7 @@ rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data, my_hash_insert(>id_hash, (uchar *)(&extra_gtids[i]))) goto err; - lock(); + mysql_mutex_lock(&LOCK_slave_state); for (i= 0; i < hash.records; ++i) { @@ -576,19 +604,19 @@ rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data, memcpy(&best_gtid, gtid, sizeof(best_gtid)); if (my_hash_delete(>id_hash, rec)) { - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); goto err; } } if ((res= (*cb)(&best_gtid, data))) { - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); goto err; } } - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); /* Also add any remaining extra domain_ids. */ for (i= 0; i < gtid_hash.records; ++i) @@ -659,11 +687,11 @@ rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid) list_element *list; uint64 best_sub_id; - lock(); + mysql_mutex_lock(&LOCK_slave_state); elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0); if (!elem || !(list= elem->list)) { - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); return false; } @@ -681,7 +709,7 @@ rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid) out_gtid->seq_no= list->seq_no; } - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); return true; } @@ -811,7 +839,7 @@ rpl_slave_state::is_empty() uint32 i; bool result= true; - lock(); + mysql_mutex_lock(&LOCK_slave_state); for (i= 0; i < hash.records; ++i) { element *e= (element *)my_hash_element(&hash, i); @@ -821,7 +849,7 @@ rpl_slave_state::is_empty() break; } } - unlock(); + mysql_mutex_unlock(&LOCK_slave_state); return result; } @@ -1647,3 +1675,444 @@ slave_connection_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size) return 0; } + + +/* + Execute a MASTER_GTID_WAIT(). + The position to wait for is in gtid_str in string form. + The timeout in microseconds is in timeout_us, zero means no timeout. + + Returns: + 1 for error. + 0 for wait completed. + -1 for wait timed out. +*/ +int +gtid_waiting::wait_for_pos(THD *thd, String *gtid_str, longlong timeout_us) +{ + int err; + rpl_gtid *wait_pos; + uint32 count, i; + struct timespec wait_until, *wait_until_ptr; + + /* Wait for the empty position returns immediately. */ + if (gtid_str->length() == 0) + return 0; + + if (!(wait_pos= gtid_parse_string_to_list(gtid_str->ptr(), gtid_str->length(), + &count))) + { + my_error(ER_INCORRECT_GTID_STATE, MYF(0)); + return 1; + } + + if (timeout_us >= 0) + { + set_timespec_nsec(wait_until, (ulonglong)1000*timeout_us); + wait_until_ptr= &wait_until; + } + else + wait_until_ptr= NULL; + err= 0; + for (i= 0; i < count; ++i) + { + if ((err= wait_for_gtid(thd, &wait_pos[i], wait_until_ptr))) + break; + } + my_free(wait_pos); + return err; +} + + +void +gtid_waiting::promote_new_waiter(gtid_waiting::hash_element *he) +{ + queue_element *qe; + + mysql_mutex_assert_owner(&LOCK_gtid_waiting); + if (queue_empty(&he->queue)) + return; + qe= (queue_element *)queue_top(&he->queue); + qe->do_small_wait= true; + mysql_cond_signal(&qe->thd->COND_wakeup_ready); +} + +void +gtid_waiting::process_wait_hash(uint64 wakeup_seq_no, + gtid_waiting::hash_element *he) +{ + mysql_mutex_assert_owner(&LOCK_gtid_waiting); + + for (;;) + { + queue_element *qe; + + if (queue_empty(&he->queue)) + break; + qe= (queue_element *)queue_top(&he->queue); + if (qe->wait_seq_no > wakeup_seq_no) + break; + DBUG_ASSERT(!qe->done); + queue_remove_top(&he->queue); + qe->done= true;; + mysql_cond_signal(&qe->thd->COND_wakeup_ready); + } +} + + +/* + Execute a MASTER_GTID_WAIT() for one specific domain. + + The implementation is optimised primarily for (1) minimal performance impact + on the slave replication threads, and secondarily for (2) quick performance + of MASTER_GTID_WAIT() on a single GTID, which can be useful for consistent + read to clients in an async replication read-scaleout scenario. + + To achieve (1), we have a "small" wait and a "large" wait. The small wait + contends with the replication threads on the lock on the gtid_slave_pos, so + only minimal processing is done under that lock, and only a single waiter at + a time does the small wait. + + If there is already a small waiter, a new thread will either replace the + small waiter (if it needs to wait for an earlier sequence number), or + instead do a "large" wait. + + Once awoken on the small wait, the waiting thread releases the lock shared + with the SQL threads quickly, and then processes all waiters currently doing + the large wait using a different lock that does not impact replication. + + This way, the SQL threads only need to do a single check + possibly a + pthread_cond_signal() when updating the gtid_slave_state, and the time that + non-SQL threads contend for the lock on gtid_slave_state is minimized. + + There is always at least one thread that has the responsibility to ensure + that there is a small waiter; this thread has queue_element::do_small_wait + set to true. This thread will do the small wait until it is done, at which + point it will make sure to pass on the responsibility to another thread. + Normally only one thread has do_small_wait==true, but it can occasionally + happen that there is more than one, when threads race one another for the + lock on the small wait (this results in slightly increased activity on the + small lock but is otherwise harmless). + + Returns: + 0 Wait completed normally + -1 Wait completed due to timeout + 1 An error (my_error() will have been called to set the error in the da) +*/ +int +gtid_waiting::wait_for_gtid(THD *thd, rpl_gtid *wait_gtid, + struct timespec *wait_until) +{ + bool timed_out= false; +#ifdef HAVE_REPLICATION + queue_element elem; + uint32 domain_id= wait_gtid->domain_id; + uint64 seq_no= wait_gtid->seq_no; + hash_element *he; + rpl_slave_state::element *slave_state_elem= NULL; + PSI_stage_info old_stage; + bool did_enter_cond= false; + + elem.wait_seq_no= seq_no; + elem.thd= thd; + elem.done= false; + + mysql_mutex_lock(&LOCK_gtid_waiting); + if (!(he= get_entry(wait_gtid->domain_id))) + { + mysql_mutex_unlock(&LOCK_gtid_waiting); + return 1; + } + /* + If there is already another waiter with seq_no no larger than our own, + we are sure that there is already a small waiter that will wake us up + (or later pass the small wait responsibility to us). So in this case, we + do not need to touch the small wait lock at all. + */ + elem.do_small_wait= + (queue_empty(&he->queue) || + ((queue_element *)queue_top(&he->queue))->wait_seq_no > seq_no); + + if (register_in_wait_queue(thd, wait_gtid, he, &elem)) + { + mysql_mutex_unlock(&LOCK_gtid_waiting); + return 1; + } + /* + Loop, doing either the small or large wait as appropriate, until either + the position waited for is reached, or we get a kill or timeout. + */ + for (;;) + { + mysql_mutex_assert_owner(&LOCK_gtid_waiting); + + if (elem.do_small_wait) + { + uint64 wakeup_seq_no; + queue_element *cur_waiter; + + mysql_mutex_lock(&rpl_global_gtid_slave_state.LOCK_slave_state); + /* + The elements in the gtid_slave_state_hash are never re-allocated once + they enter the hash, so we do not need to re-do the lookup after releasing + and re-aquiring the lock. + */ + if (!slave_state_elem && + !(slave_state_elem= rpl_global_gtid_slave_state.get_element(domain_id))) + { + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); + remove_from_wait_queue(he, &elem); + promote_new_waiter(he); + if (did_enter_cond) + thd->EXIT_COND(&old_stage); + else + mysql_mutex_unlock(&LOCK_gtid_waiting); + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return 1; + } + + if ((wakeup_seq_no= slave_state_elem->highest_seq_no) >= seq_no) + { + /* + We do not have to wait. (We will be removed from the wait queue when + we call process_wait_hash() below. + */ + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); + } + else if ((cur_waiter= slave_state_elem->gtid_waiter) && + slave_state_elem->min_wait_seq_no <= seq_no) + { + /* + There is already a suitable small waiter, go do the large wait. + (Normally we would not have needed to check the small wait in this + case, but it can happen if we race with another thread for the small + lock). + */ + elem.do_small_wait= false; + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); + } + else + { + /* + We have to do the small wait ourselves (stealing it from any thread + that might already be waiting for a later seq_no). + */ + slave_state_elem->gtid_waiter= &elem; + slave_state_elem->min_wait_seq_no= seq_no; + if (cur_waiter) + { + /* We stole the wait, so wake up the old waiting thread. */ + mysql_cond_signal(&slave_state_elem->COND_wait_gtid); + } + + /* Release the large lock, and do the small wait. */ + if (did_enter_cond) + { + thd->EXIT_COND(&old_stage); + did_enter_cond= false; + } + else + mysql_mutex_unlock(&LOCK_gtid_waiting); + thd->ENTER_COND(&slave_state_elem->COND_wait_gtid, + &rpl_global_gtid_slave_state.LOCK_slave_state, + &stage_master_gtid_wait_primary, &old_stage); + do + { + if (thd->check_killed()) + break; + else if (wait_until) + { + int err= + mysql_cond_timedwait(&slave_state_elem->COND_wait_gtid, + &rpl_global_gtid_slave_state.LOCK_slave_state, + wait_until); + if (err == ETIMEDOUT || err == ETIME) + { + timed_out= true; + break; + } + } + else + mysql_cond_wait(&slave_state_elem->COND_wait_gtid, + &rpl_global_gtid_slave_state.LOCK_slave_state); + } while (slave_state_elem->gtid_waiter == &elem); + wakeup_seq_no= slave_state_elem->highest_seq_no; + /* + If we aborted due to timeout or kill, remove us as waiter. + + If we were replaced by another waiter with a smaller seq_no, then we + no longer have responsibility for the small wait. + */ + if ((cur_waiter= slave_state_elem->gtid_waiter)) + { + if (cur_waiter == &elem) + slave_state_elem->gtid_waiter= NULL; + else if (slave_state_elem->min_wait_seq_no <= seq_no) + elem.do_small_wait= false; + } + thd->EXIT_COND(&old_stage); + + mysql_mutex_lock(&LOCK_gtid_waiting); + } + + /* + Note that hash_entry pointers do not change once allocated, so we do + not need to lookup `he' again after re-aquiring LOCK_gtid_waiting. + */ + process_wait_hash(wakeup_seq_no, he); + } + else + { + /* Do the large wait. */ + if (!did_enter_cond) + { + thd->ENTER_COND(&thd->COND_wakeup_ready, &LOCK_gtid_waiting, + &stage_master_gtid_wait, &old_stage); + did_enter_cond= true; + } + while (!elem.done && !thd->check_killed()) + { + thd_wait_begin(thd, THD_WAIT_BINLOG); + if (wait_until) + { + int err= mysql_cond_timedwait(&thd->COND_wakeup_ready, + &LOCK_gtid_waiting, wait_until); + if (err == ETIMEDOUT || err == ETIME) + timed_out= true; + } + else + mysql_cond_wait(&thd->COND_wakeup_ready, &LOCK_gtid_waiting); + thd_wait_end(thd); + if (elem.do_small_wait || timed_out) + break; + } + } + + if ((thd->killed || timed_out) && !elem.done) + { + /* Aborted, so remove ourselves from the hash. */ + remove_from_wait_queue(he, &elem); + elem.done= true; + } + if (elem.done) + { + /* + If our wait is done, but we have (or were passed) responsibility for + the small wait, then we need to pass on that task to someone else. + */ + if (elem.do_small_wait) + promote_new_waiter(he); + break; + } + } + + if (did_enter_cond) + thd->EXIT_COND(&old_stage); + else + mysql_mutex_unlock(&LOCK_gtid_waiting); + if (thd->killed) + thd->send_kill_message(); +#endif /* HAVE_REPLICATION */ + return timed_out ? -1 : 0; +} + + +static void +free_hash_element(void *p) +{ + gtid_waiting::hash_element *e= (gtid_waiting::hash_element *)p; + delete_queue(&e->queue); + my_free(e); +} + + +void +gtid_waiting::init() +{ + my_hash_init(&hash, &my_charset_bin, 32, + offsetof(hash_element, domain_id), sizeof(uint32), NULL, + free_hash_element, HASH_UNIQUE); + mysql_mutex_init(key_LOCK_gtid_waiting, &LOCK_gtid_waiting, 0); +} + + +void +gtid_waiting::destroy() +{ + mysql_mutex_destroy(&LOCK_gtid_waiting); + my_hash_free(&hash); +} + + +static int +cmp_queue_elem(void *, uchar *a, uchar *b) +{ + uint64 seq_no_a= *(uint64 *)a; + uint64 seq_no_b= *(uint64 *)b; + if (seq_no_a < seq_no_b) + return -1; + else if (seq_no_a == seq_no_b) + return 0; + else + return 1; +} + + +gtid_waiting::hash_element * +gtid_waiting::get_entry(uint32 domain_id) +{ + hash_element *e; + + if ((e= (hash_element *)my_hash_search(&hash, (const uchar *)&domain_id, 0))) + return e; + + if (!(e= (hash_element *)my_malloc(sizeof(*e), MYF(MY_WME)))) + { + my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*e)); + return NULL; + } + + if (init_queue(&e->queue, 8, offsetof(queue_element, wait_seq_no), 0, + cmp_queue_elem, NULL, 1+offsetof(queue_element, queue_idx), 1)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + my_free(e); + return NULL; + } + e->domain_id= domain_id; + if (my_hash_insert(&hash, (uchar *)e)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + delete_queue(&e->queue); + my_free(e); + return NULL; + } + return e; +} + + +int +gtid_waiting::register_in_wait_queue(THD *thd, rpl_gtid *wait_gtid, + gtid_waiting::hash_element *he, + gtid_waiting::queue_element *elem) +{ + mysql_mutex_assert_owner(&LOCK_gtid_waiting); + + if (queue_insert_safe(&he->queue, (uchar *)elem)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return 1; + } + + return 0; +} + + +void +gtid_waiting::remove_from_wait_queue(gtid_waiting::hash_element *he, + gtid_waiting::queue_element *elem) +{ + mysql_mutex_assert_owner(&LOCK_gtid_waiting); + + queue_remove(&he->queue, elem->queue_idx); +} diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index b0bc54900e7..54f352661a7 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -16,6 +16,10 @@ #ifndef RPL_GTID_H #define RPL_GTID_H +#include "hash.h" +#include "queues.h" + + /* Definitions for MariaDB global transaction ID (GTID). */ @@ -37,6 +41,57 @@ enum enum_gtid_skip_type { /* + Structure to keep track of threads waiting in MASTER_GTID_WAIT(). + + Since replication is (mostly) single-threaded, we want to minimise the + performance impact on that from MASTER_GTID_WAIT(). To achieve this, we + are careful to keep the common lock between replication threads and + MASTER_GTID_WAIT threads held for as short as possible. We keep only + a single thread waiting to be notified by the replication threads; this + thread then handles all the (potentially heavy) lifting of dealing with + all current waiting threads. +*/ +struct gtid_waiting { + /* Elements in the hash, basically a priority queue for each domain. */ + struct hash_element { + QUEUE queue; + uint32 domain_id; + }; + /* A priority queue to handle waiters in one domain in seq_no order. */ + struct queue_element { + uint64 wait_seq_no; + THD *thd; + int queue_idx; + /* + do_small_wait is true if we have responsibility for ensuring that there + is a small waiter. + */ + bool do_small_wait; + /* + The flag `done' is set when the wait is completed (either due to reaching + the position waited for, or due to timeout or kill). The queue_element + is in the queue if and only if `done' is true. + */ + bool done; + }; + + mysql_mutex_t LOCK_gtid_waiting; + HASH hash; + + void init(); + void destroy(); + hash_element *get_entry(uint32 domain_id); + int wait_for_pos(THD *thd, String *gtid_str, longlong timeout_us); + void promote_new_waiter(gtid_waiting::hash_element *he); + int wait_for_gtid(THD *thd, rpl_gtid *wait_gtid, struct timespec *wait_until); + void process_wait_hash(uint64 wakeup_seq_no, gtid_waiting::hash_element *he); + int register_in_wait_queue(THD *thd, rpl_gtid *wait_gtid, hash_element *he, + queue_element *elem); + void remove_from_wait_queue(hash_element *he, queue_element *elem); +}; + + +/* Replication slave state. For every independent replication stream (identified by domain_id), this @@ -61,6 +116,20 @@ struct rpl_slave_state { struct list_element *list; uint32 domain_id; + /* Highest seq_no seen so far in this domain. */ + uint64 highest_seq_no; + /* + If this is non-NULL, then it is the waiter responsible for the small + wait in MASTER_GTID_WAIT(). + */ + gtid_waiting::queue_element *gtid_waiter; + /* + If gtid_waiter is non-NULL, then this is the seq_no that its + MASTER_GTID_WAIT() is waiting on. When we reach this seq_no, we need to + signal the waiter on COND_wait_gtid. + */ + uint64 min_wait_seq_no; + mysql_cond_t COND_wait_gtid; list_element *grab_list() { list_element *l= list; list= NULL; return l; } void add(list_element *l) @@ -99,9 +168,6 @@ struct rpl_slave_state bool in_statement); bool is_empty(); - void lock() { DBUG_ASSERT(inited); mysql_mutex_lock(&LOCK_slave_state); } - void unlock() { DBUG_ASSERT(inited); mysql_mutex_unlock(&LOCK_slave_state); } - element *get_element(uint32 domain_id); int put_back_list(uint32 domain_id, list_element *list); @@ -204,6 +270,7 @@ struct slave_connection_state int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size); }; + extern bool rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid, bool *first); extern int gtid_check_rpl_slave_state_table(TABLE *table); diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index f4790cc963a..98788955e24 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -94,13 +94,13 @@ public: injector::transaction::table tbl(share->table, true); MY_BITMAP cols; - bitmap_init(&cols, NULL, (i + 7) / 8, false); + my_bitmap_init(&cols, NULL, (i + 7) / 8, false); inj->write_row(::server_id, tbl, &cols, row_data); or MY_BITMAP cols; - bitmap_init(&cols, NULL, (i + 7) / 8, false); + my_bitmap_init(&cols, NULL, (i + 7) / 8, false); inj->write_row(::server_id, injector::transaction::table(share->table, true), &cols, row_data); diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index fced238e334..7764400becb 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -547,7 +547,7 @@ file '%s')", fname); mi->rli.is_relay_log_recovery= FALSE; // now change cache READ -> WRITE - must do this before flush_master_info reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1); - if ((error=test(flush_master_info(mi, TRUE, TRUE)))) + if ((error= MY_TEST(flush_master_info(mi, TRUE, TRUE)))) sql_print_error("Failed to flush master info file"); mysql_mutex_unlock(&mi->data_lock); DBUG_RETURN(error); diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 80125e8aa29..27f17668849 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -2,6 +2,7 @@ #include "rpl_parallel.h" #include "slave.h" #include "rpl_mi.h" +#include "debug_sync.h" /* @@ -24,7 +25,7 @@ static int rpt_handle_event(rpl_parallel_thread::queued_event *qev, struct rpl_parallel_thread *rpt) { - int err __attribute__((unused)); + int err; rpl_group_info *rgi= qev->rgi; Relay_log_info *rli= rgi->rli; THD *thd= rgi->thd; @@ -92,32 +93,12 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) } -static bool -sql_worker_killed(THD *thd, rpl_group_info *rgi, bool in_event_group) -{ - if (!rgi->rli->abort_slave && !abort_loop) - return false; - - /* - Do not abort in the middle of an event group that cannot be rolled back. - */ - if ((thd->transaction.all.modified_non_trans_table || - (thd->variables.option_bits & OPTION_KEEP_LOG)) - && in_event_group) - return false; - /* ToDo: should we add some timeout like in sql_slave_killed? - if (rgi->last_event_start_time == 0) - rgi->last_event_start_time= my_time(0); - */ - - return true; -} - - static void finish_event_group(THD *thd, int err, uint64 sub_id, - rpl_parallel_entry *entry, wait_for_commit *wfc) + rpl_parallel_entry *entry, rpl_group_info *rgi) { + wait_for_commit *wfc= &rgi->commit_orderer; + /* Remove any left-over registration to wait for a prior commit to complete. Normally, such wait would already have been removed at @@ -142,7 +123,7 @@ finish_event_group(THD *thd, int err, uint64 sub_id, if (err) wfc->unregister_wait_for_prior_commit(); else - wfc->wait_for_prior_commit(); + err= wfc->wait_for_prior_commit(thd); thd->wait_for_commit_ptr= NULL; /* @@ -162,16 +143,57 @@ finish_event_group(THD *thd, int err, uint64 sub_id, */ mysql_mutex_lock(&entry->LOCK_parallel_entry); if (entry->last_committed_sub_id < sub_id) - { entry->last_committed_sub_id= sub_id; - mysql_cond_broadcast(&entry->COND_parallel_entry); - } + + /* + If this event group got error, then any following event groups that have + not yet started should just skip their group, preparing for stop of the + SQL driver thread. + */ + if (unlikely(rgi->is_error) && + entry->stop_on_error_sub_id == (uint64)ULONGLONG_MAX) + entry->stop_on_error_sub_id= sub_id; + /* + We need to mark that this event group started its commit phase, in case we + missed it before (otherwise we would deadlock the next event group that is + waiting for this). In most cases (normal DML), it will be a no-op. + */ + rgi->mark_start_commit_no_lock(); mysql_mutex_unlock(&entry->LOCK_parallel_entry); + thd->clear_error(); + thd->get_stmt_da()->reset_diagnostics_area(); wfc->wakeup_subsequent_commits(err); } +static void +signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi) +{ + rgi->is_error= true; + rgi->cleanup_context(thd, true); + rgi->rli->abort_slave= true; + rgi->rli->stop_for_until= false; + mysql_mutex_lock(rgi->rli->relay_log.get_log_lock()); + mysql_mutex_unlock(rgi->rli->relay_log.get_log_lock()); + rgi->rli->relay_log.signal_update(); +} + + +static void +unlock_or_exit_cond(THD *thd, mysql_mutex_t *lock, bool *did_enter_cond, + PSI_stage_info *old_stage) +{ + if (*did_enter_cond) + { + thd->EXIT_COND(old_stage); + *did_enter_cond= false; + } + else + mysql_mutex_unlock(lock); +} + + pthread_handler_t handle_rpl_parallel_thread(void *arg) { @@ -180,8 +202,14 @@ handle_rpl_parallel_thread(void *arg) struct rpl_parallel_thread::queued_event *events; bool group_standalone= true; bool in_event_group= false; + bool group_skip_for_stop= false; rpl_group_info *group_rgi= NULL; + group_commit_orderer *gco, *tmp_gco; uint64 event_gtid_sub_id= 0; + rpl_parallel_thread::queued_event *qevs_to_free; + rpl_group_info *rgis_to_free; + group_commit_orderer *gcos_to_free; + size_t total_event_size; int err; struct rpl_parallel_thread *rpt= (struct rpl_parallel_thread *)arg; @@ -207,6 +235,7 @@ handle_rpl_parallel_thread(void *arg) 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; thd_proc_info(thd, "Waiting for work from main SQL threads"); thd->set_time(); thd->variables.lock_wait_timeout= LONG_TIMEOUT; @@ -220,43 +249,61 @@ handle_rpl_parallel_thread(void *arg) rpt->running= true; mysql_cond_signal(&rpt->COND_rpl_thread); - while (!rpt->stop && !thd->killed) + while (!rpt->stop) { - rpl_parallel_thread *list; - thd->ENTER_COND(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread, &stage_waiting_for_work_from_sql_thread, &old_stage); - while (!(events= rpt->event_queue) && !rpt->stop && !thd->killed && - !(rpt->current_entry && rpt->current_entry->force_abort)) + /* + There are 4 cases that should cause us to wake up: + - Events have been queued for us to handle. + - We have an owner, but no events and not inside event group -> we need + to release ourself to the thread pool + - SQL thread is stopping, and we have an owner but no events, and we are + inside an event group; no more events will be queued to us, so we need + to abort the group (force_abort==1). + - Thread pool shutdown (rpt->stop==1). + */ + while (!( (events= rpt->event_queue) || + (rpt->current_owner && !in_event_group) || + (rpt->current_owner && group_rgi->parallel_entry->force_abort) || + rpt->stop)) mysql_cond_wait(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread); - rpt->dequeue(events); + rpt->dequeue1(events); thd->EXIT_COND(&old_stage); - mysql_cond_signal(&rpt->COND_rpl_thread); more_events: + qevs_to_free= NULL; + rgis_to_free= NULL; + gcos_to_free= NULL; + total_event_size= 0; while (events) { struct rpl_parallel_thread::queued_event *next= events->next; Log_event_type event_type; rpl_group_info *rgi= events->rgi; rpl_parallel_entry *entry= rgi->parallel_entry; - uint64 wait_for_sub_id; - uint64 wait_start_sub_id; - bool end_of_group; + bool end_of_group, group_ending; + total_event_size+= events->event_size; if (!events->ev) { handle_queued_pos_update(thd, events); - my_free(events); + events->next= qevs_to_free; + qevs_to_free= events; events= next; continue; } err= 0; group_rgi= rgi; + gco= rgi->gco; /* Handle a new event group, which will be initiated by a GTID event. */ if ((event_type= events->ev->get_type_code()) == GTID_EVENT) { + bool did_enter_cond= false; + PSI_stage_info old_stage; + uint64 wait_count; + in_event_group= true; /* If the standalone flag is set, then this event group consists of a @@ -278,29 +325,87 @@ handle_rpl_parallel_thread(void *arg) occured. Also do not start parallel execution of this event group until all - prior groups have committed that are not safe to run in parallel with. + prior groups have reached the commit phase that are not safe to run + in parallel with. */ - wait_for_sub_id= rgi->wait_commit_sub_id; - wait_start_sub_id= rgi->wait_start_sub_id; - if (wait_for_sub_id || wait_start_sub_id) + mysql_mutex_lock(&entry->LOCK_parallel_entry); + if (!gco->installed) { - mysql_mutex_lock(&entry->LOCK_parallel_entry); - if (wait_start_sub_id) - { - while (wait_start_sub_id > entry->last_committed_sub_id) - mysql_cond_wait(&entry->COND_parallel_entry, - &entry->LOCK_parallel_entry); - } - rgi->wait_start_sub_id= 0; /* No need to check again. */ - if (wait_for_sub_id > entry->last_committed_sub_id) + if (gco->prev_gco) + gco->prev_gco->next_gco= gco; + gco->installed= true; + } + wait_count= gco->wait_count; + if (wait_count > entry->count_committing_event_groups) + { + DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior"); + thd->ENTER_COND(&gco->COND_group_commit_orderer, + &entry->LOCK_parallel_entry, + &stage_waiting_for_prior_transaction_to_commit, + &old_stage); + did_enter_cond= true; + do { - wait_for_commit *waitee= - &rgi->wait_commit_group_info->commit_orderer; - rgi->commit_orderer.register_wait_for_prior_commit(waitee); - } - mysql_mutex_unlock(&entry->LOCK_parallel_entry); + if (thd->check_killed() && !rgi->is_error) + { + DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior_killed"); + thd->send_kill_message(); + slave_output_error_info(rgi->rli, thd); + signal_error_to_sql_driver_thread(thd, rgi); + /* + Even though we were killed, we need to continue waiting for the + prior event groups to signal that we can continue. Otherwise we + mess up the accounting for ordering. However, now that we have + marked the error, events will just be skipped rather than + executed, and things will progress quickly towards stop. + */ + } + mysql_cond_wait(&gco->COND_group_commit_orderer, + &entry->LOCK_parallel_entry); + } while (wait_count > entry->count_committing_event_groups); + } + + if ((tmp_gco= gco->prev_gco)) + { + /* + Now all the event groups in the previous batch have entered their + commit phase, and will no longer access their gco. So we can free + it here. + */ + DBUG_ASSERT(!tmp_gco->prev_gco); + gco->prev_gco= NULL; + tmp_gco->next_gco= gcos_to_free; + gcos_to_free= tmp_gco; } + if (entry->force_abort && wait_count > entry->stop_count) + { + /* + We are stopping (STOP SLAVE), and this event group is beyond the + point where we can safely stop. So set a flag that will cause us + to skip, rather than execute, the following events. + */ + group_skip_for_stop= true; + } + else + group_skip_for_stop= false; + + if (unlikely(entry->stop_on_error_sub_id <= rgi->wait_commit_sub_id)) + group_skip_for_stop= true; + else if (rgi->wait_commit_sub_id > entry->last_committed_sub_id) + { + /* + Register that the commit of this event group must wait for the + commit of the previous event group to complete before it may + complete itself, so that we preserve commit order. + */ + wait_for_commit *waitee= + &rgi->wait_commit_group_info->commit_orderer; + rgi->commit_orderer.register_wait_for_prior_commit(waitee); + } + unlock_or_exit_cond(thd, &entry->LOCK_parallel_entry, + &did_enter_cond, &old_stage); + if(thd->wait_for_commit_ptr) { /* @@ -317,13 +422,23 @@ handle_rpl_parallel_thread(void *arg) thd->wait_for_commit_ptr= &rgi->commit_orderer; } + group_ending= event_type == XID_EVENT || + (event_type == QUERY_EVENT && + (((Query_log_event *)events->ev)->is_commit() || + ((Query_log_event *)events->ev)->is_rollback())); + if (group_ending) + { + DEBUG_SYNC(thd, "rpl_parallel_before_mark_start_commit"); + rgi->mark_start_commit(); + } + /* If the SQL thread is stopping, we just skip execution of all the following event groups. We still do all the normal waiting and wakeup processing between the event groups as a simple way to ensure that everything is stopped and cleaned up correctly. */ - if (!rgi->is_error && !sql_worker_killed(thd, rgi, in_event_group)) + if (!rgi->is_error && !group_skip_for_stop) err= rpt_handle_event(events, rpt); else err= thd->wait_for_prior_commit(); @@ -331,34 +446,55 @@ handle_rpl_parallel_thread(void *arg) end_of_group= in_event_group && ((group_standalone && !Log_event::is_part_of_group(event_type)) || - event_type == XID_EVENT || - (event_type == QUERY_EVENT && - (((Query_log_event *)events->ev)->is_commit() || - ((Query_log_event *)events->ev)->is_rollback()))); + group_ending); delete_or_keep_event_post_apply(rgi, event_type, events->ev); - my_free(events); + events->next= qevs_to_free; + qevs_to_free= events; if (err) { - rgi->is_error= true; slave_output_error_info(rgi->rli, thd); - rgi->cleanup_context(thd, true); - rgi->rli->abort_slave= true; + signal_error_to_sql_driver_thread(thd, rgi); } if (end_of_group) { in_event_group= false; - finish_event_group(thd, err, event_gtid_sub_id, entry, - &rgi->commit_orderer); - delete rgi; + finish_event_group(thd, err, event_gtid_sub_id, entry, rgi); + rgi->next= rgis_to_free; + rgis_to_free= rgi; group_rgi= rgi= NULL; + group_skip_for_stop= false; + DEBUG_SYNC(thd, "rpl_parallel_end_of_group"); } events= next; } mysql_mutex_lock(&rpt->LOCK_rpl_thread); + /* Signal that our queue can now accept more events. */ + rpt->dequeue2(total_event_size); + mysql_cond_signal(&rpt->COND_rpl_thread_queue); + /* We need to delay the free here, to when we have the lock. */ + while (gcos_to_free) + { + group_commit_orderer *next= gcos_to_free->next_gco; + rpt->free_gco(gcos_to_free); + gcos_to_free= next; + } + while (rgis_to_free) + { + rpl_group_info *next= rgis_to_free->next; + rpt->free_rgi(rgis_to_free); + rgis_to_free= next; + } + while (qevs_to_free) + { + rpl_parallel_thread::queued_event *next= qevs_to_free->next; + rpt->free_qev(qevs_to_free); + qevs_to_free= next; + } + if ((events= rpt->event_queue) != NULL) { /* @@ -366,9 +502,8 @@ handle_rpl_parallel_thread(void *arg) This is faster than having to wakeup the pool manager thread to give us a new event. */ - rpt->dequeue(events); + rpt->dequeue1(events); mysql_mutex_unlock(&rpt->LOCK_rpl_thread); - mysql_cond_signal(&rpt->COND_rpl_thread); goto more_events; } @@ -383,29 +518,26 @@ handle_rpl_parallel_thread(void *arg) half-processed event group. */ mysql_mutex_unlock(&rpt->LOCK_rpl_thread); - group_rgi->is_error= true; + thd->wait_for_prior_commit(); finish_event_group(thd, 1, group_rgi->gtid_sub_id, - group_rgi->parallel_entry, &group_rgi->commit_orderer); - group_rgi->cleanup_context(thd, true); - group_rgi->rli->abort_slave= true; + group_rgi->parallel_entry, group_rgi); + signal_error_to_sql_driver_thread(thd, group_rgi); in_event_group= false; - delete group_rgi; - group_rgi= NULL; mysql_mutex_lock(&rpt->LOCK_rpl_thread); + rpt->free_rgi(group_rgi); + group_rgi= NULL; + group_skip_for_stop= false; } if (!in_event_group) { + rpt->current_owner= NULL; + /* Tell wait_for_done() that we are done, if it is waiting. */ + if (likely(rpt->current_entry) && + unlikely(rpt->current_entry->force_abort)) + mysql_cond_broadcast(&rpt->current_entry->COND_parallel_entry); rpt->current_entry= NULL; if (!rpt->stop) - { - mysql_mutex_lock(&rpt->pool->LOCK_rpl_thread_pool); - list= rpt->pool->free_list; - rpt->next= list; - rpt->pool->free_list= rpt; - if (!list) - mysql_cond_broadcast(&rpt->pool->COND_rpl_thread_pool); - mysql_mutex_unlock(&rpt->pool->LOCK_rpl_thread_pool); - } + rpt->pool->release_thread(rpt); } } @@ -434,6 +566,15 @@ handle_rpl_parallel_thread(void *arg) } +static void +dealloc_gco(group_commit_orderer *gco) +{ + DBUG_ASSERT(!gco->prev_gco /* Must only free after dealloc previous */); + mysql_cond_destroy(&gco->COND_group_commit_orderer); + my_free(gco); +} + + int rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, uint32 new_count, bool skip_check) @@ -468,8 +609,10 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, mysql_mutex_init(key_LOCK_rpl_thread, &new_list[i]->LOCK_rpl_thread, MY_MUTEX_INIT_SLOW); mysql_cond_init(key_COND_rpl_thread, &new_list[i]->COND_rpl_thread, NULL); + mysql_cond_init(key_COND_rpl_thread_queue, + &new_list[i]->COND_rpl_thread_queue, NULL); new_list[i]->pool= pool; - if (mysql_thread_create(key_rpl_parallel_thread, &th, NULL, + if (mysql_thread_create(key_rpl_parallel_thread, &th, &connection_attrib, handle_rpl_parallel_thread, new_list[i])) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -506,7 +649,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, */ for (i= 0; i < pool->count; ++i) { - rpl_parallel_thread *rpt= pool->get_thread(NULL); + rpl_parallel_thread *rpt= pool->get_thread(NULL, NULL); rpt->stop= true; mysql_cond_signal(&rpt->COND_rpl_thread); mysql_mutex_unlock(&rpt->LOCK_rpl_thread); @@ -521,6 +664,24 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, mysql_mutex_unlock(&rpt->LOCK_rpl_thread); mysql_mutex_destroy(&rpt->LOCK_rpl_thread); mysql_cond_destroy(&rpt->COND_rpl_thread); + while (rpt->qev_free_list) + { + rpl_parallel_thread::queued_event *next= rpt->qev_free_list->next; + my_free(rpt->qev_free_list); + rpt->qev_free_list= next; + } + while (rpt->rgi_free_list) + { + rpl_group_info *next= rpt->rgi_free_list->next; + delete rpt->rgi_free_list; + rpt->rgi_free_list= next; + } + while (rpt->gco_free_list) + { + group_commit_orderer *next= rpt->gco_free_list->next_gco; + dealloc_gco(rpt->gco_free_list); + rpt->gco_free_list= next; + } } my_free(pool->threads); @@ -544,6 +705,11 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, pool->changing= false; mysql_mutex_unlock(&LOCK_active_mi); } + + mysql_mutex_lock(&pool->LOCK_rpl_thread_pool); + mysql_cond_broadcast(&pool->COND_rpl_thread_pool); + mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); + return 0; err: @@ -576,6 +742,117 @@ err: } +rpl_parallel_thread::queued_event * +rpl_parallel_thread::get_qev(Log_event *ev, ulonglong event_size, + Relay_log_info *rli) +{ + queued_event *qev; + mysql_mutex_assert_owner(&LOCK_rpl_thread); + if ((qev= qev_free_list)) + qev_free_list= qev->next; + else if(!(qev= (queued_event *)my_malloc(sizeof(*qev), MYF(0)))) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*qev)); + return NULL; + } + qev->ev= ev; + qev->event_size= event_size; + qev->next= NULL; + strcpy(qev->event_relay_log_name, rli->event_relay_log_name); + qev->event_relay_log_pos= rli->event_relay_log_pos; + qev->future_event_relay_log_pos= rli->future_event_relay_log_pos; + strcpy(qev->future_event_master_log_name, rli->future_event_master_log_name); + return qev; +} + + +void +rpl_parallel_thread::free_qev(rpl_parallel_thread::queued_event *qev) +{ + mysql_mutex_assert_owner(&LOCK_rpl_thread); + qev->next= qev_free_list; + qev_free_list= qev; +} + + +rpl_group_info* +rpl_parallel_thread::get_rgi(Relay_log_info *rli, Gtid_log_event *gtid_ev, + rpl_parallel_entry *e) +{ + rpl_group_info *rgi; + mysql_mutex_assert_owner(&LOCK_rpl_thread); + if ((rgi= rgi_free_list)) + { + rgi_free_list= rgi->next; + rgi->reinit(rli); + } + else + { + if(!(rgi= new rpl_group_info(rli))) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*rgi)); + return NULL; + } + rgi->is_parallel_exec = true; + } + if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on()) && + !rgi->deferred_events) + rgi->deferred_events= new Deferred_log_events(rli); + if (event_group_new_gtid(rgi, gtid_ev)) + { + free_rgi(rgi); + my_error(ER_OUT_OF_RESOURCES, MYF(MY_WME)); + return NULL; + } + rgi->parallel_entry= e; + + return rgi; +} + + +void +rpl_parallel_thread::free_rgi(rpl_group_info *rgi) +{ + mysql_mutex_assert_owner(&LOCK_rpl_thread); + DBUG_ASSERT(rgi->commit_orderer.waitee == NULL); + rgi->free_annotate_event(); + rgi->next= rgi_free_list; + rgi_free_list= rgi; +} + + +group_commit_orderer * +rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev) +{ + group_commit_orderer *gco; + mysql_mutex_assert_owner(&LOCK_rpl_thread); + if ((gco= gco_free_list)) + gco_free_list= gco->next_gco; + else if(!(gco= (group_commit_orderer *)my_malloc(sizeof(*gco), MYF(0)))) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gco)); + return NULL; + } + mysql_cond_init(key_COND_group_commit_orderer, + &gco->COND_group_commit_orderer, NULL); + gco->wait_count= wait_count; + gco->prev_gco= prev; + gco->next_gco= NULL; + gco->installed= false; + return gco; +} + + +void +rpl_parallel_thread::free_gco(group_commit_orderer *gco) +{ + mysql_mutex_assert_owner(&LOCK_rpl_thread); + DBUG_ASSERT(!gco->prev_gco /* Must not free until wait has completed. */); + gco->next_gco= gco_free_list; + gco_free_list= gco; +} + + rpl_parallel_thread_pool::rpl_parallel_thread_pool() : count(0), threads(0), free_list(0), changing(false), inited(false) { @@ -618,7 +895,8 @@ rpl_parallel_thread_pool::destroy() Note that we return with the worker threads's LOCK_rpl_thread mutex locked. */ struct rpl_parallel_thread * -rpl_parallel_thread_pool::get_thread(rpl_parallel_entry *entry) +rpl_parallel_thread_pool::get_thread(rpl_parallel_thread **owner, + rpl_parallel_entry *entry) { rpl_parallel_thread *rpt; @@ -628,16 +906,152 @@ rpl_parallel_thread_pool::get_thread(rpl_parallel_entry *entry) free_list= rpt->next; mysql_mutex_unlock(&LOCK_rpl_thread_pool); mysql_mutex_lock(&rpt->LOCK_rpl_thread); + rpt->current_owner= owner; rpt->current_entry= entry; return rpt; } +/* + Release a thread to the thread pool. + The thread should be locked, and should not have any work queued for it. +*/ +void +rpl_parallel_thread_pool::release_thread(rpl_parallel_thread *rpt) +{ + rpl_parallel_thread *list; + + mysql_mutex_assert_owner(&rpt->LOCK_rpl_thread); + DBUG_ASSERT(rpt->current_owner == NULL); + mysql_mutex_lock(&LOCK_rpl_thread_pool); + list= free_list; + rpt->next= list; + free_list= rpt; + if (!list) + mysql_cond_broadcast(&COND_rpl_thread_pool); + mysql_mutex_unlock(&LOCK_rpl_thread_pool); +} + + +/* + Obtain a worker thread that we can queue an event to. + + Each invocation allocates a new worker thread, to maximise + parallelism. However, only up to a maximum of + --slave-domain-parallel-threads workers can be occupied by a single + replication domain; after that point, we start re-using worker threads that + are still executing events that were queued earlier for this thread. + + We never queue more than --rpl-parallel-wait-queue_max amount of events + for one worker, to avoid the SQL driver thread using up all memory with + queued events while worker threads are stalling. + + Note that this function returns with rpl_parallel_thread::LOCK_rpl_thread + locked. Exception is if we were killed, in which case NULL is returned. + + The *did_enter_cond flag is set true if we had to wait for a worker thread + to become free (with mysql_cond_wait()). If so, old_stage will also be set, + and the LOCK_rpl_thread must be released with THD::EXIT_COND() instead + of mysql_mutex_unlock. + + If the flag `reuse' is set, the last worker thread will be returned again, + if it is still available. Otherwise a new worker thread is allocated. +*/ +rpl_parallel_thread * +rpl_parallel_entry::choose_thread(Relay_log_info *rli, bool *did_enter_cond, + PSI_stage_info *old_stage, bool reuse) +{ + uint32 idx; + rpl_parallel_thread *thr; + + idx= rpl_thread_idx; + if (!reuse) + { + ++idx; + if (idx >= rpl_thread_max) + idx= 0; + rpl_thread_idx= idx; + } + thr= rpl_threads[idx]; + if (thr) + { + *did_enter_cond= false; + mysql_mutex_lock(&thr->LOCK_rpl_thread); + for (;;) + { + if (thr->current_owner != &rpl_threads[idx]) + { + /* + The worker thread became idle, and returned to the free list and + possibly was allocated to a different request. So we should allocate + a new worker thread. + */ + unlock_or_exit_cond(rli->sql_driver_thd, &thr->LOCK_rpl_thread, + did_enter_cond, old_stage); + thr= NULL; + break; + } + else if (thr->queued_size <= opt_slave_parallel_max_queued) + { + /* The thread is ready to queue into. */ + break; + } + else if (rli->sql_driver_thd->check_killed()) + { + unlock_or_exit_cond(rli->sql_driver_thd, &thr->LOCK_rpl_thread, + did_enter_cond, old_stage); + my_error(ER_CONNECTION_KILLED, MYF(0)); + DBUG_EXECUTE_IF("rpl_parallel_wait_queue_max", + { + debug_sync_set_action(rli->sql_driver_thd, + STRING_WITH_LEN("now SIGNAL wait_queue_killed")); + };); + slave_output_error_info(rli, rli->sql_driver_thd); + return NULL; + } + else + { + /* + We have reached the limit of how much memory we are allowed to use + for queuing events, so wait for the thread to consume some of its + queue. + */ + if (!*did_enter_cond) + { + /* + We need to do the debug_sync before ENTER_COND(). + Because debug_sync changes the thd->mysys_var->current_mutex, + and this can cause THD::awake to use the wrong mutex. + */ + DBUG_EXECUTE_IF("rpl_parallel_wait_queue_max", + { + debug_sync_set_action(rli->sql_driver_thd, + STRING_WITH_LEN("now SIGNAL wait_queue_ready")); + };); + rli->sql_driver_thd->ENTER_COND(&thr->COND_rpl_thread_queue, + &thr->LOCK_rpl_thread, + &stage_waiting_for_room_in_worker_thread, + old_stage); + *did_enter_cond= true; + } + mysql_cond_wait(&thr->COND_rpl_thread_queue, &thr->LOCK_rpl_thread); + } + } + } + if (!thr) + rpl_threads[idx]= thr= global_rpl_thread_pool.get_thread(&rpl_threads[idx], + this); + + return thr; +} + static void free_rpl_parallel_entry(void *element) { rpl_parallel_entry *e= (rpl_parallel_entry *)element; + if (e->current_gco) + dealloc_gco(e->current_gco); mysql_cond_destroy(&e->COND_parallel_entry); mysql_mutex_destroy(&e->LOCK_parallel_entry); my_free(e); @@ -677,10 +1091,22 @@ rpl_parallel::find(uint32 domain_id) (const uchar *)&domain_id, 0))) { /* Allocate a new, empty one. */ - if (!(e= (struct rpl_parallel_entry *)my_malloc(sizeof(*e), - MYF(MY_ZEROFILL)))) + ulong count= opt_slave_domain_parallel_threads; + if (count == 0 || count > opt_slave_parallel_threads) + count= opt_slave_parallel_threads; + rpl_parallel_thread **p; + if (!my_multi_malloc(MYF(MY_WME|MY_ZEROFILL), + &e, sizeof(*e), + &p, count*sizeof(*p), + NULL)) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*e)+count*sizeof(*p))); return NULL; + } + e->rpl_threads= p; + e->rpl_thread_max= count; e->domain_id= domain_id; + e->stop_on_error_sub_id= (uint64)ULONGLONG_MAX; if (my_hash_insert(&domain_hash, (uchar *)e)) { my_free(e); @@ -698,10 +1124,11 @@ rpl_parallel::find(uint32 domain_id) void -rpl_parallel::wait_for_done() +rpl_parallel::wait_for_done(THD *thd, Relay_log_info *rli) { struct rpl_parallel_entry *e; - uint32 i; + rpl_parallel_thread *rpt; + uint32 i, j; /* First signal all workers that they must force quit; no more events will @@ -709,27 +1136,123 @@ rpl_parallel::wait_for_done() */ for (i= 0; i < domain_hash.records; ++i) { - rpl_parallel_thread *rpt; - e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i); + mysql_mutex_lock(&e->LOCK_parallel_entry); + /* + We want the worker threads to stop as quickly as is safe. If the slave + SQL threads are behind, we could have significant amount of events + queued for the workers, and we want to stop without waiting for them + all to be applied first. But if any event group has already started + executing in a worker, we want to be sure that all prior event groups + are also executed, so that we stop at a consistent point in the binlog + stream (per replication domain). + + All event groups wait for e->count_committing_event_groups to reach + the value of group_commit_orderer::wait_count before starting to + execute. Thus, at this point we know that any event group with a + strictly larger wait_count are safe to skip, none of them can have + started executing yet. So we set e->stop_count here and use it to + decide in the worker threads whether to continue executing an event + group or whether to skip it, when force_abort is set. + + If we stop due to reaching the START SLAVE UNTIL condition, then we + need to continue executing any queued events up to that point. + */ e->force_abort= true; - if ((rpt= e->rpl_thread)) + e->stop_count= rli->stop_for_until ? + e->count_queued_event_groups : e->count_committing_event_groups; + mysql_mutex_unlock(&e->LOCK_parallel_entry); + for (j= 0; j < e->rpl_thread_max; ++j) { - mysql_mutex_lock(&rpt->LOCK_rpl_thread); - if (rpt->current_entry == e) - mysql_cond_signal(&rpt->COND_rpl_thread); - mysql_mutex_unlock(&rpt->LOCK_rpl_thread); + if ((rpt= e->rpl_threads[j])) + { + mysql_mutex_lock(&rpt->LOCK_rpl_thread); + if (rpt->current_owner == &e->rpl_threads[j]) + mysql_cond_signal(&rpt->COND_rpl_thread); + mysql_mutex_unlock(&rpt->LOCK_rpl_thread); + } + } + } + DBUG_EXECUTE_IF("rpl_parallel_wait_for_done_trigger", + { + debug_sync_set_action(thd, + STRING_WITH_LEN("now SIGNAL wait_for_done_waiting")); + };); + + for (i= 0; i < domain_hash.records; ++i) + { + e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i); + for (j= 0; j < e->rpl_thread_max; ++j) + { + if ((rpt= e->rpl_threads[j])) + { + mysql_mutex_lock(&rpt->LOCK_rpl_thread); + while (rpt->current_owner == &e->rpl_threads[j]) + mysql_cond_wait(&e->COND_parallel_entry, &rpt->LOCK_rpl_thread); + mysql_mutex_unlock(&rpt->LOCK_rpl_thread); + } } } +} + + +/* + This function handles the case where the SQL driver thread reached the + START SLAVE UNTIL position; we stop queueing more events but continue + processing remaining, already queued events; then use executes manual + STOP SLAVE; then this function signals to worker threads that they + should stop the processing of any remaining queued events. +*/ +void +rpl_parallel::stop_during_until() +{ + struct rpl_parallel_entry *e; + uint32 i; for (i= 0; i < domain_hash.records; ++i) { e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i); mysql_mutex_lock(&e->LOCK_parallel_entry); - while (e->current_sub_id > e->last_committed_sub_id) - mysql_cond_wait(&e->COND_parallel_entry, &e->LOCK_parallel_entry); + if (e->force_abort) + e->stop_count= e->count_committing_event_groups; + mysql_mutex_unlock(&e->LOCK_parallel_entry); + } +} + + +bool +rpl_parallel::workers_idle() +{ + struct rpl_parallel_entry *e; + uint32 i, max_i; + + max_i= domain_hash.records; + for (i= 0; i < max_i; ++i) + { + bool active; + e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i); + mysql_mutex_lock(&e->LOCK_parallel_entry); + active= e->current_sub_id > e->last_committed_sub_id; mysql_mutex_unlock(&e->LOCK_parallel_entry); + if (active) + break; } + return (i == max_i); +} + + +/* + This is used when we get an error during processing in do_event(); + We will not queue any event to the thread, but we still need to wake it up + to be sure that it will be returned to the pool. +*/ +static void +abandon_worker_thread(THD *thd, rpl_parallel_thread *cur_thread, + bool *did_enter_cond, PSI_stage_info *old_stage) +{ + unlock_or_exit_cond(thd, &cur_thread->LOCK_rpl_thread, + did_enter_cond, old_stage); + mysql_cond_signal(&cur_thread->COND_rpl_thread); } @@ -737,11 +1260,12 @@ rpl_parallel::wait_for_done() do_event() is executed by the sql_driver_thd thread. It's main purpose is to find a thread that can execute the query. - @retval false ok, event was accepted - @retval true error + @retval 0 ok, event was accepted + @retval 1 error + @retval -1 event should be executed serially, in the sql driver thread */ -bool +int rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size) { @@ -752,6 +1276,34 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, Relay_log_info *rli= serial_rgi->rli; enum Log_event_type typ; bool is_group_event; + bool did_enter_cond= false; + PSI_stage_info old_stage; + + /* Handle master log name change, seen in Rotate_log_event. */ + typ= ev->get_type_code(); + if (unlikely(typ == ROTATE_EVENT)) + { + Rotate_log_event *rev= static_cast<Rotate_log_event *>(ev); + if ((rev->server_id != global_system_variables.server_id || + rli->replicate_same_server_id) && + !rev->is_relay_log_event() && + !rli->is_in_group()) + { + memcpy(rli->future_event_master_log_name, + rev->new_log_ident, rev->ident_len+1); + } + } + + /* + Execute queries non-parallel if slave_skip_counter is set, as it's is + easier to skip queries in single threaded mode. + */ + if (rli->slave_skip_counter) + return -1; + + /* Execute pre-10.0 event, which have no GTID, in single-threaded mode. */ + if (unlikely(!current) && typ != GTID_EVENT) + return -1; /* ToDo: what to do with this lock?!? */ mysql_mutex_unlock(&rli->data_lock); @@ -759,161 +1311,132 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, /* Stop queueing additional event groups once the SQL thread is requested to stop. + + We have to queue any remaining events of any event group that has already + been partially queued, but after that we will just ignore any further + events the SQL driver thread may try to queue, and eventually it will stop. */ - if (((typ= ev->get_type_code()) == GTID_EVENT || - !(is_group_event= Log_event::is_group_event(typ))) && - rli->abort_slave) + is_group_event= Log_event::is_group_event(typ); + if ((typ == GTID_EVENT || !is_group_event) && rli->abort_slave) sql_thread_stopping= true; if (sql_thread_stopping) { - /* QQ: Need a better comment why we return false here */ - return false; + delete ev; + /* + Return "no error"; normal stop is not an error, and otherwise the error + has already been recorded. + */ + return 0; } - if (!(qev= (rpl_parallel_thread::queued_event *)my_malloc(sizeof(*qev), - MYF(0)))) + if (typ == GTID_EVENT) { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - return true; + uint32 domain_id; + if (likely(typ == GTID_EVENT)) + { + Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev); + domain_id= (rli->mi->using_gtid == Master_info::USE_GTID_NO ? + 0 : gtid_ev->domain_id); + } + else + domain_id= 0; + if (!(e= find(domain_id))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(MY_WME)); + delete ev; + return 1; + } + current= e; + } + else + e= current; + + /* + Find a worker thread to queue the event for. + Prefer a new thread, so we maximise parallelism (at least for the group + commit). But do not exceed a limit of --slave-domain-parallel-threads; + instead re-use a thread that we queued for previously. + */ + cur_thread= + e->choose_thread(rli, &did_enter_cond, &old_stage, typ != GTID_EVENT); + if (!cur_thread) + { + /* This means we were killed. The error is already signalled. */ + delete ev; + return 1; + } + + if (!(qev= cur_thread->get_qev(ev, event_size, rli))) + { + abandon_worker_thread(rli->sql_driver_thd, cur_thread, + &did_enter_cond, &old_stage); + delete ev; + return 1; } - qev->ev= ev; - qev->event_size= event_size; - qev->next= NULL; - strcpy(qev->event_relay_log_name, rli->event_relay_log_name); - qev->event_relay_log_pos= rli->event_relay_log_pos; - qev->future_event_relay_log_pos= rli->future_event_relay_log_pos; - strcpy(qev->future_event_master_log_name, rli->future_event_master_log_name); if (typ == GTID_EVENT) { Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev); - uint32 domain_id= (rli->mi->using_gtid == Master_info::USE_GTID_NO ? - 0 : gtid_ev->domain_id); - if (!(e= find(domain_id)) || - !(rgi= new rpl_group_info(rli)) || - event_group_new_gtid(rgi, gtid_ev)) + if (!(rgi= cur_thread->get_rgi(rli, gtid_ev, e))) { - my_error(ER_OUT_OF_RESOURCES, MYF(MY_WME)); - delete rgi; - return true; + cur_thread->free_qev(qev); + abandon_worker_thread(rli->sql_driver_thd, cur_thread, + &did_enter_cond, &old_stage); + delete ev; + return 1; } - rgi->is_parallel_exec = true; - if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on())) - rgi->deferred_events= new Deferred_log_events(rli); - if ((gtid_ev->flags2 & Gtid_log_event::FL_GROUP_COMMIT_ID) && - e->last_commit_id == gtid_ev->commit_id) - { - /* - We are already executing something else in this domain. But the two - event groups were committed together in the same group commit on the - master, so we can still do them in parallel here on the slave. + /* + We queue the event group in a new worker thread, to run in parallel + with previous groups. - However, the commit of this event must wait for the commit of the prior - event, to preserve binlog commit order and visibility across all - servers in the replication hierarchy. - */ - rpl_parallel_thread *rpt= global_rpl_thread_pool.get_thread(e); - rgi->wait_commit_sub_id= e->current_sub_id; - rgi->wait_commit_group_info= e->current_group_info; - rgi->wait_start_sub_id= e->prev_groupcommit_sub_id; - e->rpl_thread= cur_thread= rpt; - /* get_thread() returns with the LOCK_rpl_thread locked. */ - } - else + To preserve commit order within the replication domain, we set up + rgi->wait_commit_sub_id to make the new group commit only after the + previous group has committed. + + Event groups that group-committed together on the master can be run + in parallel with each other without restrictions. But one batch of + group-commits may not start before all groups in the previous batch + have initiated their commit phase; we set up rgi->gco to ensure that. + */ + rgi->wait_commit_sub_id= e->current_sub_id; + rgi->wait_commit_group_info= e->current_group_info; + + if (!((gtid_ev->flags2 & Gtid_log_event::FL_GROUP_COMMIT_ID) && + e->last_commit_id == gtid_ev->commit_id)) { /* - Check if we already have a worker thread for this entry. - - We continue to queue more events up for the worker thread while it is - still executing the first ones, to be able to start executing a large - event group without having to wait for the end to be fetched from the - master. And we continue to queue up more events after the first group, - so that we can continue to process subsequent parts of the relay log in - parallel without having to wait for previous long-running events to - complete. - - But if the worker thread is idle at any point, it may return to the - idle list or start servicing a different request. So check this, and - allocate a new thread if the old one is no longer processing for us. + A new batch of transactions that group-committed together on the master. + + Remember the count that marks the end of the previous group committed + batch, and allocate a new gco. */ - cur_thread= e->rpl_thread; - if (cur_thread) - { - mysql_mutex_lock(&cur_thread->LOCK_rpl_thread); - for (;;) - { - if (cur_thread->current_entry != e) - { - /* - The worker thread became idle, and returned to the free list and - possibly was allocated to a different request. This also means - that everything previously queued has already been executed, - else the worker thread would not have become idle. So we should - allocate a new worker thread. - */ - mysql_mutex_unlock(&cur_thread->LOCK_rpl_thread); - e->rpl_thread= cur_thread= NULL; - break; - } - else if (cur_thread->queued_size <= opt_slave_parallel_max_queued) - break; // The thread is ready to queue into - else - { - /* - We have reached the limit of how much memory we are allowed to - use for queuing events, so wait for the thread to consume some - of its queue. - */ - mysql_cond_wait(&cur_thread->COND_rpl_thread, - &cur_thread->LOCK_rpl_thread); - } - } - } + uint64 count= e->count_queued_event_groups; + group_commit_orderer *gco; - if (!cur_thread) - { - /* - Nothing else is currently running in this domain. We can - spawn a new thread to do this event group in parallel with - anything else that might be running in other domains. - */ - cur_thread= e->rpl_thread= global_rpl_thread_pool.get_thread(e); - /* get_thread() returns with the LOCK_rpl_thread locked. */ - } - else + if (!(gco= cur_thread->get_gco(count, e->current_gco))) { - /* - We are still executing the previous event group for this replication - domain, and we have to wait for that to finish before we can start on - the next one. So just re-use the thread. - */ + cur_thread->free_rgi(rgi); + cur_thread->free_qev(qev); + abandon_worker_thread(rli->sql_driver_thd, cur_thread, + &did_enter_cond, &old_stage); + delete ev; + return 1; } - - rgi->wait_commit_sub_id= 0; - rgi->wait_start_sub_id= 0; - e->prev_groupcommit_sub_id= e->current_sub_id; + e->current_gco= rgi->gco= gco; } - + else + rgi->gco= e->current_gco; if (gtid_ev->flags2 & Gtid_log_event::FL_GROUP_COMMIT_ID) - { - e->last_server_id= gtid_ev->server_id; - e->last_seq_no= gtid_ev->seq_no; e->last_commit_id= gtid_ev->commit_id; - } else - { - e->last_server_id= 0; - e->last_seq_no= 0; e->last_commit_id= 0; - } - qev->rgi= e->current_group_info= rgi; e->current_sub_id= rgi->gtid_sub_id; - current= rgi->parallel_entry= e; + ++e->count_queued_event_groups; } - else if (!is_group_event || !current) + else if (!is_group_event) { my_off_t log_pos; int err; @@ -922,45 +1445,22 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, Events like ROTATE and FORMAT_DESCRIPTION. Do not run in worker thread. Same for events not preceeded by GTID (we should not see those normally, but they might be from an old master). - - The varuable `current' is NULL for the case where the master did not - have GTID, like a MariaDB 5.5 or MySQL master. */ qev->rgi= serial_rgi; - /* Handle master log name change, seen in Rotate_log_event. */ - if (typ == ROTATE_EVENT) - { - Rotate_log_event *rev= static_cast<Rotate_log_event *>(qev->ev); - if ((rev->server_id != global_system_variables.server_id || - rli->replicate_same_server_id) && - !rev->is_relay_log_event() && - !rli->is_in_group()) - { - memcpy(rli->future_event_master_log_name, - rev->new_log_ident, rev->ident_len+1); - } - } tmp= serial_rgi->is_parallel_exec; serial_rgi->is_parallel_exec= true; err= rpt_handle_event(qev, NULL); serial_rgi->is_parallel_exec= tmp; - log_pos= qev->ev->log_pos; - delete_or_keep_event_post_apply(serial_rgi, typ, qev->ev); + log_pos= ev->log_pos; + delete_or_keep_event_post_apply(serial_rgi, typ, ev); if (err) { - my_free(qev); - return true; - } - qev->ev= NULL; - qev->future_event_master_log_pos= log_pos; - if (!current) - { - rli->event_relay_log_pos= rli->future_event_relay_log_pos; - handle_queued_pos_update(rli->sql_driver_thd, qev); - my_free(qev); - return false; + cur_thread->free_qev(qev); + abandon_worker_thread(rli->sql_driver_thd, cur_thread, + &did_enter_cond, &old_stage); + return 1; } /* Queue an empty event, so that the position will be updated in a @@ -974,40 +1474,12 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, least the position will not be updated until one of them has reached the current point. */ - cur_thread= current->rpl_thread; - if (cur_thread) - { - mysql_mutex_lock(&cur_thread->LOCK_rpl_thread); - if (cur_thread->current_entry != current) - { - /* Not ours anymore, we need to grab a new one. */ - mysql_mutex_unlock(&cur_thread->LOCK_rpl_thread); - cur_thread= NULL; - } - } - if (!cur_thread) - cur_thread= current->rpl_thread= - global_rpl_thread_pool.get_thread(current); + qev->ev= NULL; + qev->future_event_master_log_pos= log_pos; } else { - cur_thread= current->rpl_thread; - if (cur_thread) - { - mysql_mutex_lock(&cur_thread->LOCK_rpl_thread); - if (cur_thread->current_entry != current) - { - /* Not ours anymore, we need to grab a new one. */ - mysql_mutex_unlock(&cur_thread->LOCK_rpl_thread); - cur_thread= NULL; - } - } - if (!cur_thread) - { - cur_thread= current->rpl_thread= - global_rpl_thread_pool.get_thread(current); - } - qev->rgi= current->current_group_info; + qev->rgi= e->current_group_info; } /* @@ -1015,8 +1487,9 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, */ rli->event_relay_log_pos= rli->future_event_relay_log_pos; cur_thread->enqueue(qev); - mysql_mutex_unlock(&cur_thread->LOCK_rpl_thread); + unlock_or_exit_cond(rli->sql_driver_thd, &cur_thread->LOCK_rpl_thread, + &did_enter_cond, &old_stage); mysql_cond_signal(&cur_thread->COND_rpl_thread); - return false; + return 0; } diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h index 0e88e09652b..c4bb407e5eb 100644 --- a/sql/rpl_parallel.h +++ b/sql/rpl_parallel.h @@ -9,16 +9,66 @@ struct rpl_parallel_entry; struct rpl_parallel_thread_pool; class Relay_log_info; + + +/* + Structure used to keep track of the parallel replication of a batch of + event-groups that group-committed together on the master. + + It is used to ensure that every event group in one batch has reached the + commit stage before the next batch starts executing. + + Note the lifetime of this structure: + + - It is allocated when the first event in a new batch of group commits + is queued, from the free list rpl_parallel_entry::gco_free_list. + + - The gco for the batch currently being queued is owned by + rpl_parallel_entry::current_gco. The gco for a previous batch that has + been fully queued is owned by the gco->prev_gco pointer of the gco for + the following batch. + + - The worker thread waits on gco->COND_group_commit_orderer for + rpl_parallel_entry::count_committing_event_groups to reach wait_count + before starting; the first waiter links the gco into the next_gco + pointer of the gco of the previous batch for signalling. + + - When an event group reaches the commit stage, it signals the + COND_group_commit_orderer if its gco->next_gco pointer is non-NULL and + rpl_parallel_entry::count_committing_event_groups has reached + gco->next_gco->wait_count. + + - When gco->wait_count is reached for a worker and the wait completes, + the worker frees gco->prev_gco; at this point it is guaranteed not to + be needed any longer. +*/ +struct group_commit_orderer { + /* Wakeup condition, used with rpl_parallel_entry::LOCK_parallel_entry. */ + mysql_cond_t COND_group_commit_orderer; + uint64 wait_count; + group_commit_orderer *prev_gco; + group_commit_orderer *next_gco; + bool installed; +}; + + struct rpl_parallel_thread { bool delay_start; bool running; bool stop; mysql_mutex_t LOCK_rpl_thread; mysql_cond_t COND_rpl_thread; + mysql_cond_t COND_rpl_thread_queue; struct rpl_parallel_thread *next; /* For free list. */ struct rpl_parallel_thread_pool *pool; THD *thd; - struct rpl_parallel_entry *current_entry; + /* + Who owns the thread, if any (it's a pointer into the + rpl_parallel_entry::rpl_threads array. + */ + struct rpl_parallel_thread **current_owner; + /* The rpl_parallel_entry of the owner. */ + rpl_parallel_entry *current_entry; struct queued_event { queued_event *next; Log_event *ev; @@ -31,6 +81,9 @@ struct rpl_parallel_thread { size_t event_size; } *event_queue, *last_in_queue; uint64 queued_size; + queued_event *qev_free_list; + rpl_group_info *rgi_free_list; + group_commit_orderer *gco_free_list; void enqueue(queued_event *qev) { @@ -42,15 +95,25 @@ struct rpl_parallel_thread { queued_size+= qev->event_size; } - void dequeue(queued_event *list) + void dequeue1(queued_event *list) { - queued_event *tmp; - DBUG_ASSERT(list == event_queue); event_queue= last_in_queue= NULL; - for (tmp= list; tmp; tmp= tmp->next) - queued_size-= tmp->event_size; } + + void dequeue2(size_t dequeue_size) + { + queued_size-= dequeue_size; + } + + queued_event *get_qev(Log_event *ev, ulonglong event_size, + Relay_log_info *rli); + void free_qev(queued_event *qev); + rpl_group_info *get_rgi(Relay_log_info *rli, Gtid_log_event *gtid_ev, + rpl_parallel_entry *e); + void free_rgi(rpl_group_info *rgi); + group_commit_orderer *get_gco(uint64 wait_count, group_commit_orderer *prev); + void free_gco(group_commit_orderer *gco); }; @@ -66,14 +129,16 @@ struct rpl_parallel_thread_pool { rpl_parallel_thread_pool(); int init(uint32 size); void destroy(); - struct rpl_parallel_thread *get_thread(rpl_parallel_entry *entry); + struct rpl_parallel_thread *get_thread(rpl_parallel_thread **owner, + rpl_parallel_entry *entry); + void release_thread(rpl_parallel_thread *rpt); }; struct rpl_parallel_entry { + mysql_mutex_t LOCK_parallel_entry; + mysql_cond_t COND_parallel_entry; uint32 domain_id; - uint32 last_server_id; - uint64 last_seq_no; uint64 last_commit_id; bool active; /* @@ -82,15 +147,41 @@ struct rpl_parallel_entry { waiting for event groups to complete. */ bool force_abort; + /* + At STOP SLAVE (force_abort=true), we do not want to process all events in + the queue (which could unnecessarily delay stop, if a lot of events happen + to be queued). The stop_count provides a safe point at which to stop, so + that everything before becomes committed and nothing after does. The value + corresponds to group_commit_orderer::wait_count; if wait_count is less than + or equal to stop_count, we execute the associated event group, else we + skip it (and all following) and stop. + */ + uint64 stop_count; - rpl_parallel_thread *rpl_thread; + /* + Cyclic array recording the last rpl_thread_max worker threads that we + queued event for. This is used to limit how many workers a single domain + can occupy (--slave-domain-parallel-threads). + + Note that workers are never explicitly deleted from the array. Instead, + we need to check (under LOCK_rpl_thread) that the thread still belongs + to us before re-using (rpl_thread::current_owner). + */ + rpl_parallel_thread **rpl_threads; + uint32 rpl_thread_max; + uint32 rpl_thread_idx; /* The sub_id of the last transaction to commit within this domain_id. Must be accessed under LOCK_parallel_entry protection. + + Event groups commit in order, so the rpl_group_info for an event group + will be alive (at least) as long as + rpl_grou_info::gtid_sub_id > last_committed_sub_id. This can be used to + safely refer back to previous event groups if they are still executing, + and ignore them if they completed, without requiring explicit + synchronisation between the threads. */ uint64 last_committed_sub_id; - mysql_mutex_t LOCK_parallel_entry; - mysql_cond_t COND_parallel_entry; /* The sub_id of the last event group in this replication domain that was queued for execution by a worker thread. @@ -98,14 +189,29 @@ struct rpl_parallel_entry { uint64 current_sub_id; rpl_group_info *current_group_info; /* - The sub_id of the last event group in the previous batch of group-committed - transactions. - - When we spawn parallel worker threads for the next group-committed batch, - they first need to wait for this sub_id to be committed before it is safe - to start executing them. + If we get an error in some event group, we set the sub_id of that event + group here. Then later event groups (with higher sub_id) can know not to + try to start (event groups that already started will be rolled back when + wait_for_prior_commit() returns error). + The value is ULONGLONG_MAX when no error occured. + */ + uint64 stop_on_error_sub_id; + /* Total count of event groups queued so far. */ + uint64 count_queued_event_groups; + /* + Count of event groups that have started (but not necessarily completed) + the commit phase. We use this to know when every event group in a previous + batch of master group commits have started committing on the slave, so + that it is safe to start executing the events in the following batch. */ - uint64 prev_groupcommit_sub_id; + uint64 count_committing_event_groups; + /* The group_commit_orderer object for the events currently being queued. */ + group_commit_orderer *current_gco; + + rpl_parallel_thread * choose_thread(Relay_log_info *rli, bool *did_enter_cond, + PSI_stage_info *old_stage, bool reuse); + group_commit_orderer *get_gco(); + void free_gco(group_commit_orderer *gco); }; struct rpl_parallel { HASH domain_hash; @@ -116,9 +222,10 @@ struct rpl_parallel { ~rpl_parallel(); void reset(); rpl_parallel_entry *find(uint32 domain_id); - void wait_for_done(); - bool do_event(rpl_group_info *serial_rgi, Log_event *ev, - ulonglong event_size); + void wait_for_done(THD *thd, Relay_log_info *rli); + void stop_during_until(); + bool workers_idle(); + int do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size); }; diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 4a2e88150bf..0ac7d6d7a36 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -1,6 +1,5 @@ -/* - Copyright (c) 2007, 2010, Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab +/* Copyright (c) 2007, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2014, SkySQL 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 @@ -171,11 +170,15 @@ pack_row(TABLE *table, MY_BITMAP const* cols, @param row_data Packed row data @param cols Pointer to bitset describing columns to fill in - @param row_end Pointer to variable that will hold the value of the - one-after-end position for the row + @param curr_row_end + Pointer to variable that will hold the value of the + one-after-end position for the current row @param master_reclength Pointer to variable that will be set to the length of the record on the master side + @param row_end + Pointer to variable that will hold the value of the + end position for the data in the row event @retval 0 No error @@ -188,9 +191,9 @@ pack_row(TABLE *table, MY_BITMAP const* cols, int unpack_row(rpl_group_info *rgi, TABLE *table, uint const colcnt, - uchar const *const row_data, uchar const *const row_buffer_end, - MY_BITMAP const *cols, - uchar const **const row_end, ulong *const master_reclength) + uchar const *const row_data, MY_BITMAP const *cols, + uchar const **const current_row_end, ulong *const master_reclength, + uchar const *const row_end) { DBUG_ENTER("unpack_row"); DBUG_ASSERT(row_data); @@ -309,7 +312,7 @@ unpack_row(rpl_group_info *rgi, uchar const *const old_pack_ptr= pack_ptr; #endif #endif - pack_ptr= f->unpack(f->ptr, pack_ptr, row_buffer_end, metadata); + pack_ptr= f->unpack(f->ptr, pack_ptr, row_end, metadata); DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;" " pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d", f->field_name, metadata, @@ -322,10 +325,10 @@ unpack_row(rpl_group_info *rgi, */ WSREP_WARN("ROW event unpack field: %s metadata: 0x%x;" " pack_ptr: 0x%lx; conv_table %p conv_field %p table %s" - " row_buffer_end: 0x%lx", + " row_end: 0x%lx", f->field_name, metadata, (ulong) old_pack_ptr, conv_table, conv_field, - (table_found) ? "found" : "not found", (ulong)row_buffer_end + (table_found) ? "found" : "not found", (ulong)row_end ); rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT, @@ -407,7 +410,7 @@ unpack_row(rpl_group_info *rgi, DBUG_DUMP("row_data", row_data, pack_ptr - row_data); - *row_end = pack_ptr; + *current_row_end = pack_ptr; if (master_reclength) { if (*field_ptr) diff --git a/sql/rpl_record.h b/sql/rpl_record.h index 7d17d4f7200..c10eb8225b0 100644 --- a/sql/rpl_record.h +++ b/sql/rpl_record.h @@ -1,6 +1,5 @@ -/* - Copyright (c) 2007, 2010, Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab +/* Copyright (c) 2007, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2013, SkySQL 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 @@ -33,9 +32,9 @@ size_t pack_row(TABLE* table, MY_BITMAP const* cols, #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) int unpack_row(rpl_group_info *rgi, TABLE *table, uint const colcnt, - uchar const *const row_data, uchar const *row_buffer_end, - MY_BITMAP const *cols, - uchar const **const row_end, ulong *const master_reclength); + uchar const *const row_data, MY_BITMAP const *cols, + uchar const **const curr_row_end, ulong *const master_reclength, + uchar const *const row_end); // Fill table's record[0] with default values. int prepare_record(TABLE *const table, const uint skip, const bool check); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 9036f810020..0fae3a3bb89 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -37,6 +37,8 @@ static int count_relay_log_space(Relay_log_info* rli); domain). */ rpl_slave_state rpl_global_gtid_slave_state; +/* Object used for MASTER_GTID_WAIT(). */ +gtid_waiting rpl_global_gtid_waiting; // Defined in slave.cc @@ -56,9 +58,10 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) is_fake(FALSE), #endif group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0), - last_master_timestamp(0), slave_skip_counter(0), + last_master_timestamp(0), sql_thread_caught_up(true), slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), sql_driver_thd(), - inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE), + inited(0), abort_slave(0), stop_for_until(0), + slave_running(0), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), executed_entries(0), m_flags(0) { @@ -886,11 +889,11 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, int cmp= strcmp(group_relay_log_name, event_relay_log_name); if (cmp < 0) { - group_relay_log_pos= event_relay_log_pos; + group_relay_log_pos= rgi->future_event_relay_log_pos; strmake_buf(group_relay_log_name, event_relay_log_name); notify_group_relay_log_name_update(); - } else if (cmp == 0 && group_relay_log_pos < event_relay_log_pos) - group_relay_log_pos= event_relay_log_pos; + } else if (cmp == 0 && group_relay_log_pos < rgi->future_event_relay_log_pos) + group_relay_log_pos= rgi->future_event_relay_log_pos; /* In the parallel case we need to update the master_log_name here, rather @@ -1287,9 +1290,14 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, (probably ok - except in some very rare cases, only consequence is that value may take some time to display in Seconds_Behind_Master - not critical). + + In parallel replication, we take care to not set last_master_timestamp + backwards, in case of out-of-order calls here. */ if (!(event_creation_time == 0 && - IF_DBUG(debug_not_change_ts_if_art_event > 0, 1))) + IF_DBUG(debug_not_change_ts_if_art_event > 0, 1)) && + !(rgi->is_parallel_exec && event_creation_time <= last_master_timestamp) + ) last_master_timestamp= event_creation_time; } DBUG_VOID_RETURN; @@ -1312,9 +1320,9 @@ rpl_load_gtid_slave_state(THD *thd) uint32 i; DBUG_ENTER("rpl_load_gtid_slave_state"); - rpl_global_gtid_slave_state.lock(); + mysql_mutex_lock(&rpl_global_gtid_slave_state.LOCK_slave_state); bool loaded= rpl_global_gtid_slave_state.loaded; - rpl_global_gtid_slave_state.unlock(); + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); if (loaded) DBUG_RETURN(0); @@ -1414,10 +1422,10 @@ rpl_load_gtid_slave_state(THD *thd) } } - rpl_global_gtid_slave_state.lock(); + mysql_mutex_lock(&rpl_global_gtid_slave_state.LOCK_slave_state); if (rpl_global_gtid_slave_state.loaded) { - rpl_global_gtid_slave_state.unlock(); + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); goto end; } @@ -1429,7 +1437,7 @@ rpl_load_gtid_slave_state(THD *thd) tmp_entry.sub_id, tmp_entry.gtid.seq_no))) { - rpl_global_gtid_slave_state.unlock(); + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto end; } @@ -1442,14 +1450,14 @@ rpl_load_gtid_slave_state(THD *thd) mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id, entry->gtid.seq_no)) { - rpl_global_gtid_slave_state.unlock(); + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto end; } } rpl_global_gtid_slave_state.loaded= true; - rpl_global_gtid_slave_state.unlock(); + mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state); err= 0; /* Clear HA_ERR_END_OF_FILE */ @@ -1472,14 +1480,27 @@ end: } -rpl_group_info::rpl_group_info(Relay_log_info *rli_) - : rli(rli_), thd(0), gtid_sub_id(0), wait_commit_sub_id(0), - wait_commit_group_info(0), wait_start_sub_id(0), parallel_entry(0), - deferred_events(NULL), m_annotate_event(0), tables_to_lock(0), - tables_to_lock_count(0), trans_retries(0), last_event_start_time(0), - is_parallel_exec(false), is_error(false), - row_stmt_start_timestamp(0), long_find_row_note_printed(false) +void +rpl_group_info::reinit(Relay_log_info *rli) +{ + this->rli= rli; + tables_to_lock= NULL; + tables_to_lock_count= 0; + trans_retries= 0; + last_event_start_time= 0; + is_error= false; + row_stmt_start_timestamp= 0; + long_find_row_note_printed= false; + did_mark_start_commit= false; + commit_orderer.reinit(); +} + +rpl_group_info::rpl_group_info(Relay_log_info *rli) + : thd(0), gtid_sub_id(0), wait_commit_sub_id(0), + wait_commit_group_info(0), parallel_entry(0), + deferred_events(NULL), m_annotate_event(0), is_parallel_exec(false) { + reinit(rli); bzero(¤t_gtid, sizeof(current_gtid)); mysql_mutex_init(key_rpl_group_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST); @@ -1583,6 +1604,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error) if (error) { trans_rollback_stmt(thd); // if a "statement transaction" + /* trans_rollback() also resets OPTION_GTID_BEGIN */ trans_rollback(thd); // if a "real transaction" } m_table_map.clear_tables(); @@ -1702,4 +1724,40 @@ void rpl_group_info::slave_close_thread_tables(THD *thd) } + +static void +mark_start_commit_inner(rpl_parallel_entry *e, group_commit_orderer *gco) +{ + uint64 count= ++e->count_committing_event_groups; + if (gco->next_gco && gco->next_gco->wait_count == count) + mysql_cond_broadcast(&gco->next_gco->COND_group_commit_orderer); +} + + +void +rpl_group_info::mark_start_commit_no_lock() +{ + if (did_mark_start_commit) + return; + mark_start_commit_inner(parallel_entry, gco); + did_mark_start_commit= true; +} + + +void +rpl_group_info::mark_start_commit() +{ + rpl_parallel_entry *e; + + if (did_mark_start_commit) + return; + + e= this->parallel_entry; + mysql_mutex_lock(&e->LOCK_parallel_entry); + mark_start_commit_inner(e, gco); + mysql_mutex_unlock(&e->LOCK_parallel_entry); + did_mark_start_commit= true; +} + + #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index ff2ffd0b366..6db4ce5d61b 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -221,6 +221,12 @@ public: bool sql_force_rotate_relay; time_t last_master_timestamp; + /* + The SQL driver thread sets this true while it is waiting at the end of the + relay log for more events to arrive. SHOW SLAVE STATUS uses this to report + Seconds_Behind_Master as zero while the SQL thread is so waiting. + */ + bool sql_thread_caught_up; void clear_until_condition(); @@ -256,6 +262,7 @@ public: */ volatile bool inited; volatile bool abort_slave; + volatile bool stop_for_until; volatile uint slave_running; /* @@ -475,6 +482,7 @@ private: struct rpl_group_info { + rpl_group_info *next; /* For free list in rpl_parallel_thread */ Relay_log_info *rli; THD *thd; /* @@ -504,14 +512,15 @@ struct rpl_group_info uint64 wait_commit_sub_id; rpl_group_info *wait_commit_group_info; /* - If non-zero, the event group must wait for this sub_id to be committed - before the execution of the event group is allowed to start. + This holds a pointer to a struct that keeps track of the need to wait + for the previous batch of event groups to reach the commit stage, before + this batch can start to execute. (When we execute in parallel the transactions that group committed together on the master, we still need to wait for any prior transactions - to have commtted). + to have reached the commit stage). */ - uint64 wait_start_sub_id; + group_commit_orderer *gco; struct rpl_parallel_entry *parallel_entry; @@ -561,18 +570,22 @@ struct rpl_group_info char future_event_master_log_name[FN_REFLEN]; bool is_parallel_exec; bool is_error; + /* + Set true when we signalled that we reach the commit phase. Used to avoid + counting one event group twice. + */ + bool did_mark_start_commit; -private: /* Runtime state for printing a note when slave is taking too long while processing a row event. */ time_t row_stmt_start_timestamp; bool long_find_row_note_printed; -public: rpl_group_info(Relay_log_info *rli_); ~rpl_group_info(); + void reinit(Relay_log_info *rli); /* Returns true if the argument event resides in the containter; @@ -655,6 +668,8 @@ public: void clear_tables_to_lock(); void cleanup_context(THD *, bool); void slave_close_thread_tables(THD *); + void mark_start_commit_no_lock(); + void mark_start_commit(); time_t get_row_stmt_start_timestamp() { @@ -702,6 +717,7 @@ int init_relay_log_info(Relay_log_info* rli, const char* info_fname); 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 event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev); diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index e8bc042e565..fcb6a849fb1 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -208,7 +208,7 @@ int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata) DBUG_PRINT("result", ("%d", result)); DBUG_RETURN(result); } - +#endif //MYSQL_CLIENT /********************************************************************* * table_def member definitions * *********************************************************************/ @@ -219,7 +219,7 @@ int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata) */ uint32 table_def::calc_field_size(uint col, uchar *master_data) const { - uint32 length; + uint32 length= 0; switch (type(col)) { case MYSQL_TYPE_NEWDECIMAL: @@ -316,7 +316,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const case MYSQL_TYPE_VARCHAR: { length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length() - DBUG_ASSERT(uint2korr(master_data) > 0); length+= length == 1 ? (uint32) *master_data : uint2korr(master_data); break; } @@ -326,17 +325,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: { -#if 1 - /* - BUG#29549: - This is currently broken for NDB, which is using big-endian - order when packing length of BLOB. Once they have decided how to - fix the issue, we can enable the code below to make sure to - always read the length in little-endian order. - */ - Field_blob fb(m_field_metadata[col]); - length= fb.get_packed_size(master_data); -#else /* Compute the length of the data. We cannot use get_length() here since it is dependent on the specific table (and also checks the @@ -362,7 +350,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const } length+= m_field_metadata[col]; -#endif break; } default: @@ -371,7 +358,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const return length; } - +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) /** */ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_INFO *field_cs) diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 71789b0303b..ecf49e633ab 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -142,7 +142,6 @@ void one_thread_scheduler(scheduler_functions *func) { scheduler_init(); func->max_threads= 1; - //max_connections= 1; func->max_connections= &max_connections; func->connection_count= &connection_count; #ifndef EMBEDDED_LIBRARY diff --git a/sql/scheduler.h b/sql/scheduler.h index 4e200e86d74..06c17c7b114 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -99,7 +99,8 @@ public: void *data; /* scheduler-specific data structure */ }; -#if !defined(EMBEDDED_LIBRARY) +#undef HAVE_POOL_OF_THREADS +#if !defined(EMBEDDED_LIBRARY) && !defined(_AIX) #define HAVE_POOL_OF_THREADS 1 void pool_of_threads_scheduler(scheduler_functions* func, ulong *arg_max_connections, diff --git a/sql/set_var.cc b/sql/set_var.cc index 8ae29e01a20..ea577bbfa74 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2002, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab + Copyright (c) 2008, 2014, SkySQL 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 @@ -212,7 +212,6 @@ uchar *sys_var::global_value_ptr(THD *thd, LEX_STRING *base) bool sys_var::check(THD *thd, set_var *var) { - do_deprecated_warning(thd); if ((var->value && do_check(thd, var)) || (on_check && on_check(this, thd, var))) { @@ -546,10 +545,10 @@ int mysql_del_sys_var_chain(sys_var *first) { int result= 0; - /* A write lock should be held on LOCK_system_variables_hash */ - + mysql_rwlock_wrlock(&LOCK_system_variables_hash); for (sys_var *var= first; var; var= var->next) result|= my_hash_delete(&system_variable_hash, (uchar*) var); + mysql_rwlock_unlock(&LOCK_system_variables_hash); return result; } @@ -669,7 +668,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list) if ((error= var->check(thd))) goto err; } - if (!(error= test(thd->is_error()))) + if (!(error= MY_TEST(thd->is_error()))) { it.rewind(); while ((var= it++)) @@ -697,6 +696,7 @@ err: int set_var::check(THD *thd) { + var->do_deprecated_warning(thd); if (var->is_readonly()) { my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str, "read only"); @@ -855,9 +855,7 @@ int set_var_password::update(THD *thd) int set_var_role::check(THD *thd) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - ulonglong access; - int status= acl_check_setrole(thd, base.str, &access); - save_result.ulonglong_value= access; + int status= acl_check_setrole(thd, role.str, &access); return status; #else return 0; @@ -867,7 +865,7 @@ int set_var_role::check(THD *thd) int set_var_role::update(THD *thd) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - return acl_setrole(thd, base.str, save_result.ulonglong_value); + return acl_setrole(thd, role.str, access); #else return 0; #endif diff --git a/sql/set_var.h b/sql/set_var.h index eebbf9b590f..de47c4646e7 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1,6 +1,7 @@ #ifndef SET_VAR_INCLUDED #define SET_VAR_INCLUDED /* Copyright (c) 2002, 2013, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, SkySQL 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 @@ -60,7 +61,7 @@ public: sys_var *next; LEX_CSTRING name; enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023, - READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096 }; + READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096, SHOW_VALUE_IN_HELP=8192 }; /** Enumeration type to indicate for a system variable whether it will be written to the binlog or not. @@ -142,9 +143,11 @@ public: } bool register_option(DYNAMIC_ARRAY *array, int parse_flags) { - return (option.id != -1) && ((flags & PARSE_EARLY) == parse_flags) && - insert_dynamic(array, (uchar*)&option); + return ((((option.id != -1) && ((flags & PARSE_EARLY) == parse_flags)) || + (flags & parse_flags)) && + insert_dynamic(array, (uchar*)&option)); } + void do_deprecated_warning(THD *thd); private: virtual bool do_check(THD *thd, set_var *var) = 0; @@ -158,7 +161,7 @@ private: virtual void global_save_default(THD *thd, set_var *var) = 0; virtual bool session_update(THD *thd, set_var *var) = 0; virtual bool global_update(THD *thd, set_var *var) = 0; - void do_deprecated_warning(THD *thd); + protected: /** A pointer to a value of the variable for SHOW. @@ -281,11 +284,12 @@ public: /* For SET ROLE */ -class set_var_role: public set_var +class set_var_role: public set_var_base { + LEX_STRING role; + ulonglong access; public: - set_var_role(LEX_STRING role_arg) : - set_var(OPT_SESSION, NULL, &role_arg, NULL){}; + set_var_role(LEX_STRING role_arg) : role(role_arg) {} int check(THD *thd); int update(THD *thd); }; diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml index e82ffc85ea6..3e402226a34 100644 --- a/sql/share/charsets/Index.xml +++ b/sql/share/charsets/Index.xml @@ -3,7 +3,7 @@ <charsets max-id="99"> <copyright> - Copyright (c) 2003, 2012, Oracle and/or its affiliates. + Copyright (c) 2003-2005 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 diff --git a/sql/share/charsets/armscii8.xml b/sql/share/charsets/armscii8.xml index 52382c83af0..c1eb93b1f91 100644 --- a/sql/share/charsets/armscii8.xml +++ b/sql/share/charsets/armscii8.xml @@ -3,7 +3,8 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2004 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 diff --git a/sql/share/charsets/ascii.xml b/sql/share/charsets/ascii.xml index bec34ad525e..29336b3a665 100644 --- a/sql/share/charsets/ascii.xml +++ b/sql/share/charsets/ascii.xml @@ -3,7 +3,7 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2007 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 diff --git a/sql/share/charsets/cp1250.xml b/sql/share/charsets/cp1250.xml index 58e55de9bdc..1b4a71ef6d5 100644 --- a/sql/share/charsets/cp1250.xml +++ b/sql/share/charsets/cp1250.xml @@ -3,7 +3,7 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2005 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 diff --git a/sql/share/charsets/cp852.xml b/sql/share/charsets/cp852.xml index 7608296d5b7..e0c574d2ea1 100644 --- a/sql/share/charsets/cp852.xml +++ b/sql/share/charsets/cp852.xml @@ -3,7 +3,8 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2004 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 diff --git a/sql/share/charsets/hebrew.xml b/sql/share/charsets/hebrew.xml index e7f896a3e12..0544b27ef4f 100644 --- a/sql/share/charsets/hebrew.xml +++ b/sql/share/charsets/hebrew.xml @@ -3,7 +3,7 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2006 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 diff --git a/sql/share/charsets/latin1.xml b/sql/share/charsets/latin1.xml index 8963c3481d3..4054eea8d33 100644 --- a/sql/share/charsets/latin1.xml +++ b/sql/share/charsets/latin1.xml @@ -3,7 +3,7 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2005 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 diff --git a/sql/share/charsets/latin2.xml b/sql/share/charsets/latin2.xml index 183da7b6cd3..a44ec7e0ec6 100644 --- a/sql/share/charsets/latin2.xml +++ b/sql/share/charsets/latin2.xml @@ -3,7 +3,7 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2005 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 diff --git a/sql/share/charsets/latin5.xml b/sql/share/charsets/latin5.xml index 489299564f1..6b60e58cdda 100644 --- a/sql/share/charsets/latin5.xml +++ b/sql/share/charsets/latin5.xml @@ -3,7 +3,7 @@ <charsets> <copyright> - Copyright (C) 2003 MySQL AB + Copyright (c) 2003, 2005 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 diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 2d061fc314c..dc50c68bcdd 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -1732,30 +1732,8 @@ ER_WRONG_AUTO_KEY 42000 S1009 spa "Puede ser solamente un campo automatico y este debe ser definido como una clave" swe "Det får finnas endast ett AUTO_INCREMENT-fält och detta måste vara en nyckel" ukr "Невірне визначення таблиці; Може бути лише один автоматичний стовбець, що повинен бути визначений як ключ" -ER_READY - cze "%s: připraven na spojení\nVersion: '%s' socket: '%s' port: %d"" - dan "%s: klar til tilslutninger\nVersion: '%s' socket: '%s' port: %d"" - nla "%s: klaar voor verbindingen\nVersion: '%s' socket: '%s' port: %d"" - eng "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d" - est "%s: ootab ühendusi\nVersion: '%s' socket: '%s' port: %d"" - fre "%s: Prêt pour des connexions\nVersion: '%s' socket: '%s' port: %d"" - ger "%s: Bereit für Verbindungen.\nVersion: '%s' Socket: '%s' Port: %d" - greek "%s: σε αναμονή συνδέσεων\nVersion: '%s' socket: '%s' port: %d"" - hun "%s: kapcsolatra kesz\nVersion: '%s' socket: '%s' port: %d"" - ita "%s: Pronto per le connessioni\nVersion: '%s' socket: '%s' port: %d"" - jpn "%s: 接続準備完了。\nバージョン: '%s' socket: '%s' port: %d"" - kor "%s: 연결 준비중입니다\nVersion: '%s' socket: '%s' port: %d"" - nor "%s: klar for tilkoblinger\nVersion: '%s' socket: '%s' port: %d"" - norwegian-ny "%s: klar for tilkoblingar\nVersion: '%s' socket: '%s' port: %d"" - pol "%s: gotowe do poł?czenia\nVersion: '%s' socket: '%s' port: %d"" - por "%s: Pronto para conexões\nVersion: '%s' socket: '%s' port: %d"" - rum "%s: sint gata pentru conectii\nVersion: '%s' socket: '%s' port: %d"" - rus "%s: Готов принимать соединения.\nВерсия: '%s' сокет: '%s' порт: %d" - serbian "%s: Spreman za konekcije\nVersion: '%s' socket: '%s' port: %d"" - slo "%s: pripravený na spojenie\nVersion: '%s' socket: '%s' port: %d"" - spa "%s: preparado para conexiones\nVersion: '%s' socket: '%s' port: %d"" - swe "%s: klar att ta emot klienter\nVersion: '%s' socket: '%s' port: %d"" - ukr "%s: Готовий для з'єднань!\nVersion: '%s' socket: '%s' port: %d"" +ER_UNUSED_9 + eng "You should never see it" ER_NORMAL_SHUTDOWN cze "%s: normální ukončení\n" dan "%s: Normal nedlukning\n" @@ -2140,13 +2118,9 @@ ER_INSERT_INFO spa "Registros: %ld Duplicados: %ld Peligros: %ld" swe "Rader: %ld Dubletter: %ld Varningar: %ld" ukr "Записів: %ld Дублікатів: %ld Застережень: %ld" -ER_UPDATE_TABLE_USED - eng "You can't specify target table '%-.192s' for update in FROM clause" - ger "Die Verwendung der zu aktualisierenden Zieltabelle '%-.192s' ist in der FROM-Klausel nicht zulässig." - jpn "FROM句にある表 '%-.192s' はUPDATEの対象にできません。" - rus "Не допускается указание таблицы '%-.192s' в списке таблиц FROM для внесения в нее изменений" - swe "INSERT-table '%-.192s' får inte finnas i FROM tabell-listan" - ukr "Таблиця '%-.192s' що змінюється не дозволена у переліку таблиць FROM" +ER_UPDATE_TABLE_USED + eng "Table '%-.192s' is specified twice, both as a target for '%s' and as a separate source for data" + swe "Table '%-.192s' är använd två gånger. Både för '%s' och för att hämta data" ER_NO_SUCH_THREAD cze "Neznámá identifikace threadu: %lu" dan "Ukendt tråd id: %lu" @@ -5055,19 +5029,19 @@ ER_UNSUPPORTED_PS eng "This command is not supported in the prepared statement protocol yet" ger "Dieser Befehl wird im Protokoll für vorbereitete Anweisungen noch nicht unterstützt" ER_GET_ERRMSG - dan "Modtog fejl %d '%-.100s' fra %s" - eng "Got error %d '%-.100s' from %s" - ger "Fehler %d '%-.100s' von %s" - jpn "エラー %d '%-.100s' が %s から返されました。" - nor "Mottok feil %d '%-.100s' fa %s" - norwegian-ny "Mottok feil %d '%-.100s' fra %s" + dan "Modtog fejl %d '%-.200s' fra %s" + eng "Got error %d '%-.200s' from %s" + ger "Fehler %d '%-.200s' von %s" + jpn "エラー %d '%-.200s' が %s から返されました。" + nor "Mottok feil %d '%-.200s' fa %s" + norwegian-ny "Mottok feil %d '%-.200s' fra %s" ER_GET_TEMPORARY_ERRMSG - dan "Modtog temporary fejl %d '%-.100s' fra %s" - eng "Got temporary error %d '%-.100s' from %s" - jpn "一時エラー %d '%-.100s' が %s から返されました。" - ger "Temporärer Fehler %d '%-.100s' von %s" - nor "Mottok temporary feil %d '%-.100s' fra %s" - norwegian-ny "Mottok temporary feil %d '%-.100s' fra %s" + dan "Modtog temporary fejl %d '%-.200s' fra %s" + eng "Got temporary error %d '%-.200s' from %s" + jpn "一時エラー %d '%-.200s' が %s から返されました。" + ger "Temporärer Fehler %d '%-.200s' von %s" + nor "Mottok temporary feil %d '%-.200s' fra %s" + norwegian-ny "Mottok temporary feil %d '%-.200s' fra %s" ER_UNKNOWN_TIME_ZONE eng "Unknown or incorrect time zone: '%-.64s'" ger "Unbekannte oder falsche Zeitzone: '%-.64s'" @@ -6757,7 +6731,7 @@ ER_TABLESPACE_DISCARDED eng "Tablespace has been discarded for table '%-.192s'" ER_INTERNAL_ERROR - eng "Internal error: '%-.192s'" + eng "Internal error: %-.192s" ER_INNODB_IMPORT_ERROR eng "ALTER TABLE '%-.192s' IMPORT TABLESPACE failed with error %lu : '%s'" @@ -7056,7 +7030,7 @@ ER_UNTIL_REQUIRES_USING_GTID ER_GTID_STRICT_OUT_OF_ORDER eng "An attempt was made to binlog GTID %u-%u-%llu which would create an out-of-order sequence number with existing GTID %u-%u-%llu, and gtid strict mode is enabled." ER_GTID_START_FROM_BINLOG_HOLE - eng "The binlog on the master is missing the GTID %u-%u-%llu requested by the slave (even though both a prior and a subsequent sequence number does exist), and GTID strict mode is enabled" + eng "The binlog on the master is missing the GTID %u-%u-%llu requested by the slave (even though a subsequent sequence number does exist), and GTID strict mode is enabled" ER_SLAVE_UNEXPECTED_MASTER_SWITCH eng "Unexpected GTID received from master after reconnect. This normally indicates that the master server was replaced without restarting the slave threads. %s" ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO @@ -7089,3 +7063,7 @@ ER_CHANGE_SLAVE_PARALLEL_THREADS_ACTIVE eng "Cannot change @@slave_parallel_threads while another change is in progress" ER_PRIOR_COMMIT_FAILED eng "Commit failed due to failure of an earlier commit on which this one depends" +ER_IT_IS_A_VIEW 42S02 + eng "'%-.192s' is a view" +ER_SLAVE_SKIP_NOT_IN_GTID + eng "When using GTID, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position." diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index c50072f5159..3fadbcd088f 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. - Copyright (c) 2011, Monty Program Ab. +/* Copyright (c) 2011, 2012, Oracle and/or its affiliates. + Copyright (c) 2011, 2014, SkySQL 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 diff --git a/sql/slave.cc b/sql/slave.cc index e29387a4b7f..ecfd048fd26 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2011, Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2014, SkySQL 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 @@ -334,7 +334,7 @@ run_slave_init_thread() pthread_t th; slave_init_thread_running= true; - if (mysql_thread_create(key_thread_slave_init, &th, NULL, + if (mysql_thread_create(key_thread_slave_init, &th, &connection_attrib, handle_slave_init, NULL)) { sql_print_error("Failed to create thread while initialising slave"); @@ -576,7 +576,7 @@ void init_slave_skip_errors(const char* arg) const char *p; DBUG_ENTER("init_slave_skip_errors"); - if (bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0)) + 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); @@ -618,7 +618,14 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) if (thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) { DBUG_PRINT("info",("Terminating SQL thread")); - mi->rli.abort_slave=1; + if (opt_slave_parallel_threads > 0 && + mi->rli.abort_slave && mi->rli.stop_for_until) + { + mi->rli.stop_for_until= false; + mi->rli.parallel.stop_during_until(); + } + else + mi->rli.abort_slave=1; if ((error=terminate_slave_thread(mi->rli.sql_driver_thd, sql_lock, &mi->rli.stop_cond, &mi->rli.slave_running, @@ -1050,9 +1057,10 @@ static bool sql_slave_killed(rpl_group_info *rgi) "documentation for details)."; DBUG_PRINT("info", ("modified_non_trans_table: %d OPTION_BEGIN: %d " - "is_in_group: %d", + "OPTION_KEEP_LOG: %d is_in_group: %d", thd->transaction.all.modified_non_trans_table, - test(thd->variables.option_bits & OPTION_BEGIN), + MY_TEST(thd->variables.option_bits & OPTION_BEGIN), + MY_TEST(thd->variables.option_bits & OPTION_KEEP_LOG), rli->is_in_group())); if (rli->abort_slave) @@ -1338,6 +1346,7 @@ bool is_network_error(uint errorno) errorno == ER_CON_COUNT_ERROR || errorno == ER_CONNECTION_KILLED || errorno == ER_NEW_ABORTING_CONNECTION || + errorno == ER_NET_READ_INTERRUPTED || errorno == ER_SERVER_SHUTDOWN) return TRUE; @@ -1797,10 +1806,14 @@ when it try to get the value of TIME_ZONE global variable from master."; if (mysql_errno(mysql) == ER_UNKNOWN_SYSTEM_VARIABLE) { - // this is tolerable as OM -> NS is supported - mi->report(WARNING_LEVEL, mysql_errno(mysql), - "Notifying master by %s failed with " - "error: %s", query, mysql_error(mysql)); + /* Ignore this expected error if not a high error level */ + if (global_system_variables.log_warnings > 1) + { + // this is tolerable as OM -> NS is supported + mi->report(WARNING_LEVEL, mysql_errno(mysql), + "Notifying master by %s failed with " + "error: %s", query, mysql_error(mysql)); + } } else { @@ -2626,8 +2639,24 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) && mi->rli.slave_running) { - long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp) - - mi->clock_diff_with_master); + long time_diff; + bool idle; + time_t stamp= mi->rli.last_master_timestamp; + + if (!stamp) + idle= true; + else + { + idle= mi->rli.sql_thread_caught_up; + if (opt_slave_parallel_threads > 0 && idle && + !mi->rli.parallel.workers_idle()) + idle= false; + } + if (idle) + time_diff= 0; + else + { + time_diff= ((long)(time(0) - stamp) - mi->clock_diff_with_master); /* Apparently on some systems time_diff can be <0. Here are possible reasons related to MySQL: @@ -2643,13 +2672,15 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, slave is 2. At SHOW SLAVE STATUS time, assume that the difference between timestamp of slave and rli->last_master_timestamp is 0 (i.e. they are in the same second), then we get 0-(2-1)=-1 as a result. - This confuses users, so we don't go below 0: hence the MY_MAX(). + This confuses users, so we don't go below 0. last_master_timestamp == 0 (an "impossible" timestamp 1970) is a special marker to say "consider we have caught up". */ - protocol->store((longlong)(mi->rli.last_master_timestamp ? - MY_MAX(0, time_diff) : 0)); + if (time_diff < 0) + time_diff= 0; + } + protocol->store((longlong)time_diff); } else { @@ -3127,9 +3158,10 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)", ev->get_type_str(), ev->get_type_code(), ev->server_id)); - DBUG_PRINT("info", ("thd->options: %s%s; rgi->last_event_start_time: %lu", + DBUG_PRINT("info", ("thd->options: '%s%s%s' rgi->last_event_start_time: %lu", FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT), FLAGSTR(thd->variables.option_bits, OPTION_BEGIN), + FLAGSTR(thd->variables.option_bits, OPTION_GTID_BEGIN), (ulong) rgi->last_event_start_time)); /* @@ -3202,7 +3234,7 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, "skipped because event skip counter was non-zero" }; DBUG_PRINT("info", ("OPTION_BEGIN: %d IN_STMT: %d IN_TRANSACTION: %d", - test(thd->variables.option_bits & OPTION_BEGIN), + MY_TEST(thd->variables.option_bits & OPTION_BEGIN), rli->get_flag(Relay_log_info::IN_STMT), rli->get_flag(Relay_log_info::IN_TRANSACTION))); DBUG_PRINT("skip_event", ("%s event was %s", @@ -3406,6 +3438,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, message about error in query execution to be printed. */ rli->abort_slave= 1; + rli->stop_for_until= true; mysql_mutex_unlock(&rli->data_lock); delete ev; DBUG_RETURN(1); @@ -3433,13 +3466,17 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, update_state_of_relay_log(rli, ev); - /* - Execute queries in parallel, except if slave_skip_counter is set, - as it's is easier to skip queries in single threaded mode. - */ - - if (opt_slave_parallel_threads > 0 && rli->slave_skip_counter == 0) - DBUG_RETURN(rli->parallel.do_event(serial_rgi, ev, event_size)); + if (opt_slave_parallel_threads > 0) + { + int res= rli->parallel.do_event(serial_rgi, ev, event_size); + if (res >= 0) + DBUG_RETURN(res); + /* + Else we proceed to execute the event non-parallel. + This is the case for pre-10.0 events without GTID, and for handling + slave_skip_counter. + */ + } /* For GTID, allocate a new sub_id for the given domain_id. @@ -4350,6 +4387,7 @@ pthread_handler_t handle_slave_sql(void *arg) Seconds_Behind_Master grows. No big deal. */ rli->abort_slave = 0; + rli->stop_for_until= false; mysql_mutex_unlock(&rli->run_lock); mysql_cond_broadcast(&rli->start_cond); @@ -4526,7 +4564,7 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME, } if (opt_slave_parallel_threads > 0) - rli->parallel.wait_for_done(); + rli->parallel.wait_for_done(thd, rli); /* Thread stopped. Print the current replication position to the log */ { @@ -4552,7 +4590,7 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME, get the correct position printed.) */ if (opt_slave_parallel_threads > 0) - rli->parallel.wait_for_done(); + rli->parallel.wait_for_done(thd, rli); /* Some events set some playgrounds, which won't be cleared because thread @@ -5746,8 +5784,8 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi, mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir); /* Set MYSQL_PLUGIN_DIR in case master asks for an external authentication plugin */ - if (opt_plugin_dir_ptr && *opt_plugin_dir_ptr)
- mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir_ptr);
+ if (opt_plugin_dir_ptr && *opt_plugin_dir_ptr) + mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir_ptr); /* we disallow empty users */ if (mi->user == NULL || mi->user[0] == 0) @@ -6122,6 +6160,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) if (hot_log) mysql_mutex_unlock(log_lock); + rli->sql_thread_caught_up= false; DBUG_RETURN(ev); } if (opt_reckless_slave) // For mysql-test @@ -6159,12 +6198,10 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) Seconds_Behind_Master would be zero only when master has no more updates in binlog for slave. The heartbeat can be sent in a (small) fraction of slave_net_timeout. Until it's done - rli->last_master_timestamp is temporarely (for time of - waiting for the following event) reset whenever EOF is - reached. + rli->sql_thread_caught_up is temporarely (for time of waiting for + the following event) set whenever EOF is reached. */ - time_t save_timestamp= rli->last_master_timestamp; - rli->last_master_timestamp= 0; + rli->sql_thread_caught_up= true; DBUG_ASSERT(rli->relay_log.get_open_count() == rli->cur_log_old_open_count); @@ -6208,6 +6245,17 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) } /* + We have to check sql_slave_killed() here an extra time. + Otherwise we may miss a wakeup, since last check was done + without holding LOCK_log. + */ + if (sql_slave_killed(rgi)) + { + mysql_mutex_unlock(log_lock); + break; + } + + /* We can, and should release data_lock while we are waiting for update. If we do not, show slave status will block */ @@ -6279,7 +6327,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) rli->relay_log.wait_for_update_relay_log(rli->sql_driver_thd); // re-acquire data lock since we released it earlier mysql_mutex_lock(&rli->data_lock); - rli->last_master_timestamp= save_timestamp; + rli->sql_thread_caught_up= false; continue; } /* diff --git a/sql/sp_head.h b/sql/sp_head.h index 77adbf091b8..c1fb455e103 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -462,9 +462,10 @@ public: else if (m_flags & HAS_SQLCOM_FLUSH) my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH"); - return test(m_flags & - (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT| - HAS_COMMIT_OR_ROLLBACK|HAS_SQLCOM_RESET|HAS_SQLCOM_FLUSH)); + return MY_TEST(m_flags & + (CONTAINS_DYNAMIC_SQL | MULTI_RESULTS | + HAS_SET_AUTOCOMMIT_STMT | HAS_COMMIT_OR_ROLLBACK | + HAS_SQLCOM_RESET | HAS_SQLCOM_FLUSH)); } #ifndef DBUG_OFF diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index ce692024d0d..c48025da93d 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -450,7 +450,7 @@ public: int close(THD *thd); my_bool is_open() - { return test(server_side_cursor); } + { return MY_TEST(server_side_cursor); } int fetch(THD *, List<sp_variable> *vars); diff --git a/sql/spatial.cc b/sql/spatial.cc index b82e6977f8a..34d2417f632 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -302,7 +302,8 @@ int Geometry::create_from_opresult(Geometry_buffer *g_buf, res->q_append((char) wkb_ndr); res->q_append(geom_type); - return obj->init_from_opresult(res, rr.result(), rr.length()); + return obj->init_from_opresult(res, rr.result(), rr.length()) == 0 && + rr.length(); } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f2e762980b9..91bdb174d51 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2013, Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, SkySQL 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 @@ -800,7 +800,7 @@ ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) : static bool is_invalid_role_name(const char *str) { - if (strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE")) + if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE")) return false; my_error(ER_INVALID_ROLE, MYF(0), str); @@ -1085,7 +1085,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); (void) my_hash_init2(&acl_roles,50, &my_charset_utf8_bin, - 0,0,0, (my_hash_get_key) acl_role_get_key, + 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0, (void (*)(void *))free_acl_role, 0); username_char_length= MY_MIN(table->field[1]->char_length(), @@ -1427,8 +1427,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); /* account for every role mapping */ - (void) my_hash_init2(&acl_roles_mappings, 50, system_charset_info, - 0,0,0, (my_hash_get_key) acl_role_map_get_key, 0,0); + (void) my_hash_init2(&acl_roles_mappings, 50, system_charset_info, 0, 0, 0, + (my_hash_get_key) acl_role_map_get_key, 0, 0, 0); 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))) @@ -3001,9 +3001,9 @@ static bool update_user_table(THD *thd, TABLE *table, static bool test_if_create_new_users(THD *thd) { Security_context *sctx= thd->security_ctx; - bool create_new_users= test(sctx->master_access & INSERT_ACL) || + bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) || (!opt_safe_user_create && - test(sctx->master_access & CREATE_USER_ACL)); + MY_TEST(sctx->master_access & CREATE_USER_ACL)); if (!create_new_users) { TABLE_LIST tl; @@ -3420,7 +3420,7 @@ abort: } /** - Updates the mysql.roles_mapping table and the acl_roles_mappings hash. + Updates the mysql.roles_mapping table @param table TABLE to update @param user user name of the grantee @@ -3468,20 +3468,10 @@ replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host, host->str, user->str, role->str)); goto table_error; } - /* - This should always return something, as the check was performed - earlier - */ - my_hash_delete(&acl_roles_mappings, (uchar*)existing); } - else + else if (with_admin) { - if (revoke_grant) - existing->with_admin= false; - else - existing->with_admin|= with_admin; - - table->field[3]->store(existing->with_admin + 1); + table->field[3]->store(!revoke_grant + 1); if ((error= table->file->ha_update_row(table->record[1], table->record[0]))) { @@ -3501,14 +3491,6 @@ replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host, host->str, user->str, role->str)); goto table_error; } - else - { - /* allocate a new entry that will go in the hash */ - ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR; - if (hash_entry->init(&acl_memroot, user->str, host->str, role->str, with_admin)) - DBUG_RETURN(1); - my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry); - } /* all ok */ DBUG_RETURN(0); @@ -3519,6 +3501,48 @@ table_error: DBUG_RETURN(1); } + +/** + Updates the acl_roles_mappings hash + + @param user user name of the grantee + @param host host name of the grantee + @param role role name to grant + @param with_admin WITH ADMIN OPTION flag + @param existing the entry in the acl_roles_mappings hash or NULL. + it is never NULL if revoke_grant is true. + it is NULL when a new pair is added, it's not NULL + when an existing pair is updated. + @param revoke_grant true for REVOKE, false for GRANT +*/ +static int +update_role_mapping(LEX_STRING *user, LEX_STRING *host, LEX_STRING *role, + bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant) +{ + if (revoke_grant) + { + if (with_admin) + { + existing->with_admin= false; + return 0; + } + return my_hash_delete(&acl_roles_mappings, (uchar*)existing); + } + + if (existing) + { + existing->with_admin|= with_admin; + return 0; + } + + /* allocate a new entry that will go in the hash */ + ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR; + if (hash_entry->init(&acl_memroot, user->str, host->str, + role->str, with_admin)) + return 1; + return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry); +} + static void acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke) { @@ -3742,8 +3766,8 @@ public: bool ok() { return privs != 0 || cols != 0; } void init_hash() { - my_hash_init2(&hash_columns, 4, system_charset_info, - 0, 0, 0, (my_hash_get_key) get_key_column, 0, 0); + my_hash_init2(&hash_columns, 4, system_charset_info, 0, 0, 0, + (my_hash_get_key) get_key_column, 0, 0, 0); } }; @@ -5092,7 +5116,8 @@ static int update_role_table_columns(GRANT_TABLE *merged, now those roles were dropped or had their privileges revoked). we need to remove this GRANT_TABLE */ - DBUG_EXECUTE_IF("role_merge_stats", role_column_merges+= test(merged->cols);); + DBUG_EXECUTE_IF("role_merge_stats", + role_column_merges+= MY_TEST(merged->cols);); my_hash_delete(&column_priv_hash,(uchar*) merged); return 4; } @@ -5117,7 +5142,7 @@ static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee, const char *db, const char *tname, role_hash_t *rhash) { Dynamic_array<GRANT_TABLE *> grants; - DBUG_ASSERT(test(db) == test(tname)); // both must be set, or neither + DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither /* first, collect table/column privileges granted to @@ -5240,7 +5265,7 @@ static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee, { ulong update_flags= 0; - DBUG_ASSERT(test(db) == test(tname)); // both must be set, or neither + DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;); @@ -5538,8 +5563,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, else error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, - test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER)); + MY_TEST(thd->variables.sql_mode & + MODE_NO_AUTO_CREATE_USER)); if (error) { result= TRUE; // Remember error @@ -5746,8 +5771,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, - test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER)); + MY_TEST(thd->variables.sql_mode & + MODE_NO_AUTO_CREATE_USER)); if (error) { result= TRUE; // Remember error @@ -5876,6 +5901,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) */ DBUG_ASSERT(list.elements >= 2); bool result= 0; + bool create_new_user, no_auto_create_user; String wrong_users; LEX_USER *user, *granted_role; LEX_STRING rolename; @@ -5891,12 +5917,19 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) DBUG_ASSERT(granted_role->is_role()); rolename= granted_role->user; - TABLE_LIST tables; - tables.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("roles_mapping"), - "roles_mapping", TL_WRITE); + create_new_user= test_if_create_new_users(thd); + no_auto_create_user= MY_TEST(thd->variables.sql_mode & + MODE_NO_AUTO_CREATE_USER); + + TABLE_LIST tables[2]; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("roles_mapping"), + "roles_mapping", TL_WRITE); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_WRITE); + tables[0].next_local= tables[0].next_global= tables+1; - if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) DBUG_RETURN(TRUE); /* purecov: deadcode */ mysql_rwlock_wrlock(&LOCK_grant); @@ -5984,6 +6017,27 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) if (!grantee) grantee= find_user_exact(hostname.str, username.str); + if (!grantee && !revoke) + { + LEX_USER user_combo = *user; + user_combo.host = hostname; + user_combo.user = username; + + /* create the user if it does not exist */ + if (replace_user_table(thd, tables[1].table, user_combo, 0, + false, create_new_user, + no_auto_create_user)) + { + append_user(&wrong_users, username.str, hostname.str); + result= 1; + continue; + } + grantee= find_user_exact(hostname.str, username.str); + + /* either replace_user_table failed, or we've added the user */ + DBUG_ASSERT(grantee); + } + if (!grantee) { append_user(&wrong_users, username.str, hostname.str); @@ -6036,7 +6090,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) } /* write into the roles_mapping table */ - if (replace_roles_mapping_table(tables.table, + if (replace_roles_mapping_table(tables[0].table, &username, &hostname, &rolename, thd->lex->with_admin_option, hash_entry, revoke)) @@ -6055,6 +6109,8 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) } continue; } + update_role_mapping(&username, &hostname, &rolename, + thd->lex->with_admin_option, hash_entry, revoke); /* Only need to propagate grants when granting/revoking a role to/from @@ -6181,8 +6237,8 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, else if (replace_user_table(thd, tables[0].table, *Str, (!db ? rights : 0), revoke_grant, create_new_users, - test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER))) + MY_TEST(thd->variables.sql_mode & + MODE_NO_AUTO_CREATE_USER))) result= -1; else if (db) { @@ -6611,7 +6667,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, for (tl= tables; number-- ; tl= tl->next_global) { - sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx; + sctx= MY_TEST(tl->security_ctx) ? tl->security_ctx : thd->security_ctx; const ACL_internal_table_access *access= get_cached_table_access(&tl->grant.m_internal, @@ -6866,7 +6922,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, GRANT_INFO *grant; const char *db_name; const char *table_name; - Security_context *sctx= test(table_ref->security_ctx) ? + Security_context *sctx= MY_TEST(table_ref->security_ctx) ? table_ref->security_ctx : thd->security_ctx; if (table_ref->view || table_ref->field_translation) @@ -8355,7 +8411,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, { role= safe_str(get_field(thd->mem_root, role_field)); - if (strcmp(user_from->user.str, role)) + if (!user_from->is_role() || strcmp(user_from->user.str, role)) continue; error= 0; @@ -8578,7 +8634,6 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, const char *UNINIT_VAR(user); const char *UNINIT_VAR(host); const char *UNINIT_VAR(role); - uint role_not_matched= 1; ACL_USER *acl_user= NULL; ACL_ROLE *acl_role= NULL; ACL_DB *acl_db= NULL; @@ -8738,11 +8793,10 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, if (struct_no == ROLES_MAPPINGS_HASH) { - role_not_matched= strcmp(user_from->user.str, role); - if (role_not_matched && + if (user_from->is_role() ? strcmp(user_from->user.str, role) : (strcmp(user_from->user.str, user) || my_strcasecmp(system_charset_info, user_from->host.str, host))) - continue; + continue; } else { @@ -8862,14 +8916,14 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, size_t old_key_length= role_grant_pair->hashkey.length; bool oom; - if (role_not_matched) - oom= role_grant_pair->init(&acl_memroot, user_to->user.str, - user_to->host.str, - role_grant_pair->r_uname, false); - else + if (user_to->is_role()) oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname, role_grant_pair->u_hname, user_to->user.str, false); + else + oom= role_grant_pair->init(&acl_memroot, user_to->user.str, + user_to->host.str, + role_grant_pair->r_uname, false); if (oom) DBUG_RETURN(-1); @@ -9211,6 +9265,10 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) undo_add_role_user_mapping(grantee, role); result= TRUE; } + else if (grantee) + update_role_mapping(&thd->lex->definer->user, + &thd->lex->definer->host, + &user_name->user, true, NULL, false); } } @@ -9275,7 +9333,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) if (handle_as_role != user_name->is_role()) { - append_user(&wrong_users, tmp_user_name); + append_user(&wrong_users, user_name); result= TRUE; continue; } @@ -9353,20 +9411,20 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) while ((tmp_user_from= user_list++)) { tmp_user_to= user_list++; - if (!(user_from= get_current_user(thd, tmp_user_from, false)) || - user_from->is_role()) + if (!(user_from= get_current_user(thd, tmp_user_from, false))) { append_user(&wrong_users, user_from); result= TRUE; continue; } - if (!(user_to= get_current_user(thd, tmp_user_to, false)) || - user_to->is_role()) + if (!(user_to= get_current_user(thd, tmp_user_to, false))) { append_user(&wrong_users, user_to); result= TRUE; continue; } + DBUG_ASSERT(!user_from->is_role()); + DBUG_ASSERT(!user_to->is_role()); /* Search all in-memory structures and grant tables @@ -9597,6 +9655,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) { result= -1; //Something went wrong } + update_role_mapping(&lex_user->user, &lex_user->host, + &role_grant->user, false, pair, true); /* Delete from the parent_grantee array of the roles granted, the entry pointing to this user_or_role @@ -11448,7 +11508,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, db + passwd_len + 1 : 0; if (passwd == NULL || - passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len) + passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len) return packet_error; /* strlen() can't be easily deleted without changing protocol */ diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 3f5ae79299d..6989b76accb 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -163,10 +163,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, - Run a normal repair using the new index file and the old data file */ - if (table->s->frm_version != FRM_VER_TRUE_VARCHAR) + if (table->s->frm_version != FRM_VER_TRUE_VARCHAR && + table->s->varchar_fields) { error= send_check_errmsg(thd, table_list, "repair", - "Failed repairing incompatible .frm file"); + "Failed repairing a very old .frm file as the data file format has changed between versions. Please dump the table in your old system with mysqldump and read it into this system with mysql or mysqlimport"); goto end; } @@ -1222,7 +1223,7 @@ bool Sql_cmd_repair_table::execute(THD *thd) WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL) res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair", TL_WRITE, 1, - test(m_lex->check_opt.sql_flags & TT_USEFRM), + MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM), HA_OPEN_FOR_REPAIR, &prepare_for_repair, &handler::ha_repair, 0); diff --git a/sql/sql_admin.h b/sql/sql_admin.h index fa89fc9063f..77fc41e2ec4 100644 --- a/sql/sql_admin.h +++ b/sql/sql_admin.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 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 diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 86bc3bb4e54..e3282709cd6 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -356,19 +356,8 @@ bool Sql_cmd_discard_import_tablespace::execute(THD *thd) it is the case. TODO: this design is obsolete and will be removed. */ - int table_kind= check_if_log_table(table_list->db_length, table_list->db, - table_list->table_name_length, - table_list->table_name, false); - - if (table_kind) - { - /* Disable alter of enabled log tables */ - if (logger.is_log_table_enabled(table_kind)) - { - my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER"); - return true; - } - } + if (check_if_log_table(table_list, TRUE, "ALTER")) + return true; return mysql_discard_or_import_tablespace(thd, table_list, diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index be35340df27..ac3f7f25518 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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 diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index f0e35cb022f..a92f69f3da4 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2013, 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 diff --git a/sql/sql_audit.h b/sql/sql_audit.h index bf8d837580a..85e7abb23e9 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -1,7 +1,7 @@ #ifndef SQL_AUDIT_INCLUDED #define SQL_AUDIT_INCLUDED -/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2013, 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 diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5c70c4501f0..f1479d47fcd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -389,7 +389,8 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share) { THD *in_use= tab->in_use; - if (in_use && (in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + DBUG_ASSERT(in_use && tab->s->tdc.flushed); + if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { in_use->killed= KILL_SYSTEM_THREAD; @@ -430,9 +431,12 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, { bool result= FALSE; struct timespec abstime; + ulong refresh_version; DBUG_ENTER("close_cached_tables"); DBUG_ASSERT(thd || (!wait_for_refresh && !tables)); + refresh_version= tdc_increment_refresh_version(); + if (!tables) { /* @@ -442,13 +446,12 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, incrementing of refresh_version is followed by purge of unused table shares. */ - tdc_increment_refresh_version(); kill_delayed_threads(); /* Get rid of all unused TABLE and TABLE_SHARE instances. By doing this we automatically close all tables which were marked as "old". */ - tc_purge(); + tc_purge(true); /* Free table shares which were not freed implicitly by loop above. */ tdc_purge(true); } @@ -530,7 +533,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, while ((share= tdc_it.next())) { mysql_mutex_lock(&share->tdc.LOCK_table_share); - if (share->has_old_version()) + if (share->tdc.flushed && share->tdc.version < refresh_version) { /* wait_for_old_version() will unlock mutex and free share */ found= true; @@ -558,7 +561,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, if (thd->killed) break; if (tdc_wait_for_old_version(thd, table->db, table->table_name, timeout, - MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL)) + MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL, + refresh_version)) { result= TRUE; break; @@ -788,12 +792,18 @@ static void close_open_tables(THD *thd) access the table cache key @param[in] extra - HA_EXTRA_PREPRE_FOR_DROP if the table is being dropped - HA_EXTRA_PREPARE_FOR_REANME if the table is being renamed - HA_EXTRA_NOT_USED no drop/rename - In case of drop/reanme the documented behaviour is to + HA_EXTRA_PREPARE_FOR_DROP + - The table is dropped + HA_EXTRA_PREPARE_FOR_RENAME + - The table is renamed + HA_EXTRA_NOT_USED + - The table is marked as closed in the + locked_table_list but kept there so one can call + locked_table_list->reopen_tables() to put it back. + + In case of drop/rename the documented behavior is to implicitly remove the table from LOCK TABLES - list. + list. @pre Must be called with an X MDL lock on the table. */ @@ -1136,7 +1146,8 @@ bool close_temporary_tables(THD *thd) /* We always quote db,table names though it is slight overkill */ if (found_user_tables && - !(was_quote_show= test(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE))) + !(was_quote_show= MY_TEST(thd->variables.option_bits & + OPTION_QUOTE_SHOW_CREATE))) { thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE; } @@ -1481,7 +1492,7 @@ void update_non_unique_table_error(TABLE_LIST *update, return; } } - my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias); + my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias, operation); } @@ -1592,26 +1603,21 @@ TABLE *find_temporary_table(THD *thd, thd->temporary_tables list, it's impossible to tell here whether we're dealing with an internal or a user temporary table. - If is_trans is not null, we return the type of the table: - either transactional (e.g. innodb) as TRUE or non-transactional - (e.g. myisam) as FALSE. + @param thd Thread handler + @param table Temporary table to be deleted + @param is_trans Is set to the type of the table: + transactional (e.g. innodb) as TRUE or non-transactional + (e.g. myisam) as FALSE. @retval 0 the table was found and dropped successfully. - @retval 1 the table was not found in the list of temporary tables - of this thread @retval -1 the table is in use by a outer query */ -int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans) +int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans) { DBUG_ENTER("drop_temporary_table"); DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", - table_list->db, table_list->table_name)); - - if (!is_temporary_table(table_list)) - DBUG_RETURN(1); - - TABLE *table= table_list->table; + table->s->db.str, table->s->table_name.str)); /* Table might be in use by some outer statement. */ if (table->query_id && table->query_id != thd->query_id) @@ -1631,10 +1637,10 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans) */ mysql_lock_remove(thd, thd->lock, table); close_temporary_table(thd, table, 1, 1); - table_list->table= NULL; DBUG_RETURN(0); } + /* unlink from thd->temporary tables and close temporary table */ @@ -1757,7 +1763,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, DBUG_ENTER("wait_while_table_is_used"); DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", table->s->table_name.str, (ulong) table->s, - table->db_stat, table->s->version)); + table->db_stat, table->s->tdc.version)); if (thd->mdl_context.upgrade_shared_lock( table->mdl_ticket, MDL_EXCLUSIVE, @@ -2294,7 +2300,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, retry_share: share= tdc_acquire_share(thd, table_list->db, table_list->table_name, - key, key_length, gts_flags, &table); + key, key_length, + table_list->mdl_request.key.tc_hash_value(), + gts_flags, &table); if (!share) { @@ -2324,7 +2332,7 @@ retry_share: /* Check if this TABLE_SHARE-object corresponds to a view. Note, that there is - no need to call TABLE_SHARE::has_old_version() as we do for regular tables, + no need to check TABLE_SHARE::tdc.flushed as we do for regular tables, because view shares are always up to date. */ if (share->is_view) @@ -2365,8 +2373,10 @@ retry_share: if (!(flags & MYSQL_OPEN_IGNORE_FLUSH)) { - if (share->has_old_version()) + if (share->tdc.flushed) { + DBUG_PRINT("info", ("Found old share version: %lu current: %lu", + share->tdc.version, tdc_refresh_version())); /* We already have an MDL lock. But we have encountered an old version of table in the table definition cache which is possible @@ -2397,7 +2407,7 @@ retry_share: goto retry_share; } - if (thd->open_tables && thd->open_tables->s->version != share->version) + if (thd->open_tables && thd->open_tables->s->tdc.flushed) { /* If the version changes while we're opening the tables, @@ -2498,6 +2508,7 @@ retry_share: err_lock: tdc_release_share(share); + DBUG_PRINT("exit", ("failed")); DBUG_RETURN(TRUE); } @@ -2615,9 +2626,9 @@ Locked_tables_list::init_locked_tables(THD *thd) { TABLE_LIST *src_table_list= table->pos_in_table_list; char *db, *table_name, *alias; - size_t db_len= src_table_list->db_length; - size_t table_name_len= src_table_list->table_name_length; - size_t alias_len= strlen(src_table_list->alias); + size_t db_len= table->s->db.length; + size_t table_name_len= table->s->table_name.length; + size_t alias_len= table->alias.length(); TABLE_LIST *dst_table_list; if (! multi_alloc_root(&m_locked_tables_root, @@ -2627,23 +2638,15 @@ Locked_tables_list::init_locked_tables(THD *thd) &alias, alias_len + 1, NullS)) { - unlock_locked_tables(0); + reset(); return TRUE; } - memcpy(db, src_table_list->db, db_len + 1); - memcpy(table_name, src_table_list->table_name, table_name_len + 1); - memcpy(alias, src_table_list->alias, alias_len + 1); - /** - Sic: remember the *actual* table level lock type taken, to - acquire the exact same type in reopen_tables(). - E.g. if the table was locked for write, src_table_list->lock_type is - TL_WRITE_DEFAULT, whereas reginfo.lock_type has been updated from - thd->update_lock_default. - */ + memcpy(db, table->s->db.str, db_len + 1); + memcpy(table_name, table->s->table_name.str, table_name_len + 1); + strmake(alias, table->alias.ptr(), alias_len); dst_table_list->init_one_table(db, db_len, table_name, table_name_len, - alias, - src_table_list->table->reginfo.lock_type); + alias, table->reginfo.lock_type); dst_table_list->table= table; dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket; @@ -2664,7 +2667,7 @@ Locked_tables_list::init_locked_tables(THD *thd) (m_locked_tables_count+1)); if (m_reopen_array == NULL) { - unlock_locked_tables(0); + reset(); return TRUE; } } @@ -2685,42 +2688,50 @@ Locked_tables_list::init_locked_tables(THD *thd) void Locked_tables_list::unlock_locked_tables(THD *thd) { - if (thd) + DBUG_ASSERT(!thd->in_sub_stmt && + !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); + /* + Sic: we must be careful to not close open tables if + we're not in LOCK TABLES mode: unlock_locked_tables() is + sometimes called implicitly, expecting no effect on + open tables, e.g. from begin_trans(). + */ + if (thd->locked_tables_mode != LTM_LOCK_TABLES) + return; + + for (TABLE_LIST *table_list= m_locked_tables; + table_list; table_list= table_list->next_global) { - DBUG_ASSERT(!thd->in_sub_stmt && - !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); /* - Sic: we must be careful to not close open tables if - we're not in LOCK TABLES mode: unlock_locked_tables() is - sometimes called implicitly, expecting no effect on - open tables, e.g. from begin_trans(). + Clear the position in the list, the TABLE object will be + returned to the table cache. */ - if (thd->locked_tables_mode != LTM_LOCK_TABLES) - return; + if (table_list->table) // If not closed + table_list->table->pos_in_locked_tables= NULL; + } + thd->leave_locked_tables_mode(); - for (TABLE_LIST *table_list= m_locked_tables; - table_list; table_list= table_list->next_global) - { - /* - Clear the position in the list, the TABLE object will be - returned to the table cache. - */ - if (table_list->table) // If not closed - table_list->table->pos_in_locked_tables= NULL; - } - thd->leave_locked_tables_mode(); + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + + /* + We rely on the caller to implicitly commit the + transaction and release transactional locks. + */ - DBUG_ASSERT(thd->transaction.stmt.is_empty()); - close_thread_tables(thd); - /* - We rely on the caller to implicitly commit the - transaction and release transactional locks. - */ - } /* After closing tables we can free memory used for storing lock request for metadata locks and TABLE_LIST elements. */ + reset(); +} + +/* + Free memory allocated for storing locks +*/ + +void Locked_tables_list::reset() +{ free_root(&m_locked_tables_root, MYF(0)); m_locked_tables= NULL; m_locked_tables_last= &m_locked_tables; @@ -2785,6 +2796,7 @@ void Locked_tables_list::unlink_from_list(THD *thd, m_locked_tables_last= table_list->prev_global; else table_list->next_global->prev_global= table_list->prev_global; + m_locked_tables_count--; } } @@ -2838,8 +2850,13 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count) m_locked_tables_last= table_list->prev_global; else table_list->next_global->prev_global= table_list->prev_global; + m_locked_tables_count--; } } + + /* If no tables left, do an automatic UNLOCK TABLES */ + if (thd->lock && thd->lock->table_count == 0) + unlock_locked_tables(thd); } @@ -2912,6 +2929,62 @@ Locked_tables_list::reopen_tables(THD *thd) return FALSE; } +/** + Add back a locked table to the locked list that we just removed from it. + This is needed in CREATE OR REPLACE TABLE where we are dropping, creating + and re-opening a locked table. + + @return 0 0k + @return 1 error +*/ + +bool Locked_tables_list::restore_lock(THD *thd, TABLE_LIST *dst_table_list, + TABLE *table, MYSQL_LOCK *lock) +{ + MYSQL_LOCK *merged_lock; + DBUG_ENTER("restore_lock"); + DBUG_ASSERT(!strcmp(dst_table_list->table_name, table->s->table_name.str)); + + /* Ensure we have the memory to add the table back */ + if (!(merged_lock= mysql_lock_merge(thd->lock, lock))) + DBUG_RETURN(1); + thd->lock= merged_lock; + + /* Link to the new table */ + dst_table_list->table= table; + /* + The lock type may have changed (normally it should not as create + table will lock the table in write mode + */ + dst_table_list->lock_type= table->reginfo.lock_type; + table->pos_in_locked_tables= dst_table_list; + + add_back_last_deleted_lock(dst_table_list); + + table->mdl_ticket->downgrade_lock(table->reginfo.lock_type >= + TL_WRITE_ALLOW_WRITE ? + MDL_SHARED_NO_READ_WRITE : + MDL_SHARED_READ); + + DBUG_RETURN(0); +} + +/* + Add back the last deleted lock structure. + This should be followed by a call to reopen_tables() to + open the table. +*/ + +void Locked_tables_list::add_back_last_deleted_lock(TABLE_LIST *dst_table_list) +{ + /* Link the lock back in the locked tables list */ + dst_table_list->prev_global= m_locked_tables_last; + *m_locked_tables_last= dst_table_list; + m_locked_tables_last= &dst_table_list->next_global; + dst_table_list->next_global= 0; + m_locked_tables_count++; +} + #ifndef DBUG_OFF /* Cause a spurious statement reprepare for debug purposes. */ @@ -4049,9 +4122,9 @@ lock_table_names(THD *thd, if (mdl_requests.is_empty()) DBUG_RETURN(FALSE); - /* Check if CREATE TABLE was used */ - create_table= (tables_start && tables_start->open_strategy == - TABLE_LIST::OPEN_IF_EXISTS); + /* Check if CREATE TABLE without REPLACE was used */ + create_table= (thd->lex->sql_command == SQLCOM_CREATE_TABLE && + !(thd->lex->create_info.options & HA_LEX_CREATE_REPLACE)); if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) { @@ -5332,6 +5405,39 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, } +/* + Restart transaction for tables + + This is used when we had to do an implicit commit after tables are opened + and want to restart transactions on tables. + + This is used in case of: + LOCK TABLES xx + CREATE OR REPLACE TABLE xx; +*/ + +bool restart_trans_for_tables(THD *thd, TABLE_LIST *table) +{ + DBUG_ENTER("restart_trans_for_tables"); + + if (!thd->locked_tables_mode) + DBUG_RETURN(FALSE); + + for (; table; table= table->next_global) + { + if (table->placeholder()) + continue; + + if (check_lock_and_start_stmt(thd, thd->lex, table)) + { + DBUG_ASSERT(0); // Should never happen + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE); +} + + /** Prepare statement for reopening of tables and recalculation of set of prelocked tables. @@ -6495,7 +6601,7 @@ find_field_in_tables(THD *thd, Item_ident *item, */ if (db) return cur_field; - + if (found) { if (report_error == REPORT_ALL_ERRORS || @@ -6510,7 +6616,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (found) return found; - + /* If the field was qualified and there were no tables to search, issue an error that an unknown table was given. The situation is detected @@ -7437,10 +7543,16 @@ err: order, thus when we iterate over it, we are moving from the right to the left in the FROM clause. + NOTES + We can't run this many times as the first_name_resolution_table would + be different for subsequent runs when sub queries has been optimized + away. + RETURN TRUE Error FALSE OK */ + static bool setup_natural_join_row_types(THD *thd, List<TABLE_LIST> *from_clause, Name_resolution_context *context) @@ -7450,6 +7562,19 @@ static bool setup_natural_join_row_types(THD *thd, if (from_clause->elements == 0) DBUG_RETURN(false); /* We come here in the case of UNIONs. */ + /* + Do not redo work if already done: + 1) for stored procedures, + 2) for multitable update after lock failure and table reopening. + */ + if (!context->select_lex->first_natural_join_processing) + { + context->first_name_resolution_table= context->natural_join_first_table; + DBUG_PRINT("info", ("using cached setup_natural_join_row_types")); + DBUG_RETURN(false); + } + context->select_lex->first_natural_join_processing= false; + List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause); TABLE_LIST *table_ref; /* Current table reference. */ /* Table reference to the left of the current. */ @@ -7466,22 +7591,15 @@ static bool setup_natural_join_row_types(THD *thd, left_neighbor= table_ref_it++; } while (left_neighbor && left_neighbor->sj_subq_pred); - /* - Do not redo work if already done: - 1) for stored procedures, - 2) for multitable update after lock failure and table reopening. - */ - if (context->select_lex->first_natural_join_processing) + + if (store_top_level_join_columns(thd, table_ref, + left_neighbor, right_neighbor)) + DBUG_RETURN(true); + if (left_neighbor) { - if (store_top_level_join_columns(thd, table_ref, - left_neighbor, right_neighbor)) - DBUG_RETURN(true); - if (left_neighbor) - { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); - left_neighbor->next_name_resolution_table= first_leaf_on_the_right; - } + TABLE_LIST *first_leaf_on_the_right; + first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); + left_neighbor->next_name_resolution_table= first_leaf_on_the_right; } right_neighbor= table_ref; } @@ -7495,8 +7613,11 @@ static bool setup_natural_join_row_types(THD *thd, DBUG_ASSERT(right_neighbor); context->first_name_resolution_table= right_neighbor->first_leaf_for_name_resolution(); - context->select_lex->first_natural_join_processing= false; - + /* + This is only to ensure that first_name_resolution_table doesn't + change on re-execution + */ + context->natural_join_first_table= context->first_name_resolution_table; DBUG_RETURN (false); } @@ -7670,7 +7791,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, thd->lex->allow_sum_func= save_allow_sum_func; thd->mark_used_columns= save_mark_used_columns; DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); - DBUG_RETURN(test(thd->is_error())); + DBUG_RETURN(MY_TEST(thd->is_error())); } @@ -7842,12 +7963,9 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (table_list->merge_underlying_list) { DBUG_ASSERT(table_list->is_merged_derived()); - Query_arena *arena= thd->stmt_arena, backup; + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); bool res; - if (arena->is_conventional()) - arena= 0; // For easier test - else - thd->set_n_backup_active_arena(arena, &backup); res= table_list->setup_underlying(thd); if (arena) thd->restore_active_arena(arena, &backup); @@ -8098,7 +8216,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!(item= field_iterator.create_item(thd))) DBUG_RETURN(TRUE); -// DBUG_ASSERT(item->fixed); + /* cache the table for the Item_fields inserted by expanding stars */ if (item->type() == Item::FIELD_ITEM && tables->cacheable_table) ((Item_field *)item)->cached_table= tables; @@ -8226,11 +8344,8 @@ void wrap_ident(THD *thd, Item **conds) { Item_direct_ref_to_ident *wrapper; DBUG_ASSERT((*conds)->type() == Item::FIELD_ITEM || (*conds)->type() == Item::REF_ITEM); - Query_arena *arena= thd->stmt_arena, backup; - if (arena->is_conventional()) - arena= 0; - else - thd->set_n_backup_active_arena(arena, &backup); + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); if ((wrapper= new Item_direct_ref_to_ident((Item_ident *)(*conds)))) (*conds)= (Item*) wrapper; if (arena) @@ -8377,7 +8492,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, select_lex->where= *conds; } thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; - DBUG_RETURN(test(thd->is_error())); + DBUG_RETURN(MY_TEST(thd->is_error())); err_no_arena: select_lex->is_item_list_lookup= save_is_item_list_lookup; diff --git a/sql/sql_base.h b/sql/sql_base.h index 3e633fad084..61442843a39 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -248,7 +248,7 @@ void close_thread_table(THD *thd, TABLE **table_ptr); bool close_temporary_tables(THD *thd); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, bool check_alias); -int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans); +int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans); void close_temporary_table(THD *thd, TABLE *table, bool free_share, bool delete_table); void close_temporary(TABLE *table, bool free_share, bool delete_table); @@ -486,6 +486,8 @@ inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, } +bool restart_trans_for_tables(THD *thd, TABLE_LIST *table); + /** A context of open_tables() function, used to recover from a failed open_table() or open_routine() attempt. diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 4d91dbab9a6..fef959c37ca 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2005, 2011, Oracle and/or its affiliates. + Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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 diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h index 7e163b0dbcc..55b2d7eefd9 100644 --- a/sql/sql_bitmap.h +++ b/sql/sql_bitmap.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2003, 2010, Oracle and/or its affiliates +/* Copyright (c) 2003, 2013, Oracle and/or its affiliates Copyright (c) 2009, 2013, Monty Program Ab. This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ public: Bitmap() { init(); } Bitmap(const Bitmap& from) { *this=from; } explicit Bitmap(uint prefix_to_set) { init(prefix_to_set); } - void init() { bitmap_init(&map, buffer, default_width, 0); } + void init() { my_bitmap_init(&map, buffer, default_width, 0); } void init(uint prefix_to_set) { init(); set_prefix(prefix_to_set); } uint length() const { return default_width; } Bitmap& operator=(const Bitmap& map2) @@ -52,7 +52,7 @@ public: void intersect(ulonglong map2buff) { MY_BITMAP map2; - bitmap_init(&map2, (uint32 *)&map2buff, sizeof(ulonglong)*8, 0); + my_bitmap_init(&map2, (uint32 *)&map2buff, sizeof(ulonglong)*8, 0); bitmap_intersect(&map, &map2); } /* Use highest bit for all bits above sizeof(ulonglong)*8. */ @@ -61,7 +61,7 @@ public: intersect(map2buff); if (map.n_bits > sizeof(ulonglong) * 8) bitmap_set_above(&map, sizeof(ulonglong), - test(map2buff & (1LL << (sizeof(ulonglong) * 8 - 1)))); + MY_TEST(map2buff & (1LL << (sizeof(ulonglong) * 8 - 1)))); } void subtract(Bitmap& map2) { bitmap_subtract(&map, &map2.map); } void merge(Bitmap& map2) { bitmap_union(&map, &map2.map); } @@ -156,7 +156,7 @@ public: void intersect_extended(ulonglong map2) { map&= map2; } void subtract(Bitmap<64>& map2) { map&= ~map2.map; } void merge(Bitmap<64>& map2) { map|= map2.map; } - bool is_set(uint n) const { return test(map & (((ulonglong)1) << n)); } + bool is_set(uint n) const { return MY_TEST(map & (((ulonglong) 1) << n)); } bool is_prefix(uint n) const { return map == (((ulonglong)1) << n)-1; } bool is_clear_all() const { return map == (ulonglong)0; } bool is_set_all() const { return map == ~(ulonglong)0; } diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 5307f4f01f8..c6c5418e0cf 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1333,6 +1333,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg) query->unlock_n_destroy(); block= block->next; } while (block != queries_blocks); + queries_blocks= NULL; // avoid second destroying by free_cache } free_cache(); @@ -1404,9 +1405,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) Query_cache_query_flags flags; // fill all gaps between fields with 0 to get repeatable key bzero(&flags, QUERY_CACHE_FLAGS_SIZE); - flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG); - flags.client_protocol_41= test(thd->client_capabilities & - CLIENT_PROTOCOL_41); + flags.client_long_flag= MY_TEST(thd->client_capabilities & CLIENT_LONG_FLAG); + flags.client_protocol_41= MY_TEST(thd->client_capabilities & + CLIENT_PROTOCOL_41); /* Protocol influences result format, so statement results in the binary protocol (COM_EXECUTE) cannot be served to statements asking for results @@ -1415,10 +1416,10 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) flags.protocol_type= (unsigned int) thd->protocol->type(); /* PROTOCOL_LOCAL results are not cached. */ DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL); - flags.more_results_exists= test(thd->server_status & - SERVER_MORE_RESULTS_EXISTS); + flags.more_results_exists= MY_TEST(thd->server_status & + SERVER_MORE_RESULTS_EXISTS); flags.in_trans= thd->in_active_multi_stmt_transaction(); - flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT); + flags.autocommit= MY_TEST(thd->server_status & SERVER_STATUS_AUTOCOMMIT); flags.pkt_nr= net->pkt_nr; flags.character_set_client_num= thd->variables.character_set_client->number; @@ -1899,14 +1900,14 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) // fill all gaps between fields with 0 to get repeatable key bzero(&flags, QUERY_CACHE_FLAGS_SIZE); - flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG); - flags.client_protocol_41= test(thd->client_capabilities & - CLIENT_PROTOCOL_41); + flags.client_long_flag= MY_TEST(thd->client_capabilities & CLIENT_LONG_FLAG); + flags.client_protocol_41= MY_TEST(thd->client_capabilities & + CLIENT_PROTOCOL_41); flags.protocol_type= (unsigned int) thd->protocol->type(); - flags.more_results_exists= test(thd->server_status & - SERVER_MORE_RESULTS_EXISTS); + flags.more_results_exists= MY_TEST(thd->server_status & + SERVER_MORE_RESULTS_EXISTS); flags.in_trans= thd->in_active_multi_stmt_transaction(); - flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT); + flags.autocommit= MY_TEST(thd->server_status & SERVER_STATUS_AUTOCOMMIT); flags.pkt_nr= thd->net.pkt_nr; flags.character_set_client_num= thd->variables.character_set_client->number; flags.character_set_results_num= @@ -3409,7 +3410,7 @@ my_bool Query_cache::register_all_tables(THD *thd, if (block_table->parent) unlink_table(block_table); } - return test(n); + return MY_TEST(n); } @@ -4109,7 +4110,7 @@ Query_cache::is_cacheable(THD *thd, LEX *lex, (long) OPTION_TO_QUERY_CACHE, (long) lex->select_lex.options, (int) thd->variables.query_cache_type, - (uint) test(qc_is_able_to_intercept_result(thd)))); + (uint) MY_TEST(qc_is_able_to_intercept_result(thd)))); DBUG_RETURN(0); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b1b8a017992..d979df6dc33 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -518,14 +518,14 @@ extern "C" int mysql_tmpfile(const char *prefix) extern "C" int thd_in_lock_tables(const THD *thd) { - return test(thd->in_lock_tables); + return MY_TEST(thd->in_lock_tables); } extern "C" int thd_tablespace_op(const THD *thd) { - return test(thd->tablespace_op); + return MY_TEST(thd->tablespace_op); } extern "C" @@ -1445,6 +1445,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, got_warning= 1; break; case Sql_condition::WARN_LEVEL_ERROR: + mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg); break; default: DBUG_ASSERT(FALSE); @@ -1591,6 +1592,7 @@ void THD::init(void) set_status_var_init(); bzero((char *) &org_status_var, sizeof(org_status_var)); start_bytes_received= 0; + last_commit_gtid.seq_no= 0; #ifdef WITH_WSREP wsrep_exec_mode= wsrep_applier ? REPL_RECV : LOCAL_STATE; wsrep_conflict_state= NO_CONFLICT; @@ -2702,6 +2704,7 @@ bool select_result::check_simple_select() const static String default_line_term("\n",default_charset_info); static String default_escaped("\\",default_charset_info); static String default_field_term("\t",default_charset_info); +static String default_enclosed_and_line_start("", default_charset_info); static String default_xml_row_term("<row>", default_charset_info); sql_exchange::sql_exchange(char *name, bool flag, @@ -2710,7 +2713,7 @@ sql_exchange::sql_exchange(char *name, bool flag, { filetype= filetype_arg; field_term= &default_field_term; - enclosed= line_start= &my_empty_string; + enclosed= line_start= &default_enclosed_and_line_start; line_term= filetype == FILETYPE_CSV ? &default_line_term : &default_xml_row_term; escaped= &default_escaped; @@ -2850,7 +2853,7 @@ void select_to_file::send_error(uint errcode,const char *err) bool select_to_file::send_eof() { - int error= test(end_io_cache(&cache)); + int error= MY_TEST(end_io_cache(&cache)); if (mysql_file_close(file, MYF(MY_WME)) || thd->is_error()) error= true; @@ -3031,8 +3034,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) escape_char= (int) (uchar) (*exchange->escaped)[0]; else escape_char= -1; - is_ambiguous_field_sep= test(strchr(ESCAPE_CHARS, field_sep_char)); - is_unsafe_field_sep= test(strchr(NUMERIC_CHARS, field_sep_char)); + is_ambiguous_field_sep= MY_TEST(strchr(ESCAPE_CHARS, field_sep_char)); + is_unsafe_field_sep= MY_TEST(strchr(NUMERIC_CHARS, field_sep_char)); line_sep_char= (exchange->line_term->length() ? (int) (uchar) (*exchange->line_term)[0] : INT_MAX); if (!field_term_length) @@ -5307,7 +5310,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 && !table->table->s->is_gtid_slave_pos) + if (table->table->no_replicate) { /* The statement uses a table that is not replicated. @@ -5649,6 +5652,10 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id, /* Fetch the type code for the RowsEventT template parameter */ int const general_type_code= RowsEventT::TYPE_CODE; + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_transactional= 1; + /* There is no good place to set up the transactional data, so we have to do it here. @@ -5850,6 +5857,10 @@ int THD::binlog_write_row(TABLE* table, bool is_trans, size_t const len= pack_row(table, cols, row_data, record); + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_trans= 1; + Rows_log_event* const ev= binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt, len, is_trans, @@ -5889,6 +5900,10 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, size_t const after_size= pack_row(table, cols, after_row, after_record); + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_trans= 1; + /* Don't print debug messages when running valgrind since they can trigger false warnings. @@ -5937,6 +5952,10 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans, size_t const len= pack_row(table, cols, row_data, record); + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_trans= 1; + Rows_log_event* const ev= binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt, len, is_trans, @@ -5961,6 +5980,10 @@ int THD::binlog_remove_pending_rows_event(bool clear_maps, #endif DBUG_RETURN(0); + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_transactional= 1; + mysql_bin_log.remove_pending_rows_event(this, is_transactional); if (clear_maps) @@ -5984,6 +6007,10 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional) #endif DBUG_RETURN(0); + /* Ensure that all events in a GTID group are in the same cache */ + if (variables.option_bits & OPTION_GTID_BEGIN) + is_transactional= 1; + /* Mark the event as the last event of a statement if the stmt_end flag is set. @@ -6236,6 +6263,14 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, DBUG_ASSERT(query_arg && mysql_bin_log.is_open()); #endif + /* If this is withing a BEGIN ... COMMIT group, don't log it */ + if (variables.option_bits & OPTION_GTID_BEGIN) + { + direct= 0; + is_trans= 1; + } + DBUG_PRINT("info", ("is_trans: %d direct: %d", is_trans, direct)); + if (get_binlog_local_stmt_filter() == BINLOG_FILTER_SET) { /* @@ -6373,14 +6408,23 @@ bool THD::rgi_have_temporary_tables() } +void +wait_for_commit::reinit() +{ + subsequent_commits_list= NULL; + next_subsequent_commit= NULL; + waitee= NULL; + opaque_pointer= NULL; + wakeup_error= 0; + wakeup_subsequent_commits_running= false; +} + + wait_for_commit::wait_for_commit() - : subsequent_commits_list(0), next_subsequent_commit(0), waitee(0), - opaque_pointer(0), - waiting_for_commit(false), wakeup_error(0), - wakeup_subsequent_commits_running(false) { mysql_mutex_init(key_LOCK_wait_commit, &LOCK_wait_commit, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_wait_commit, &COND_wait_commit, 0); + reinit(); } @@ -6428,7 +6472,7 @@ wait_for_commit::wakeup(int wakeup_error) */ mysql_mutex_lock(&LOCK_wait_commit); - waiting_for_commit= false; + waitee= NULL; this->wakeup_error= wakeup_error; /* Note that it is critical that the mysql_cond_signal() here is done while @@ -6460,9 +6504,8 @@ wait_for_commit::wakeup(int wakeup_error) void wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee) { - waiting_for_commit= true; - wakeup_error= 0; DBUG_ASSERT(!this->waitee /* No prior registration allowed */); + wakeup_error= 0; this->waitee= waitee; mysql_mutex_lock(&waitee->LOCK_wait_commit); @@ -6472,7 +6515,7 @@ wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee) see comments on wakeup_subsequent_commits2() for details. */ if (waitee->wakeup_subsequent_commits_running) - waiting_for_commit= false; + this->waitee= NULL; else { /* @@ -6492,13 +6535,62 @@ wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee) returns immediately. */ int -wait_for_commit::wait_for_prior_commit2() +wait_for_commit::wait_for_prior_commit2(THD *thd) { + PSI_stage_info old_stage; + wait_for_commit *loc_waitee; + mysql_mutex_lock(&LOCK_wait_commit); - while (waiting_for_commit) + DEBUG_SYNC(thd, "wait_for_prior_commit_waiting"); + thd->ENTER_COND(&COND_wait_commit, &LOCK_wait_commit, + &stage_waiting_for_prior_transaction_to_commit, + &old_stage); + while ((loc_waitee= this->waitee) && !thd->check_killed()) mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit); - mysql_mutex_unlock(&LOCK_wait_commit); - waitee= NULL; + if (!loc_waitee) + { + if (wakeup_error) + my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); + goto end; + } + /* + Wait was interrupted by kill. We need to unregister our wait and give the + error. But if a wakeup is already in progress, then we must ignore the + kill and not give error, otherwise we get inconsistency between waitee and + waiter as to whether we succeed or fail (eg. we may roll back but waitee + might attempt to commit both us and any subsequent commits waiting for us). + */ + mysql_mutex_lock(&loc_waitee->LOCK_wait_commit); + if (loc_waitee->wakeup_subsequent_commits_running) + { + /* We are being woken up; ignore the kill and just wait. */ + mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit); + do + { + mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit); + } while (this->waitee); + if (wakeup_error) + my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); + goto end; + } + remove_from_list(&loc_waitee->subsequent_commits_list); + mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit); + this->waitee= NULL; + + wakeup_error= thd->killed_errno(); + if (!wakeup_error) + wakeup_error= ER_QUERY_INTERRUPTED; + my_message(wakeup_error, ER(wakeup_error), MYF(0)); + thd->EXIT_COND(&old_stage); + /* + Must do the DEBUG_SYNC() _after_ exit_cond(), as DEBUG_SYNC is not safe to + use within enter_cond/exit_cond. + */ + DEBUG_SYNC(thd, "wait_for_prior_commit_killed"); + return wakeup_error; + +end: + thd->EXIT_COND(&old_stage); return wakeup_error; } @@ -6581,11 +6673,11 @@ wait_for_commit::wakeup_subsequent_commits2(int wakeup_error) void wait_for_commit::unregister_wait_for_prior_commit2() { + wait_for_commit *loc_waitee; + mysql_mutex_lock(&LOCK_wait_commit); - if (waiting_for_commit) + if ((loc_waitee= this->waitee)) { - wait_for_commit *loc_waitee= this->waitee; - wait_for_commit **next_ptr_ptr, *cur; mysql_mutex_lock(&loc_waitee->LOCK_wait_commit); if (loc_waitee->wakeup_subsequent_commits_running) { @@ -6597,28 +6689,18 @@ wait_for_commit::unregister_wait_for_prior_commit2() See comments on wakeup_subsequent_commits2() for more details. */ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit); - while (waiting_for_commit) + while (this->waitee) mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit); } else { /* Remove ourselves from the list in the waitee. */ - next_ptr_ptr= &loc_waitee->subsequent_commits_list; - while ((cur= *next_ptr_ptr) != NULL) - { - if (cur == this) - { - *next_ptr_ptr= this->next_subsequent_commit; - break; - } - next_ptr_ptr= &cur->next_subsequent_commit; - } - waiting_for_commit= false; + remove_from_list(&loc_waitee->subsequent_commits_list); mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit); + this->waitee= NULL; } } mysql_mutex_unlock(&LOCK_wait_commit); - this->waitee= NULL; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 6144dd42a0a..ab11249cafd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -55,6 +55,7 @@ void set_thd_stage_info(void *thd, (thd)->enter_stage(& stage, NULL, __func__, __FILE__, __LINE__) #include "my_apc.h" +#include "rpl_gtid.h" #ifdef WITH_WSREP #include "wsrep_mysqld.h" @@ -131,6 +132,11 @@ enum enum_filetype { FILETYPE_CSV, FILETYPE_XML }; #define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30) #define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31) +/* Bits for different old style modes */ +#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0) +#define OLD_MODE_NO_PROGRESS_INFO (1 << 1) +#define OLD_MODE_ZERO_DATE_TIME_CAST (1 << 2) + extern char internal_table_name[2]; extern char empty_c_string[1]; extern LEX_STRING EMPTY_STR; @@ -510,6 +516,7 @@ typedef struct system_variables ulonglong long_query_time; ulonglong optimizer_switch; sql_mode_t sql_mode; ///< which non-standard SQL behaviour should be enabled + sql_mode_t old_behavior; ///< which old SQL behaviour should be enabled ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING ulonglong join_buff_space_limit; ulonglong log_slow_filter; @@ -1530,8 +1537,9 @@ public: void unlock_locked_tables(THD *thd); ~Locked_tables_list() { - unlock_locked_tables(0); + reset(); } + void reset(); bool init_locked_tables(THD *thd); TABLE_LIST *locked_tables() { return m_locked_tables; } void unlink_from_list(THD *thd, TABLE_LIST *table_list, @@ -1540,6 +1548,9 @@ public: MYSQL_LOCK *lock, size_t reopen_count); bool reopen_tables(THD *thd); + bool restore_lock(THD *thd, TABLE_LIST *dst_table_list, TABLE *table, + MYSQL_LOCK *lock); + void add_back_last_deleted_lock(TABLE_LIST *dst_table_list); }; @@ -1663,8 +1674,8 @@ struct wait_for_commit { /* The LOCK_wait_commit protects the fields subsequent_commits_list and - wakeup_subsequent_commits_running (for a waitee), and the flag - waiting_for_commit and associated COND_wait_commit (for a waiter). + wakeup_subsequent_commits_running (for a waitee), and the pointer + waiterr and associated COND_wait_commit (for a waiter). */ mysql_mutex_t LOCK_wait_commit; mysql_cond_t COND_wait_commit; @@ -1672,7 +1683,13 @@ struct wait_for_commit wait_for_commit *subsequent_commits_list; /* Link field for entries in subsequent_commits_list. */ wait_for_commit *next_subsequent_commit; - /* Our waitee, if we did register_wait_for_prior_commit(), else NULL. */ + /* + Our waitee, if we did register_wait_for_prior_commit(), and were not + yet woken up. Else NULL. + + When this is cleared for wakeup, the COND_wait_commit condition is + signalled. + */ wait_for_commit *waitee; /* Generic pointer for use by the transaction coordinator to optimise the @@ -1683,12 +1700,6 @@ struct wait_for_commit used by another transaction coordinator for similar purposes. */ void *opaque_pointer; - /* - The waiting_for_commit flag is cleared when a waiter has been woken - up. The COND_wait_commit condition is signalled when this has been - cleared. - */ - bool waiting_for_commit; /* The wakeup error code from the waitee. 0 means no error. */ int wakeup_error; /* @@ -1698,16 +1709,20 @@ struct wait_for_commit bool wakeup_subsequent_commits_running; void register_wait_for_prior_commit(wait_for_commit *waitee); - int wait_for_prior_commit() + int wait_for_prior_commit(THD *thd) { /* Quick inline check, to avoid function call and locking in the common case where no wakeup is registered, or a registered wait was already signalled. */ - if (waiting_for_commit) - return wait_for_prior_commit2(); + if (waitee) + return wait_for_prior_commit2(thd); else + { + if (wakeup_error) + my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); return wakeup_error; + } } void wakeup_subsequent_commits(int wakeup_error) { @@ -1728,18 +1743,38 @@ struct wait_for_commit } void unregister_wait_for_prior_commit() { - if (waiting_for_commit) + if (waitee) unregister_wait_for_prior_commit2(); } + /* + Remove a waiter from the list in the waitee. Used to unregister a wait. + The caller must be holding the locks of both waiter and waitee. + */ + void remove_from_list(wait_for_commit **next_ptr_ptr) + { + wait_for_commit *cur; + + while ((cur= *next_ptr_ptr) != NULL) + { + if (cur == this) + { + *next_ptr_ptr= this->next_subsequent_commit; + break; + } + next_ptr_ptr= &cur->next_subsequent_commit; + } + waitee= NULL; + } void wakeup(int wakeup_error); - int wait_for_prior_commit2(); + int wait_for_prior_commit2(THD *thd); void wakeup_subsequent_commits2(int wakeup_error); void unregister_wait_for_prior_commit2(); wait_for_commit(); ~wait_for_commit(); + void reinit(); }; @@ -1964,7 +1999,10 @@ public: uint in_sub_stmt; /* True when opt_userstat_running is set at start of query */ bool userstat_running; - /* True if we want to log all errors */ + /* + True if we have to log all errors. Are set by some engines to temporary + force errors to the error log. + */ bool log_all_errors; /* Do not set socket timeouts for wait_timeout (used with threadpool) */ @@ -2555,12 +2593,12 @@ public: */ LEX_STRING connection_name; char default_master_connection_buff[MAX_CONNECTION_NAME+1]; + uint8 password; /* 0, 1 or 2 */ + uint8 failed_com_change_user; bool slave_thread, one_shot_set; bool extra_port; /* If extra connection */ bool no_errors; - uint8 password; - uint8 failed_com_change_user; /** Set to TRUE if execution of the current compound statement @@ -2593,13 +2631,6 @@ public: /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; - /** - True if a slave error. Causes the slave to stop. Not the same - as the statement execution error (is_error()), since - a statement may be expected to return an error, e.g. because - it returned an error on master, and this is OK on the slave. - */ - bool is_slave_error; bool bootstrap, cleanup_done; /** is set if some thread specific value(s) used in a statement. */ @@ -2616,6 +2647,20 @@ public: /* set during loop of derived table processing */ bool derived_tables_processing; bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */ + /* True if we have to log the current statement */ + bool log_current_statement; + /** + True if a slave error. Causes the slave to stop. Not the same + as the statement execution error (is_error()), since + a statement may be expected to return an error, e.g. because + it returned an error on master, and this is OK on the slave. + */ + bool is_slave_error; + /* + In case of a slave, set to the error code the master got when executing + the query. 0 if no error on the master. + */ + int slave_expected_error; sp_rcontext *spcont; // SP runtime context sp_cache *sp_proc_cache; @@ -3634,12 +3679,7 @@ public: int wait_for_prior_commit() { if (wait_for_commit_ptr) - { - int err= wait_for_commit_ptr->wait_for_prior_commit(); - if (err) - my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); - return err; - } + return wait_for_commit_ptr->wait_for_prior_commit(this); return 0; } void wakeup_subsequent_commits(int wakeup_error) @@ -3691,6 +3731,12 @@ private: */ LEX_STRING invoker_user; LEX_STRING invoker_host; + + /* Protect against add/delete of temporary tables in parallel replication */ + void rgi_lock_temporary_tables(); + void rgi_unlock_temporary_tables(); + bool rgi_have_temporary_tables(); +public: /* Flag, mutex and condition for a thread to wait for a signal from another thread. @@ -3701,12 +3747,12 @@ private: bool wakeup_ready; mysql_mutex_t LOCK_wakeup_ready; mysql_cond_t COND_wakeup_ready; + /* + The GTID assigned to the last commit. If no GTID was assigned to any commit + so far, this is indicated by last_commit_gtid.seq_no == 0. + */ + rpl_gtid last_commit_gtid; - /* Protect against add/delete of temporary tables in parallel replication */ - void rgi_lock_temporary_tables(); - void rgi_unlock_temporary_tables(); - bool rgi_have_temporary_tables(); -public: inline void lock_temporary_tables() { if (rgi_slave) @@ -4058,6 +4104,8 @@ class select_create: public select_insert { MYSQL_LOCK *m_lock; /* m_lock or thd->extra_lock */ MYSQL_LOCK **m_plock; + bool exit_done; + public: select_create (TABLE_LIST *table_arg, HA_CREATE_INFO *create_info_par, @@ -4069,7 +4117,7 @@ public: create_info(create_info_par), select_tables(select_tables_arg), alter_info(alter_info_arg), - m_plock(NULL) + m_plock(NULL), exit_done(0) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); @@ -4469,7 +4517,7 @@ public: table.str= internal_table_name; table.length=1; } - bool is_derived_table() const { return test(sel); } + bool is_derived_table() const { return MY_TEST(sel); } inline void change_db(char *db_name) { db.str= db_name; db.length= (uint) strlen(db_name); diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 03d9a804372..5acc8b1e473 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2007, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab + Copyright (c) 2007, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2014, SkySQL 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 diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 8068901ebec..fcde2b4acbd 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -798,14 +798,8 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if ((my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db) == 0)) { for (table= tables; table; table= table->next_local) - { - if (check_if_log_table(table->db_length, table->db, - table->table_name_length, table->table_name, true)) - { - my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); + if (check_if_log_table(table, TRUE, "DROP")) goto exit; - } - } } /* Lock all tables and stored routines about to be dropped. */ @@ -835,7 +829,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, true, false, true, true))) + mysql_rm_table_no_locks(thd, tables, true, false, true, true, false))) { /* We temporarily disable the binary log while dropping the objects diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 840fd4947a6..014da5cfbaf 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -140,7 +140,7 @@ void Update_plan::save_explain_data_intern(Explain_query *query, explain->jtype= JT_NEXT; } - explain->using_where= test(select && select->cond); + explain->using_where= MY_TEST(select && select->cond); explain->using_filesort= using_filesort; explain->using_io_buffer= using_io_buffer; @@ -252,7 +252,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->map=1; query_plan.select_lex= &thd->lex->select_lex; query_plan.table= table; - query_plan.updating_a_view= test(table_list->view); + query_plan.updating_a_view= MY_TEST(table_list->view); if (mysql_prepare_delete(thd, table_list, select_lex->with_wild, select_lex->item_list, &conds)) @@ -291,7 +291,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); const_cond= (!conds || conds->const_item()); - safe_update=test(thd->variables.option_bits & OPTION_SAFE_UPDATES); + safe_update= MY_TEST(thd->variables.option_bits & OPTION_SAFE_UPDATES); if (safe_update && const_cond) { my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 712b1606c11..00ea0977a07 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -358,6 +358,14 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) if (derived->merged) return FALSE; + if (dt_select->uncacheable & UNCACHEABLE_RAND) + { + /* There is random function => fall back to materialization. */ + derived->change_refs_to_fields(); + derived->set_materialized_derived(); + return FALSE; + } + if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI || thd->lex->sql_command == SQLCOM_DELETE_MULTI) thd->save_prep_leaf_list= TRUE; @@ -604,11 +612,8 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) thd->lex->sql_command == SQLCOM_DELETE_MULTI)))) DBUG_RETURN(FALSE); - Query_arena *arena= thd->stmt_arena, backup; - if (arena->is_conventional()) - arena= 0; // For easier test - else - thd->set_n_backup_active_arena(arena, &backup); + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); SELECT_LEX *first_select= unit->first_select(); @@ -620,7 +625,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) if ((res= sl->handle_derived(lex, DT_PREPARE))) goto exit; - if (derived->outer_join) + if (derived->outer_join && sl->first_cond_optimization) { /* Mark that table is part of OUTER JOIN and fields may be NULL */ for (TABLE_LIST *cursor= (TABLE_LIST*) sl->table_list.first; @@ -708,7 +713,7 @@ exit: { TABLE *table= derived->table; table->derived_select_number= first_select->select_number; - table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE; + table->s->tmp_table= INTERNAL_TMP_TABLE; #ifndef NO_EMBEDDED_ACCESS_CHECKS if (derived->referencing_view) table->grant= derived->grant; diff --git a/sql/sql_error.cc b/sql/sql_error.cc index f382f18a983..fb1bb811c9d 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -464,6 +464,7 @@ Diagnostics_area::set_error_status(uint sql_errno, const Sql_condition *error_condition) { DBUG_ENTER("set_error_status"); + DBUG_PRINT("enter", ("error: %d", sql_errno)); /* Only allowed to report error if has not yet reported a success The only exception is when we flush the message to the client, diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 812a5dc1461..6b961ac2262 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -1135,7 +1135,7 @@ void mysql_ha_flush(THD *thd) ((hash_tables->table->mdl_ticket && hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) || (!hash_tables->table->s->tmp_table && - hash_tables->table->s->has_old_version()))) + hash_tables->table->s->tdc.flushed))) mysql_ha_close_table(hash_tables); } diff --git a/sql/sql_help.cc b/sql/sql_help.cc index d3c36e2c5d7..844810af0f4 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -1,5 +1,4 @@ -/* - Copyright (c) 2002, 2011, Oracle and/or its affiliates. +/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. 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 diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c93d1ae7a1e..7901e2b681e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -660,7 +660,7 @@ static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list) thd->lex->explain->add_insert_plan(explain); /* See Update_plan::updating_a_view for details */ - bool skip= test(table_list->view); + bool skip= MY_TEST(table_list->view); /* Save subquery children */ for (SELECT_LEX_UNIT *unit= thd->lex->select_lex.first_inner_unit(); @@ -1233,7 +1233,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) DBUG_ASSERT(view->table != 0 && view->field_translation != 0); - (void) bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0); + (void) my_bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0); bitmap_clear_all(&used_fields); view->contain_auto_increment= 0; @@ -1753,7 +1753,9 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (info->ignore && !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) { - table->file->print_error(error, MYF(ME_JUST_WARNING)); + if (!(thd->variables.old_behavior & + OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE)) + table->file->print_error(error, MYF(ME_JUST_WARNING)); goto ok_or_after_trg_err; } goto err; @@ -1881,7 +1883,9 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (!info->ignore || table->file->is_fatal_error(error, HA_CHECK_DUP)) goto err; - table->file->print_error(error, MYF(ME_JUST_WARNING)); + if (!(thd->variables.old_behavior & + OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE)) + table->file->print_error(error, MYF(ME_JUST_WARNING)); table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; } @@ -1933,7 +1937,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, if (table_list) { table_list= table_list->top_table(); - view= test(table_list->view); + view= MY_TEST(table_list->view); } if (view) { @@ -2432,6 +2436,10 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) goto error; dfield_ptr= copy->default_field; } + + /* Ensure we don't use the table list of the original table */ + copy->pos_in_table_list= 0; + /* Make a copy of all fields. The copied fields need to point into the copied record. This is done @@ -3068,7 +3076,7 @@ bool Delayed_insert::handle_inserts(void) THD_STAGE_INFO(&thd, stage_insert); max_rows= delayed_insert_limit; - if (thd.killed || table->s->has_old_version()) + if (thd.killed || table->s->tdc.flushed) { thd.killed= KILL_SYSTEM_THREAD; max_rows= ULONG_MAX; // Do as much as possible @@ -3565,7 +3573,8 @@ int select_insert::prepare2(void) { DBUG_ENTER("select_insert::prepare2"); if (thd->lex->current_select->options & OPTION_BUFFER_RESULT && - thd->locked_tables_mode <= LTM_LOCK_TABLES) + thd->locked_tables_mode <= LTM_LOCK_TABLES && + !thd->lex->describe) table->file->ha_start_bulk_insert((ha_rows) 0); DBUG_RETURN(0); } @@ -3816,7 +3825,8 @@ void select_insert::abort_result_set() { */ changed= (info.copied || info.deleted || info.updated); transactional_table= table->file->has_transactions(); - if (thd->transaction.stmt.modified_non_trans_table) + if (thd->transaction.stmt.modified_non_trans_table || + thd->log_current_statement) { if (!can_rollback_data()) thd->transaction.all.modified_non_trans_table= TRUE; @@ -3942,6 +3952,16 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DEBUG_SYNC(thd,"create_table_select_before_create"); + /* Check if LOCK TABLES + CREATE OR REPLACE of existing normal table*/ + if (thd->locked_tables_mode && create_table->table && + !create_info->tmp_table()) + { + /* Remember information about the locked table */ + create_info->pos_in_locked_tables= + create_table->table->pos_in_locked_tables; + create_info->mdl_ticket= create_table->table->mdl_ticket; + } + /* Create and lock table. @@ -3958,52 +3978,63 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, TABLE, which is a wrong order. So we keep binary logging disabled when we open_table(). */ + + if (!mysql_create_table_no_lock(thd, create_table->db, + create_table->table_name, + create_info, alter_info, NULL, + select_field_count)) { - if (!mysql_create_table_no_lock(thd, create_table->db, - create_table->table_name, - create_info, alter_info, NULL, - select_field_count)) + DEBUG_SYNC(thd,"create_table_select_before_open"); + + /* + If we had a temporary table or a table used with LOCK TABLES, + it was closed by mysql_create() + */ + create_table->table= 0; + + if (!create_info->tmp_table()) { - DEBUG_SYNC(thd,"create_table_select_before_open"); + Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); + TABLE_LIST::enum_open_strategy save_open_strategy; - if (!create_info->tmp_table()) - { - Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); - /* - Here we open the destination table, on which we already have - an exclusive metadata lock. - */ - if (open_table(thd, create_table, thd->mem_root, &ot_ctx)) - { - quick_rm_table(thd, create_info->db_type, create_table->db, - table_case_name(create_info, create_table->table_name), - 0); - } - else - table= create_table->table; - } - else + /* Force the newly created table to be opened */ + save_open_strategy= create_table->open_strategy; + create_table->open_strategy= TABLE_LIST::OPEN_NORMAL; + /* + Here we open the destination table, on which we already have + an exclusive metadata lock. + */ + if (open_table(thd, create_table, thd->mem_root, &ot_ctx)) { - if (open_temporary_table(thd, create_table)) - { - /* - This shouldn't happen as creation of temporary table should make - it preparable for open. Anyway we can't drop temporary table if - we are unable to find it. - */ - DBUG_ASSERT(0); - } - else - table= create_table->table; + quick_rm_table(thd, create_info->db_type, create_table->db, + table_case_name(create_info, create_table->table_name), + 0); } + /* Restore */ + create_table->open_strategy= save_open_strategy; } - if (!table) // open failed + else { - if (!thd->is_error()) // CREATE ... IF NOT EXISTS - my_ok(thd); // succeed, but did nothing - DBUG_RETURN(0); + if (open_temporary_table(thd, create_table)) + { + /* + This shouldn't happen as creation of temporary table should make + it preparable for open. Anyway we can't drop temporary table if + we are unable to find it. + */ + DBUG_ASSERT(0); + } } } + else + create_table->table= 0; // Create failed + + if (!(table= create_table->table)) + { + if (!thd->is_error()) // CREATE ... IF NOT EXISTS + my_ok(thd); // succeed, but did nothing + DBUG_RETURN(0); + } DEBUG_SYNC(thd,"create_table_select_before_lock"); @@ -4020,7 +4051,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, /* purecov: begin tested */ /* This can happen in innodb when you get a deadlock when using same table - in insert and select + in insert and select or when you run out of memory. */ my_error(ER_CANT_LOCK, MYF(0), my_errno); if (*lock) @@ -4118,8 +4149,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) thd->binlog_start_trans_and_stmt(); } - DBUG_ASSERT(create_table->table == NULL); - DEBUG_SYNC(thd,"create_table_select_before_check_if_exists"); if (!(table= create_table_from_items(thd, create_info, create_table, @@ -4207,7 +4236,9 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) query.length(0); // Have to zero it since constructor doesn't result= store_create_info(thd, &tmp_table_list, &query, create_info, - /* show_database */ TRUE); + /* show_database */ TRUE, + MY_TEST(create_info->options & + HA_LEX_CREATE_REPLACE)); DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */ #ifdef WITH_WSREP @@ -4269,19 +4300,22 @@ void select_create::send_error(uint errcode,const char *err) bool select_create::send_eof() { - bool tmp=select_insert::send_eof(); - if (tmp) + if (select_insert::send_eof()) + { abort_result_set(); - else + return 1; + } + + exit_done= 1; // Avoid double calls + /* + Do an implicit commit at end of statement for non-temporary + tables. This can fail, but we should unlock the table + nevertheless. + */ + if (!table->s->tmp_table) { - /* - Do an implicit commit at end of statement for non-temporary - tables. This can fail, but we should unlock the table - nevertheless. - */ - if (!table->s->tmp_table) - { - trans_commit_stmt(thd); + trans_commit_stmt(thd); + if (!(thd->variables.option_bits & OPTION_GTID_BEGIN)) trans_commit_implicit(thd); #ifdef WITH_WSREP mysql_mutex_lock(&thd->LOCK_wsrep_thd); @@ -4295,25 +4329,52 @@ bool select_create::send_eof() } mysql_mutex_unlock(&thd->LOCK_wsrep_thd); #endif /* WITH_WSREP */ - } + } + else if (!thd->is_current_stmt_binlog_format_row()) + table->s->table_creation_was_logged= 1; - table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); - if (m_plock) + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); + + if (m_plock) + { + MYSQL_LOCK *lock= *m_plock; + *m_plock= NULL; + m_plock= NULL; + + if (create_info->pos_in_locked_tables) { - mysql_unlock_tables(thd, *m_plock); - *m_plock= NULL; - m_plock= NULL; + /* + If we are under lock tables, we have created a table that was + originally locked. We should add back the lock to ensure that + all tables in the thd->open_list are locked! + */ + table->mdl_ticket= create_info->mdl_ticket; + + /* The following should never fail, except if out of memory */ + if (!thd->locked_tables_list.restore_lock(thd, + create_info-> + pos_in_locked_tables, + table, lock)) + return 0; // ok + /* Fail. Continue without locking the table */ } + mysql_unlock_tables(thd, lock); } - return tmp; + return 0; } void select_create::abort_result_set() { + ulonglong save_option_bits; DBUG_ENTER("select_create::abort_result_set"); + /* Avoid double calls, could happen in case of out of memory on cleanup */ + if (exit_done) + DBUG_VOID_RETURN; + exit_done= 1; + /* In select_insert::abort_result_set() we roll back the statement, including truncating the transaction cache of the binary log. To do this, we @@ -4328,11 +4389,18 @@ void select_create::abort_result_set() We also roll back the statement regardless of whether the creation of the table succeeded or not, since we need to reset the binary log state. + + However if there was an orignal table that was deleted, as part of + create or replace table, then we must log the statement. */ - tmp_disable_binlog(thd); + + save_option_bits= thd->variables.option_bits; + if (!(thd->log_current_statement)) + thd->variables.option_bits&= ~OPTION_BIN_LOG; select_insert::abort_result_set(); thd->transaction.stmt.modified_non_trans_table= FALSE; - reenable_binlog(thd); + thd->variables.option_bits= save_option_bits; + /* possible error of writing binary log is ignored deliberately */ (void) thd->binlog_flush_pending_rows_event(TRUE, TRUE); diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 3b39ebf145f..abd23c344c2 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -222,8 +222,8 @@ void JOIN_CACHE::calc_record_fields() for (; tab != join_tab ; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { tab->calc_used_field_length(FALSE); - flag_fields+= test(tab->used_null_fields || tab->used_uneven_bit_fields); - flag_fields+= test(tab->table->maybe_null); + flag_fields+= MY_TEST(tab->used_null_fields || tab->used_uneven_bit_fields); + flag_fields+= MY_TEST(tab->table->maybe_null); fields+= tab->used_fields; blobs+= tab->used_blobs; } @@ -736,7 +736,7 @@ void JOIN_CACHE::set_constants() uint JOIN_CACHE::get_record_max_affix_length() { uint len= get_prefix_length() + - test(with_match_flag) + + MY_TEST(with_match_flag) + size_of_fld_ofs * data_field_count; return len; } @@ -1012,7 +1012,7 @@ int JOIN_CACHE::realloc_buffer() { int rc; free(); - rc= test(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC)))); + rc= MY_TEST(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC)))); reset(TRUE); return rc; } @@ -1766,7 +1766,7 @@ uint JOIN_CACHE::read_flag_fields() CACHE_FIELD *copy_end= copy+flag_fields; if (with_match_flag) { - copy->str[0]= test((Match_flag) pos[0] == MATCH_FOUND); + copy->str[0]= MY_TEST((Match_flag) pos[0] == MATCH_FOUND); pos+= copy->length; copy++; } @@ -2520,7 +2520,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) if (!records) DBUG_RETURN(NESTED_LOOP_OK); - cnt= records - (is_key_access() ? 0 : test(skip_last)); + cnt= records - (is_key_access() ? 0 : MY_TEST(skip_last)); /* This function may be called only for inner tables of outer joins */ DBUG_ASSERT(join_tab->first_inner); @@ -2570,7 +2570,7 @@ finish: void JOIN_CACHE::save_explain_data(struct st_explain_bka_type *explain) { - explain->incremental= test(prev_cache); + explain->incremental= MY_TEST(prev_cache); switch (get_join_alg()) { case BNL_JOIN_ALG: @@ -2792,7 +2792,7 @@ int JOIN_CACHE_HASHED::realloc_buffer() { int rc; free(); - rc= test(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC)))); + rc= MY_TEST(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC)))); init_hash_table(); reset(TRUE); return rc; @@ -3472,7 +3472,7 @@ bool JOIN_CACHE_BNL::prepare_look_for_matches(bool skip_last) if (!records) return TRUE; reset(FALSE); - rem_records= records-test(skip_last); + rem_records= records - MY_TEST(skip_last); return rem_records == 0; } @@ -4588,7 +4588,7 @@ int JOIN_CACHE_BKAH::init() { bool check_only_first_match= join_tab->check_only_first_match(); - no_association= test(mrr_mode & HA_MRR_NO_ASSOCIATION); + no_association= MY_TEST(mrr_mode & HA_MRR_NO_ASSOCIATION); RANGE_SEQ_IF rs_funcs= { bka_range_seq_key_info, bkah_range_seq_init, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e5b8090dfbc..26cef090b83 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -299,7 +299,7 @@ Lex_input_stream::reset(char *buffer, unsigned int length) m_cpp_utf8_processed_ptr= NULL; next_state= MY_LEX_START; found_semicolon= NULL; - ignore_space= test(m_thd->variables.sql_mode & MODE_IGNORE_SPACE); + ignore_space= MY_TEST(m_thd->variables.sql_mode & MODE_IGNORE_SPACE); stmt_prepare_mode= FALSE; multi_statements= TRUE; in_comment=NO_COMMENT; @@ -497,13 +497,13 @@ void lex_start(THD *thd) lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; lex->select_lex.group_list.empty(); lex->select_lex.order_list.empty(); + lex->select_lex.gorder_list.empty(); lex->m_sql_cmd= NULL; lex->duplicates= DUP_ERROR; lex->ignore= 0; lex->spname= NULL; lex->sphead= NULL; lex->spcont= NULL; - lex->m_sql_cmd= NULL; lex->proc_list.first= 0; lex->escape_used= FALSE; lex->query_tables= 0; @@ -2120,14 +2120,15 @@ void st_select_lex_unit::exclude_tree() this to 'last' as dependent SYNOPSIS - last - pointer to last st_select_lex struct, before wich all + last - pointer to last st_select_lex struct, before which all st_select_lex have to be marked as dependent NOTE 'last' should be reachable from this st_select_lex_node */ -bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency) +bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, + Item *dependency) { DBUG_ASSERT(this != last); @@ -2320,7 +2321,10 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) If we need a bigger array, we must allocate a new one. */ if (ref_pointer_array_size >= n_elems) + { + DBUG_PRINT("info", ("reusing old ref_array")); return false; + } } ref_pointer_array= static_cast<Item**>(arena->alloc(sizeof(Item*) * n_elems)); if (ref_pointer_array != NULL) @@ -2586,7 +2590,9 @@ bool LEX::can_be_merged() // TODO: do not forget implement case when select_lex.table_list.elements==0 /* find non VIEW subqueries/unions */ - bool selects_allow_merge= select_lex.next_select() == 0; + bool selects_allow_merge= (select_lex.next_select() == 0 && + !(select_lex.uncacheable & + UNCACHEABLE_RAND)); if (selects_allow_merge) { for (SELECT_LEX_UNIT *tmp_unit= select_lex.first_inner_unit(); @@ -3078,7 +3084,7 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local) /* and from local list if it is not empty */ - if ((*link_to_local= test(select_lex.table_list.first))) + if ((*link_to_local= MY_TEST(select_lex.table_list.first))) { select_lex.context.table_list= select_lex.context.first_name_resolution_table= first->next_local; @@ -3323,6 +3329,7 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl) void st_select_lex::fix_prepare_information(THD *thd, Item **conds, Item **having_conds) { + DBUG_ENTER("st_select_lex::fix_prepare_information"); if (!thd->stmt_arena->is_conventional() && first_execution) { first_execution= 0; @@ -3351,6 +3358,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds, } fix_prepare_info_in_table_list(thd, table_list.first); } + DBUG_VOID_RETURN; } @@ -3827,7 +3835,7 @@ void SELECT_LEX::update_used_tables() do { bool maybe_null; - if ((maybe_null= test(embedding->outer_join))) + if ((maybe_null= MY_TEST(embedding->outer_join))) { tl->table->maybe_null= maybe_null; break; @@ -3904,37 +3912,40 @@ void st_select_lex::update_correlated_cache() while ((tl= ti++)) { if (tl->on_expr) - is_correlated|= test(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT); for (TABLE_LIST *embedding= tl->embedding ; embedding ; embedding= embedding->embedding) { if (embedding->on_expr) - is_correlated|= test(embedding->on_expr->used_tables() & - OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST(embedding->on_expr->used_tables() & + OUTER_REF_TABLE_BIT); } } if (join->conds) - is_correlated|= test(join->conds->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST(join->conds->used_tables() & OUTER_REF_TABLE_BIT); if (join->having) - is_correlated|= test(join->having->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST(join->having->used_tables() & OUTER_REF_TABLE_BIT); if (join->tmp_having) - is_correlated|= test(join->tmp_having->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST(join->tmp_having->used_tables() & + OUTER_REF_TABLE_BIT); Item *item; List_iterator_fast<Item> it(join->fields_list); while ((item= it++)) - is_correlated|= test(item->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST(item->used_tables() & OUTER_REF_TABLE_BIT); for (ORDER *order= group_list.first; order; order= order->next) - is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST((*order->item)->used_tables() & + OUTER_REF_TABLE_BIT); if (!master_unit()->is_union()) { for (ORDER *order= order_list.first; order; order= order->next) - is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); + is_correlated|= MY_TEST((*order->item)->used_tables() & + OUTER_REF_TABLE_BIT); } if (!is_correlated) @@ -4088,11 +4099,8 @@ void SELECT_LEX::mark_const_derived(bool empty) bool st_select_lex::save_leaf_tables(THD *thd) { - Query_arena *arena= thd->stmt_arena, backup; - if (arena->is_conventional()) - arena= 0; - else - thd->set_n_backup_active_arena(arena, &backup); + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); List_iterator_fast<TABLE_LIST> li(leaf_tables); TABLE_LIST *table; @@ -4120,10 +4128,7 @@ bool st_select_lex::save_prep_leaf_tables(THD *thd) return 0; Query_arena *arena= thd->stmt_arena, backup; - if (arena->is_conventional()) - arena= 0; - else - thd->set_n_backup_active_arena(arena, &backup); + arena= thd->activate_stmt_arena_if_needed(&backup); List_iterator_fast<TABLE_LIST> li(leaf_tables); TABLE_LIST *table; @@ -4229,7 +4234,7 @@ int st_select_lex_unit::save_union_explain(Explain_query *output) eu->add_select(sl->select_number); eu->fake_select_type= "UNION RESULT"; - eu->using_filesort= test(global_parameters->order_list.first); + eu->using_filesort= MY_TEST(global_parameters->order_list.first); // Save the UNION node output->add_node(eu); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 479fa81edd7..13cbcdd9f08 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1152,7 +1152,7 @@ public: } bool requires_prelocking() { - return test(query_tables_own_last); + return MY_TEST(query_tables_own_last); } void mark_as_requiring_prelocking(TABLE_LIST **tables_own_last) { @@ -2641,6 +2641,7 @@ struct LEX: public Query_tables_list sl->uncacheable|= cause; un->uncacheable|= cause; } + select_lex.uncacheable|= cause; } void set_trg_event_type_for_tables(); diff --git a/sql/sql_lifo_buffer.h b/sql/sql_lifo_buffer.h index 5b7ddf35474..feec4aeb4c2 100644 --- a/sql/sql_lifo_buffer.h +++ b/sql/sql_lifo_buffer.h @@ -83,7 +83,8 @@ public: { start= start_arg; end= end_arg; - TRASH(start, end - start); + if (end != start) + TRASH(start, end - start); reset(); } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 281d1de7877..d112b3e689a 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -258,7 +258,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, */ if (unique_table(thd, table_list, table_list->next_global, 0)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); + my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name, + "LOAD DATA"); DBUG_RETURN(TRUE); } @@ -461,7 +462,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } thd_proc_info(thd, "reading file"); - if (!(error=test(read_info.error))) + if (!(error= MY_TEST(read_info.error))) { table->next_number_field=table->found_next_number_field; if (ignore || @@ -904,7 +905,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->get_stmt_da()->inc_current_row_for_warning(); continue_loop:; } - DBUG_RETURN(test(read_info.error)); + DBUG_RETURN(MY_TEST(read_info.error)); } @@ -1129,7 +1130,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->get_stmt_da()->inc_current_row_for_warning(); continue_loop:; } - DBUG_RETURN(test(read_info.error)); + DBUG_RETURN(MY_TEST(read_info.error)); } @@ -1297,7 +1298,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->get_stmt_da()->inc_current_row_for_warning(); continue_loop:; } - DBUG_RETURN(test(read_info.error) || thd->is_error()); + DBUG_RETURN(MY_TEST(read_info.error) || thd->is_error()); } /* load xml end */ diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 0771f569f35..f13448ca46e 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2013, 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 diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 863658f7878..04b20a7d40f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -201,6 +201,8 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) @param thd Thread handle. @param mask Bitmask used for the SQL command match. + @return 0 No implicit commit + @return 1 Do a commit */ static bool stmt_causes_implicit_commit(THD *thd, uint mask) { @@ -213,12 +215,22 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask) switch (lex->sql_command) { case SQLCOM_DROP_TABLE: - skip= lex->drop_temporary; + skip= (lex->drop_temporary || + (thd->variables.option_bits & OPTION_GTID_BEGIN)); break; case SQLCOM_ALTER_TABLE: + /* If ALTER TABLE of non-temporary table, do implicit commit */ + skip= (lex->create_info.tmp_table()); + break; case SQLCOM_CREATE_TABLE: - /* If CREATE TABLE of non-temporary table, do implicit commit */ - skip= lex->create_info.tmp_table(); + /* + If CREATE TABLE of non-temporary table and the table is not part + if a BEGIN GTID ... COMMIT group, do a implicit commit. + This ensures that CREATE ... SELECT will in the same GTID group on the + master and slave. + */ + skip= (lex->create_info.tmp_table() || + (thd->variables.option_bits & OPTION_GTID_BEGIN)); break; case SQLCOM_SET_OPTION: skip= lex->autocommit ? FALSE : TRUE; @@ -2440,6 +2452,7 @@ mysql_execute_command(THD *thd) Rpl_filter *rpl_filter= thd->rpl_filter; #endif DBUG_ENTER("mysql_execute_command"); + #ifdef WITH_PARTITION_STORAGE_ENGINE thd->work_part_info= 0; #endif @@ -2681,8 +2694,8 @@ mysql_execute_command(THD *thd) } #endif /* WITH_WSREP */ status_var_increment(thd->status_var.com_stat[lex->sql_command]); - thd->progress.report_to_client= test(sql_command_flags[lex->sql_command] & - CF_REPORT_PROGRESS); + thd->progress.report_to_client= MY_TEST(sql_command_flags[lex->sql_command] & + CF_REPORT_PROGRESS); DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE); @@ -2717,15 +2730,18 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(! thd->in_sub_stmt); /* Statement transaction still should not be started. */ DBUG_ASSERT(thd->transaction.stmt.is_empty()); - /* Commit the normal transaction if one is active. */ - if (trans_commit_implicit(thd)) + if (!(thd->variables.option_bits & OPTION_GTID_BEGIN)) { + /* Commit the normal transaction if one is active. */ + if (trans_commit_implicit(thd)) + { + thd->mdl_context.release_transactional_locks(); + WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id); + goto error; + } + /* Release metadata locks acquired in this transaction. */ thd->mdl_context.release_transactional_locks(); - WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id); - goto error; } - /* Release metadata locks acquired in this transaction. */ - thd->mdl_context.release_transactional_locks(); } #ifndef DBUG_OFF @@ -3124,6 +3140,7 @@ case SQLCOM_PREPARE: goto end_with_restore_list; } + /* Check privileges */ if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; @@ -3158,6 +3175,23 @@ case SQLCOM_PREPARE: create_info.table_charset= 0; } + /* + For CREATE TABLE we should not open the table even if it exists. + If the table exists, we should either not create it or replace it + */ + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + + /* + If we are a slave, we should add OR REPLACE if we don't have + IF EXISTS. This will help a slave to recover from + CREATE TABLE OR EXISTS failures by dropping the table and + retrying the create. + */ + if (thd->slave_thread && + slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT && + !(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)) + create_info.options|= HA_LEX_CREATE_REPLACE; + #ifdef WITH_PARTITION_STORAGE_ENGINE { partition_info *part_info= thd->lex->part_info; @@ -3246,28 +3280,25 @@ case SQLCOM_PREPARE: /* Got error or warning. Set res to 1 if error */ if (!(res= thd->is_error())) my_ok(thd); // CREATE ... IF NOT EXISTS + goto end_with_restore_list; } - else + + /* Ensure we don't try to create something from which we select from */ + if ((create_info.options & HA_LEX_CREATE_REPLACE) && + !create_info.tmp_table()) { - /* The table already exists */ - if (create_table->table) + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, lex->query_tables, + lex->query_tables->next_global, + 0))) { - if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS) - { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR, - ER(ER_TABLE_EXISTS_ERROR), - create_info.alias); - my_ok(thd); - } - else - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias); - res= 1; - } + update_non_unique_table_error(lex->query_tables, "CREATE", + duplicate); + res= TRUE; goto end_with_restore_list; } - + } + { /* Remove target table from main select and name resolution context. This can't be done earlier as it will break view merging in @@ -3275,9 +3306,8 @@ case SQLCOM_PREPARE: */ lex->unlink_first_table(&link_to_local); - /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ - if (create_info.tmp_table()) - thd->variables.option_bits|= OPTION_KEEP_LOG; + /* Store reference to table in case of LOCK TABLES */ + create_info.table= create_table->table; /* select_create is currently not re-execution friendly and @@ -3295,18 +3325,18 @@ case SQLCOM_PREPARE: CREATE from SELECT give its SELECT_LEX for SELECT, and item_list belong to SELECT */ - res= handle_select(thd, lex, result, 0); + if (!(res= handle_select(thd, lex, result, 0))) + { + if (create_info.tmp_table()) + thd->variables.option_bits|= OPTION_KEEP_LOG; + } delete result; } - lex->link_first_table_back(create_table, link_to_local); } } else { - /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ - if (create_info.tmp_table()) - thd->variables.option_bits|= OPTION_KEEP_LOG; /* regular create */ if (create_info.options & HA_LEX_CREATE_TABLE_LIKE) { @@ -3330,7 +3360,12 @@ case SQLCOM_PREPARE: &create_info, &alter_info); } if (!res) + { + /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ + if (create_info.tmp_table()) + thd->variables.option_bits|= OPTION_KEEP_LOG; my_ok(thd); + } } end_with_restore_list: @@ -3780,7 +3815,7 @@ end_with_restore_list: case SQLCOM_INSERT_SELECT: { select_result *sel_result; - bool explain= test(lex->describe); + bool explain= MY_TEST(lex->describe); DBUG_ASSERT(first_table == all_tables && first_table != 0); if ((res= insert_precheck(thd, all_tables))) break; @@ -3903,7 +3938,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - bool explain= test(lex->describe); + bool explain= MY_TEST(lex->describe); multi_delete *result; if ((res= multi_delete_precheck(thd, all_tables))) @@ -3987,6 +4022,16 @@ end_with_restore_list: } } #endif /* WITH_WSREP */ + /* + If we are a slave, we should add IF EXISTS if the query executed + on the master without an error. This will help a slave to + recover from multi-table DROP TABLE that was aborted in the + middle. + */ + if (thd->slave_thread && !thd->slave_expected_error && + slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT) + lex->check_exists= 1; + /* DDL and binlog write order are protected by metadata locks. */ res= mysql_rm_table(thd, first_table, lex->check_exists, lex->drop_temporary); @@ -4570,14 +4615,16 @@ end_with_restore_list: if (check_global_access(thd,RELOAD_ACL)) goto error; - if (first_table && lex->type & REFRESH_READ_LOCK) + if (first_table && lex->type & (REFRESH_READ_LOCK|REFRESH_FOR_EXPORT)) { /* Check table-level privileges. */ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; + if (flush_tables_with_read_lock(thd, all_tables)) goto error; + my_ok(thd); break; } @@ -4777,6 +4824,7 @@ end_with_restore_list: bool tx_release= (lex->tx_release == TVL_YES || (thd->variables.completion_type == 2 && lex->tx_release != TVL_NO)); + if (trans_rollback(thd)) { thd->mdl_context.release_transactional_locks(); @@ -5498,6 +5546,7 @@ finish: DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() || thd->in_multi_stmt_transaction_mode()); + lex->unit.cleanup(); if (! thd->in_sub_stmt) { @@ -5529,7 +5578,6 @@ finish: ha_maria::implicit_commit(thd, FALSE); #endif } - lex->unit.cleanup(); /* Free tables */ THD_STAGE_INFO(thd, stage_closing_tables); @@ -5562,12 +5610,15 @@ finish: { /* No transaction control allowed in sub-statements. */ DBUG_ASSERT(! thd->in_sub_stmt); - /* If commit fails, we should be able to reset the OK status. */ - thd->get_stmt_da()->set_overwrite_status(true); - /* Commit the normal transaction if one is active. */ - trans_commit_implicit(thd); - thd->get_stmt_da()->set_overwrite_status(false); - thd->mdl_context.release_transactional_locks(); + if (!(thd->variables.option_bits & OPTION_GTID_BEGIN)) + { + /* If commit fails, we should be able to reset the OK status. */ + thd->get_stmt_da()->set_overwrite_status(true); + /* Commit the normal transaction if one is active. */ + trans_commit_implicit(thd); + thd->get_stmt_da()->set_overwrite_status(false); + thd->mdl_context.release_transactional_locks(); + } } else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) { @@ -6549,6 +6600,8 @@ void THD::reset_for_next_command() thd->query_start_used= 0; thd->query_start_sec_part_used= 0; thd->is_fatal_error= thd->time_zone_used= 0; + thd->log_current_statement= 0; + /* Clear the status flag that are expected to be cleared at the beginning of each SQL statement. @@ -7165,6 +7218,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc) order->free_me=0; order->used=0; order->counter_used= 0; + order->fast_field_copier_setup= 0; list.link_in_list(order, &order->next); DBUG_RETURN(0); } @@ -7210,7 +7264,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (!table) DBUG_RETURN(0); // End of memory alias_str= alias ? alias->str : table->table.str; - if (!test(table_options & TL_OPTION_ALIAS) && + if (!MY_TEST(table_options & TL_OPTION_ALIAS) && check_table_name(table->table.str, table->table.length, FALSE)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str); @@ -7255,10 +7309,10 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->table_name=table->table.str; ptr->table_name_length=table->table.length; ptr->lock_type= lock_type; - ptr->updating= test(table_options & TL_OPTION_UPDATING); + ptr->updating= MY_TEST(table_options & TL_OPTION_UPDATING); /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */ - ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); - ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); + ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX); + ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length)) { @@ -7292,7 +7346,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->schema_table= schema_table; } ptr->select_lex= lex->current_select; - ptr->cacheable_table= 1; + /* + We can't cache internal temporary tables between prepares as the + table may be deleted before next exection. + */ + ptr->cacheable_table= !table->is_derived_table(); ptr->index_hints= index_hints_arg; ptr->option= option ? option->str : 0; /* check that used name is unique */ @@ -7349,7 +7407,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, lex->add_to_query_tables(ptr); // Pure table aliases do not need to be locked: - if (!test(table_options & TL_OPTION_ALIAS)) + if (!MY_TEST(table_options & TL_OPTION_ALIAS)) { ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type, MDL_TRANSACTION); @@ -8515,6 +8573,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, want_priv= lex->create_info.tmp_table() ? CREATE_TMP_ACL : (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0)); + /* CREATE OR REPLACE on not temporary tables require DROP_ACL */ + if ((lex->create_info.options & HA_LEX_CREATE_REPLACE) && + !lex->create_info.tmp_table()) + want_priv|= DROP_ACL; + if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, &create_table->grant.m_internal, @@ -8551,8 +8614,8 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, - For temporary MERGE tables we do not track if their child tables are base or temporary. As result we can't guarantee that privilege check - which was done in presence of temporary child will stay relevant later - as this temporary table might be removed. + which was done in presence of temporary child will stay relevant + later as this temporary table might be removed. If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for the underlying *base* tables, it would create a security breach as in @@ -8583,6 +8646,12 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, } error= FALSE; + /* + For CREATE TABLE we should not open the table even if it exists. + If the table exists, we should either not create it or replace it + */ + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + err: DBUG_RETURN(error); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index c2a7a184c20..773ede9edee 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -200,7 +200,7 @@ bool check_global_access(THD *thd, ulong want_access, bool no_errors= false); inline bool is_supported_parser_charset(CHARSET_INFO *cs) { - return test(cs->mbminlen == 1); + return MY_TEST(cs->mbminlen == 1); } #ifdef WITH_WSREP diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 3e19a33ee32..a9846df3b02 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -651,7 +651,7 @@ static bool create_full_part_field_array(THD *thd, TABLE *table, result= TRUE; goto end; } - if (bitmap_init(&part_info->full_part_field_set, bitmap_buf, + if (my_bitmap_init(&part_info->full_part_field_set, bitmap_buf, table->s->fields, FALSE)) { mem_alloc_error(table->s->fields); @@ -1230,9 +1230,9 @@ static bool set_up_partition_bitmaps(THD *thd, partition_info *part_info) mem_alloc_error(bitmap_bytes * 2); DBUG_RETURN(TRUE); } - bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE); + my_bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE); /* Use the second half of the allocated buffer for lock_partitions */ - bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4), + my_bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4), bitmap_bits, FALSE); part_info->bitmaps_are_initialized= TRUE; part_info->set_partition_bitmaps(NULL); @@ -3308,7 +3308,7 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info, } else { - DBUG_RETURN(list_index + test(left_endpoint ^ include_endpoint)); + DBUG_RETURN(list_index + MY_TEST(left_endpoint ^ include_endpoint)); } } while (max_list_index >= min_list_index); notfound: @@ -5225,6 +5225,8 @@ that are reorganised. } else if (alter_info->flags & Alter_info::ALTER_REBUILD_PARTITION) { + set_engine_all_partitions(tab_part_info, + tab_part_info->default_engine_type); if (set_part_state(alter_info, tab_part_info, PART_CHANGED)) { my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD"); @@ -5781,7 +5783,7 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) if (mysql_trans_commit_alter_copy_data(thd)) error= 1; /* The error has been reported */ - DBUG_RETURN(test(error)); + DBUG_RETURN(MY_TEST(error)); } @@ -7890,7 +7892,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, index-in-ordered-array-of-list-constants (for LIST) space. */ store_key_image_to_rec(field, min_value, field_len); - bool include_endp= !test(flags & NEAR_MIN); + bool include_endp= !MY_TEST(flags & NEAR_MIN); part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp); if (!can_match_multiple_values && part_info->part_expr->null_value) { @@ -7925,7 +7927,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, else { store_key_image_to_rec(field, max_value, field_len); - bool include_endp= !test(flags & NEAR_MAX); + bool include_endp= !MY_TEST(flags & NEAR_MAX); part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp); if (check_zero_dates && !zero_in_start_date && @@ -8092,8 +8094,8 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, if ((ulonglong)b - (ulonglong)a == ~0ULL) DBUG_RETURN(-1); - a += test(flags & NEAR_MIN); - b += test(!(flags & NEAR_MAX)); + a+= MY_TEST(flags & NEAR_MIN); + b+= MY_TEST(!(flags & NEAR_MAX)); ulonglong n_values= b - a; /* diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 29ca86fa274..8c59febeb77 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2013, 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 @@ -506,13 +506,8 @@ bool Sql_cmd_alter_table_exchange_partition:: /* Don't allow to exchange with log table */ swap_table_list= table_list->next_local; - if (check_if_log_table(swap_table_list->db_length, swap_table_list->db, - swap_table_list->table_name_length, - swap_table_list->table_name, 0)) - { - my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); + if (check_if_log_table(swap_table_list, FALSE, "ALTER PARTITION")) DBUG_RETURN(TRUE); - } /* Currently no MDL lock that allows both read and write and is upgradeable diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 8508a0d7c60..df76c9f6417 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2005, 2012, Oracle and/or its affiliates. - Copyright (c) 2010, 2013, Monty Program Ab + Copyright (c) 2005, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2014, SkySQL 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 @@ -192,6 +192,7 @@ mysql_mutex_t LOCK_plugin; static DYNAMIC_ARRAY plugin_dl_array; static DYNAMIC_ARRAY plugin_array; static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; +static MEM_ROOT plugin_mem_root; static bool reap_needed= false; static int plugin_array_version=0; @@ -203,7 +204,7 @@ ulong dlopen_count; write-lock on LOCK_system_variables_hash is required before modifying the following variables/structures */ -static MEM_ROOT plugin_mem_root; +static MEM_ROOT plugin_vars_mem_root; static uint global_variables_dynamic_size= 0; static HASH bookmark_hash; @@ -290,8 +291,8 @@ public: /* prototypes */ -static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv); -static bool plugin_load_list(MEM_ROOT *, int *, char **, const char *); +static void plugin_load(MEM_ROOT *tmp_root); +static bool plugin_load_list(MEM_ROOT *, const char *); static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *, int *, char **); static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *, @@ -1037,8 +1038,7 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) Requires that a write-lock is held on LOCK_system_variables_hash */ static bool plugin_add(MEM_ROOT *tmp_root, - const LEX_STRING *name, LEX_STRING *dl, - int *argc, char **argv, int report) + const LEX_STRING *name, LEX_STRING *dl, int report) { struct st_plugin_int tmp; struct st_maria_plugin *plugin; @@ -1075,7 +1075,6 @@ static bool plugin_add(MEM_ROOT *tmp_root, dupes++; continue; // already installed } - struct st_plugin_int *tmp_plugin_ptr; if (*(int*)plugin->info < min_plugin_info_interface_version[plugin->type] || @@ -1106,14 +1105,9 @@ static bool plugin_add(MEM_ROOT *tmp_root, tmp.ref_count= 0; tmp.state= PLUGIN_IS_UNINITIALIZED; tmp.load_option= PLUGIN_ON; - if (test_plugin_options(tmp_root, &tmp, argc, argv)) - tmp.state= PLUGIN_IS_DISABLED; if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp))) - { - mysql_del_sys_var_chain(tmp.system_vars); goto err; - } plugin_array_version++; if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr)) tmp_plugin_ptr->state= PLUGIN_IS_FREED; @@ -1128,8 +1122,6 @@ static bool plugin_add(MEM_ROOT *tmp_root, err: errs++; - if (tmp.nbackups) - restore_ptr_backup(tmp.nbackups, tmp.ptr_backup); if (name->str) break; } @@ -1198,6 +1190,8 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) if (ref_check && plugin->ref_count) sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.", plugin->name.str, plugin->ref_count); + + mysql_del_sys_var_chain(plugin->system_vars); } static void plugin_del(struct st_plugin_int *plugin) @@ -1205,11 +1199,8 @@ static void plugin_del(struct st_plugin_int *plugin) DBUG_ENTER("plugin_del"); mysql_mutex_assert_owner(&LOCK_plugin); /* Free allocated strings before deleting the plugin. */ - mysql_rwlock_wrlock(&LOCK_system_variables_hash); - mysql_del_sys_var_chain(plugin->system_vars); - mysql_rwlock_unlock(&LOCK_system_variables_hash); - restore_ptr_backup(plugin->nbackups, plugin->ptr_backup); plugin_vars_free_values(plugin->system_vars); + restore_ptr_backup(plugin->nbackups, plugin->ptr_backup); my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin); plugin_dl_del(plugin->plugin_dl); plugin->state= PLUGIN_IS_FREED; @@ -1343,7 +1334,8 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) } -static int plugin_initialize(struct st_plugin_int *plugin) +static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin, + int *argc, char **argv, bool options_only) { int ret= 1; DBUG_ENTER("plugin_initialize"); @@ -1353,6 +1345,28 @@ static int plugin_initialize(struct st_plugin_int *plugin) DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED); mysql_mutex_unlock(&LOCK_plugin); + + mysql_rwlock_wrlock(&LOCK_system_variables_hash); + if (test_plugin_options(tmp_root, plugin, argc, argv)) + state= PLUGIN_IS_DISABLED; + mysql_rwlock_unlock(&LOCK_system_variables_hash); + + if (options_only || state == PLUGIN_IS_DISABLED) + { + ret= 0; + goto err; + } + + if (plugin->plugin_dl && global_system_variables.log_warnings >= 9) + { + void *sym= dlsym(plugin->plugin_dl->handle, + plugin->plugin_dl->mariaversion ? + maria_plugin_declarations_sym : plugin_declarations_sym); + DBUG_ASSERT(sym); + sql_print_information("Plugin %s loaded at %p", + plugin->name.str, sym); + } + if (plugin_type_initialize[plugin->plugin->type]) { if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) @@ -1412,6 +1426,9 @@ static int plugin_initialize(struct st_plugin_int *plugin) ret= 0; err: + if (ret) + mysql_del_sys_var_chain(plugin->system_vars); + mysql_mutex_lock(&LOCK_plugin); plugin->state= state; @@ -1504,6 +1521,7 @@ int plugin_init(int *argc, char **argv, int flags) #endif init_alloc_root(&plugin_mem_root, 4096, 4096, MYF(0)); + init_alloc_root(&plugin_vars_mem_root, 4096, 4096, MYF(0)); init_alloc_root(&tmp_root, 4096, 4096, MYF(0)); if (my_hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0, @@ -1571,10 +1589,7 @@ int plugin_init(int *argc, char **argv, int flags) } free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE)); - if (test_plugin_options(&tmp_root, &tmp, argc, argv)) - tmp.state= PLUGIN_IS_DISABLED; - else - tmp.state= PLUGIN_IS_UNINITIALIZED; + tmp.state= PLUGIN_IS_UNINITIALIZED; if (register_builtin(plugin, &tmp, &plugin_ptr)) goto err_unlock; @@ -1589,15 +1604,12 @@ int plugin_init(int *argc, char **argv, int flags) mysqld --help for all other users, we will only initialize MyISAM here. */ - if (!(flags & PLUGIN_INIT_SKIP_INITIALIZATION) || is_myisam) + if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv, !is_myisam && + (flags & PLUGIN_INIT_SKIP_INITIALIZATION))) { - if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED && - plugin_initialize(plugin_ptr)) - { - if (mandatory) - goto err_unlock; - plugin_ptr->state= PLUGIN_IS_DISABLED; - } + if (mandatory) + goto err_unlock; + plugin_ptr->state= PLUGIN_IS_DISABLED; } /* @@ -1625,15 +1637,12 @@ int plugin_init(int *argc, char **argv, int flags) I_List_iterator<i_string> iter(opt_plugin_load_list); i_string *item; while (NULL != (item= iter++)) - plugin_load_list(&tmp_root, argc, argv, item->ptr); + plugin_load_list(&tmp_root, item->ptr); if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE)) - plugin_load(&tmp_root, argc, argv); + plugin_load(&tmp_root); } - if (flags & PLUGIN_INIT_SKIP_INITIALIZATION) - goto end; - /* Now we initialize all remaining plugins */ @@ -1645,9 +1654,10 @@ int plugin_init(int *argc, char **argv, int flags) for (i= 0; i < plugin_array.elements; i++) { plugin_ptr= *dynamic_element(&plugin_array, i, struct st_plugin_int **); - if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED) + if (plugin_ptr->plugin_dl && plugin_ptr->state == PLUGIN_IS_UNINITIALIZED) { - if (plugin_initialize(plugin_ptr)) + if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv, + (flags & PLUGIN_INIT_SKIP_INITIALIZATION))) { plugin_ptr->state= PLUGIN_IS_DYING; *(reap++)= plugin_ptr; @@ -1674,7 +1684,6 @@ int plugin_init(int *argc, char **argv, int flags) if (reaped_mandatory_plugin) goto err; -end: free_root(&tmp_root, MYF(0)); DBUG_RETURN(0); @@ -1713,7 +1722,7 @@ static bool register_builtin(struct st_maria_plugin *plugin, /* called only by plugin_init() */ -static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) +static void plugin_load(MEM_ROOT *tmp_root) { TABLE_LIST tables; TABLE *table; @@ -1772,7 +1781,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) the mutex here to satisfy the assert */ mysql_mutex_lock(&LOCK_plugin); - if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) + if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG)) sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.", str_name.c_ptr(), str_dl.c_ptr()); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); @@ -1794,8 +1803,7 @@ end: /* called only by plugin_init() */ -static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, - const char *list) +static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) { char buffer[FN_REFLEN]; LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; @@ -1830,14 +1838,14 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, mysql_mutex_lock(&LOCK_plugin); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); name.str= 0; // load everything - if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) + if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG)) goto error; } else { free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_lock(&LOCK_plugin); - if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) + if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG)) goto error; } mysql_mutex_unlock(&LOCK_plugin); @@ -1995,6 +2003,7 @@ void plugin_shutdown(void) my_hash_free(&bookmark_hash); free_root(&plugin_mem_root, MYF(0)); + free_root(&plugin_vars_mem_root, MYF(0)); global_variables_dynamic_size= 0; @@ -2006,28 +2015,22 @@ void plugin_shutdown(void) That is, initialize it, and update mysql.plugin table */ -static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name) +static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name, + int *argc, char **argv) { struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN); int error; DBUG_ASSERT(tmp); mysql_mutex_assert_owner(&LOCK_plugin); // because of tmp->state - if (tmp->state == PLUGIN_IS_DISABLED) - { - if (global_system_variables.log_warnings) - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF), - name->str, "Plugin is disabled"); - } - else if (tmp->state != PLUGIN_IS_UNINITIALIZED) + if (tmp->state != PLUGIN_IS_UNINITIALIZED) { /* already installed */ return 0; } else { - if (plugin_initialize(tmp)) + if (plugin_initialize(thd->mem_root, tmp, argc, argv, false)) { report_error(REPORT_TO_USER, ER_CANT_INITIALIZE_UDF, name->str, "Plugin initialization function failed."); @@ -2035,6 +2038,13 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name) return 1; } } + if (tmp->state == PLUGIN_IS_DISABLED) + { + if (global_system_variables.log_warnings) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF), + name->str, "Plugin is disabled"); + } /* We do not replicate the INSTALL PLUGIN statement. Disable binlogging @@ -2084,6 +2094,12 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, MYSQL_LOCK_IGNORE_TIMEOUT))) DBUG_RETURN(TRUE); + if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL)) + { + report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str); + DBUG_RETURN(TRUE); + } + /* Pre-acquire audit plugins for events that may potentially occur during [UN]INSTALL PLUGIN. @@ -2110,23 +2126,12 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, mysql_audit_acquire_plugins(thd, event_class_mask); mysql_mutex_lock(&LOCK_plugin); - mysql_rwlock_wrlock(&LOCK_system_variables_hash); - - if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL)) - { - report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str); - goto err; - } - error= plugin_add(thd->mem_root, name, &dl, &argc, argv, REPORT_TO_USER); - if (argv) - free_defaults(argv); - mysql_rwlock_unlock(&LOCK_system_variables_hash); - + error= plugin_add(thd->mem_root, name, &dl, REPORT_TO_USER); if (error) goto err; if (name->str) - error= finalize_install(thd, table, name); + error= finalize_install(thd, table, name, &argc, argv); else { st_plugin_dl *plugin_dl= plugin_dl_find(&dl); @@ -2134,22 +2139,20 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, for (plugin= plugin_dl->plugins; plugin->info; plugin++) { LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) }; - error|= finalize_install(thd, table, &str); + error|= finalize_install(thd, table, &str, &argc, argv); } } if (error) - goto deinit; - - mysql_mutex_unlock(&LOCK_plugin); - DBUG_RETURN(FALSE); - -deinit: - reap_needed= true; - reap_plugins(); + { + reap_needed= true; + reap_plugins(); + } err: mysql_mutex_unlock(&LOCK_plugin); - DBUG_RETURN(TRUE); + if (argv) + free_defaults(argv); + DBUG_RETURN(error); } @@ -2872,7 +2875,7 @@ static st_bookmark *register_var(const char *plugin, const char *name, if (!(result= find_bookmark(NULL, varname + 1, flags))) { - result= (st_bookmark*) alloc_root(&plugin_mem_root, + result= (st_bookmark*) alloc_root(&plugin_vars_mem_root, sizeof(struct st_bookmark) + length-1); varname[0]= plugin_var_bookmark_key(flags); memcpy(result->key, varname, length); @@ -3846,7 +3849,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, enum_plugin_load_option plugin_load_option= tmp->load_option; MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ? - &tmp->mem_root : &plugin_mem_root; + &tmp->mem_root : &plugin_vars_mem_root; st_mysql_sys_var **opt; my_option *opts= NULL; LEX_STRING plugin_name; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a9cc26cfb61..3456a7b381f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -881,7 +881,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array, if (param->state == Item_param::NO_VALUE) DBUG_RETURN(1); - if (param->limit_clause_param) + if (param->limit_clause_param && param->state != Item_param::INT_VALUE) { param->set_int(param->val_int(), MY_INT64_NUM_DECIMAL_DIGITS); param->item_type= Item::INT_ITEM; @@ -978,7 +978,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, typecode= sint2korr(read_pos); read_pos+= 2; - (**it).unsigned_flag= test(typecode & signed_bit); + (**it).unsigned_flag= MY_TEST(typecode & signed_bit); setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit)); } } @@ -2716,7 +2716,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_PRINT("exec_query", ("%s", stmt->query())); DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); - open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY); + open_cursor= MY_TEST(flags & (ulong) CURSOR_TYPE_READ_ONLY); thd->protocol= &thd->protocol_binary; stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end); diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h index ea5ebddb561..b468ac1bf9b 100644 --- a/sql/sql_prepare.h +++ b/sql/sql_prepare.h @@ -290,7 +290,7 @@ public: one. Never fails. */ - bool has_next_result() const { return test(m_current_rset->m_next_rset); } + bool has_next_result() const { return MY_TEST(m_current_rset->m_next_rset); } /** Only valid to call if has_next_result() returned true. Otherwise the result is undefined. @@ -298,7 +298,7 @@ public: bool move_to_next_result() { m_current_rset= m_current_rset->m_next_rset; - return test(m_current_rset); + return MY_TEST(m_current_rset); } ~Ed_connection() { free_old_result(); } diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 383888bac30..0676fca8fdc 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -110,6 +110,8 @@ /* The following is used to detect a conflict with DISTINCT */ #define SELECT_ALL (1ULL << 24) // SELECT, user, parser +#define OPTION_GTID_BEGIN (1ULL << 25) // GTID BEGIN found in log + /** The following can be set when importing tables in a 'wrong order' to suppress foreign key checks */ #define OPTION_NO_FOREIGN_KEY_CHECKS (1ULL << 26) // THD, user, binlog @@ -238,6 +240,7 @@ template <class T> bool valid_buffer_range(T jump, OPTIMIZER_SWITCH_DERIVED_MERGE | \ OPTIMIZER_SWITCH_DERIVED_WITH_KEYS | \ OPTIMIZER_SWITCH_TABLE_ELIMINATION | \ + OPTIMIZER_SWITCH_EXTENDED_KEYS | \ OPTIMIZER_SWITCH_IN_TO_EXISTS | \ OPTIMIZER_SWITCH_MATERIALIZATION | \ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\ diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index a229c861116..f1f9ba9452f 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -418,7 +418,8 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, /** - Implementation of FLUSH TABLES <table_list> WITH READ LOCK. + Implementation of FLUSH TABLES <table_list> WITH READ LOCK + and FLUSH TABLES <table_list> FOR EXPORT In brief: take exclusive locks, expel tables from the table cache, reopen the tables, enter the 'LOCKED TABLES' mode, @@ -507,33 +508,36 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) goto error; } - /* - Acquire SNW locks on tables to be flushed. Don't acquire global - IX and database-scope IX locks on the tables as this will make - this statement incompatible with FLUSH TABLES WITH READ LOCK. - */ - if (lock_table_names(thd, all_tables, NULL, - thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) - goto error; + if (thd->lex->type & REFRESH_READ_LOCK) + { + /* + Acquire SNW locks on tables to be flushed. Don't acquire global + IX and database-scope IX locks on the tables as this will make + this statement incompatible with FLUSH TABLES WITH READ LOCK. + */ + if (lock_table_names(thd, all_tables, NULL, + thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) + goto error; - DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks"); + DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks"); - for (table_list= all_tables; table_list; - table_list= table_list->next_global) - { - /* Request removal of table from cache. */ - tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, - table_list->db, - table_list->table_name, FALSE); - /* Reset ticket to satisfy asserts in open_tables(). */ - table_list->mdl_request.ticket= NULL; + for (table_list= all_tables; table_list; + table_list= table_list->next_global) + { + /* Request removal of table from cache. */ + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, + table_list->db, + table_list->table_name, FALSE); + /* Reset ticket to satisfy asserts in open_tables(). */ + table_list->mdl_request.ticket= NULL; + } } /* Before opening and locking tables the below call also waits for old shares to go away, so the fact that we don't pass - MYSQL_LOCK_IGNORE_FLUSH flag to it is important. + MYSQL_OPEN_IGNORE_FLUSH flag to it is important. Also we don't pass MYSQL_OPEN_HAS_MDL_LOCK flag as we want to open underlying tables if merge table is flushed. For underlying tables of the merge the below call has to @@ -542,11 +546,27 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) */ if (open_and_lock_tables(thd, all_tables, FALSE, MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK, - &lock_tables_prelocking_strategy) || - thd->locked_tables_list.init_locked_tables(thd)) - { + &lock_tables_prelocking_strategy)) goto error; + + if (thd->lex->type & REFRESH_FOR_EXPORT) + { + // Check if all storage engines support FOR EXPORT. + for (TABLE_LIST *table_list= all_tables; table_list; + table_list= table_list->next_global) + { + if (!(table_list->table->file->ha_table_flags() & HA_CAN_EXPORT)) + { + my_error(ER_ILLEGAL_HA, MYF(0),table_list->table->file->table_type(), + table_list->db, table_list->table_name); + return true; + } + } } + + if (thd->locked_tables_list.init_locked_tables(thd)) + goto error; + thd->variables.option_bits|= OPTION_TABLE_LOCK; /* diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 6babdd7c636..897aa183b60 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -84,12 +84,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) for (to_table= 0, ren_table= table_list; ren_table; to_table= 1 - to_table, ren_table= ren_table->next_local) { - int log_table_rename= 0; - - if ((log_table_rename= - check_if_log_table(ren_table->db_length, ren_table->db, - ren_table->table_name_length, - ren_table->table_name, 1))) + int log_table_rename; + if ((log_table_rename= check_if_log_table(ren_table, TRUE, NullS))) { /* as we use log_table_rename as an array index, we need it to start diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index e4116759c13..9e99b7292d0 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1960,8 +1960,8 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, }); if (global_system_variables.log_warnings > 1) - sql_print_information("Start binlog_dump to slave_server(%d), pos(%s, %lu)", - (int)thd->variables.server_id, log_ident, (ulong)pos); + sql_print_information("Start binlog_dump to slave_server(%lu), pos(%s, %lu)", + thd->variables.server_id, log_ident, (ulong)pos); if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos))) { errmsg= "Failed to run hook 'transmit_start'"; @@ -3453,7 +3453,6 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) } if (need_relay_log_purge) { - relay_log_purge= 1; THD_STAGE_INFO(thd, stage_purging_old_relay_logs); if (purge_relay_logs(&mi->rli, thd, 0 /* not only reset, but also reinit */, @@ -3467,7 +3466,6 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) else { const char* msg; - relay_log_purge= 0; /* Relay log is already initialized */ if (init_relay_log_pos(&mi->rli, mi->rli.group_relay_log_name, @@ -3977,6 +3975,20 @@ rpl_deinit_gtid_slave_state() } +void +rpl_init_gtid_waiting() +{ + rpl_global_gtid_waiting.init(); +} + + +void +rpl_deinit_gtid_waiting() +{ + rpl_global_gtid_waiting.destroy(); +} + + /* Format the current GTID state as a string, for returning the value of @@global.gtid_slave_pos. diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 018b23673ee..7f7751b8f44 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -72,6 +72,8 @@ extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state; #endif void rpl_init_gtid_slave_state(); void rpl_deinit_gtid_slave_state(); +void rpl_init_gtid_waiting(); +void rpl_deinit_gtid_waiting(); int gtid_state_from_binlog_pos(const char *name, uint32 pos, String *out_str); int rpl_append_gtid_state(String *dest, bool use_binlog); int rpl_load_gtid_state(slave_connection_state *state, bool use_binlog); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c109ee10877..cfef5eafa27 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -725,7 +725,7 @@ JOIN::prepare(Item ***rref_pointer_array, aggregate functions in the SELECT list is a MySQL exptenstion that is allowed only if the ONLY_FULL_GROUP_BY sql mode is not set. */ - bool mixed_implicit_grouping= false; + mixed_implicit_grouping= false; if ((~thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) && select_lex->with_sum_func && !group_list) { @@ -764,7 +764,7 @@ JOIN::prepare(Item ***rref_pointer_array, Note: this loop doesn't touch tables inside merged semi-joins, because subquery-to-semijoin conversion has not been done yet. This is intended. */ - if (mixed_implicit_grouping) + if (mixed_implicit_grouping && tbl->table) tbl->table->maybe_null= 1; } @@ -1078,8 +1078,10 @@ JOIN::optimize_inner() if (select_lex->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(TRUE); table_count= select_lex->leaf_tables.elements; - select_lex->update_used_tables(); } + // Update used tables after all handling derived table procedures + select_lex->update_used_tables(); + /* In fact we transform underlying subqueries after their 'prepare' phase and before 'optimize' from upper query 'optimize' to allow semijoin @@ -1158,11 +1160,8 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S MEMROOT for prepared statements and stored procedures. */ - Query_arena *arena= thd->stmt_arena, backup; - if (arena->is_conventional()) - arena= 0; // For easier test - else - thd->set_n_backup_active_arena(arena, &backup); + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); sel->first_cond_optimization= 0; @@ -1767,7 +1766,7 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S /* Perform FULLTEXT search before all regular searches */ if (!(select_options & SELECT_DESCRIBE)) - init_ftfuncs(thd, select_lex, test(order)); + init_ftfuncs(thd, select_lex, MY_TEST(order)); if (optimize_unflattened_subqueries()) DBUG_RETURN(1); @@ -2280,7 +2279,7 @@ JOIN::reinit() } if (!(select_options & SELECT_DESCRIBE)) - init_ftfuncs(thd, select_lex, test(order)); + init_ftfuncs(thd, select_lex, MY_TEST(order)); DBUG_RETURN(0); } @@ -2431,6 +2430,7 @@ void JOIN::exec_inner() In this case JOIN::exec must check for JOIN::having_value, in the same way it checks for JOIN::cond_value. */ + DBUG_ASSERT(error == 0); if (cond_value != Item::COND_FALSE && having_value != Item::COND_FALSE && (!conds || conds->val_int()) && @@ -2441,16 +2441,15 @@ void JOIN::exec_inner() procedure->end_of_records()) : result->send_data(fields_list)> 0)) error= 1; else - { - error= (int) result->send_eof(); send_records= ((select_options & OPTION_FOUND_ROWS) ? 1 : thd->get_sent_row_count()); - } } else - { - error=(int) result->send_eof(); send_records= 0; + if (!error) + { + join_free(); // Unlock all cursors + error= (int) result->send_eof(); } } /* Single select (without union) always returns 0 or 1 row */ @@ -2603,7 +2602,7 @@ void JOIN::exec_inner() curr_join->const_tables != curr_join->table_count) { JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables; - first_tab->sorted= test(first_tab->loosescan_match_tab); + first_tab->sorted= MY_TEST(first_tab->loosescan_match_tab); } Procedure *save_proc= curr_join->procedure; @@ -2783,7 +2782,7 @@ void JOIN::exec_inner() curr_join->const_tables != curr_join->table_count) { JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables; - first_tab->sorted= test(first_tab->loosescan_match_tab); + first_tab->sorted= MY_TEST(first_tab->loosescan_match_tab); } tmp_error= -1; if (setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) || @@ -3065,8 +3064,7 @@ void JOIN::exec_inner() Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); error= do_select(curr_join, curr_fields_list, NULL, procedure); thd->limit_found_rows= curr_join->send_records; - if (curr_join->order && - curr_join->sortorder) + if (curr_join->order && curr_join->filesort_found_rows) { /* Use info provided by filesort. */ DBUG_ASSERT(curr_join->table_count > curr_join->const_tables); @@ -3392,7 +3390,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, stat_ref=(JOIN_TAB**) join->thd->alloc(sizeof(JOIN_TAB*)* (MAX_TABLES + table_count + 1)); stat_vector= stat_ref + MAX_TABLES; - table_vector=(TABLE**) join->thd->alloc(sizeof(TABLE*)*(table_count*2)); + table_vector=(TABLE**) join->thd->calloc(sizeof(TABLE*)*(table_count*2)); join->positions= new (join->thd->mem_root) POSITION[(table_count+1)]; /* best_positions is ok to allocate with alloc() as we copy things to it with @@ -3431,13 +3429,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, #endif DBUG_EXECUTE_IF("bug11747970_raise_error", - { - if (!error) - { - my_error(ER_UNKNOWN_ERROR, MYF(0)); - goto error; - } - }); + { join->thd->killed= KILL_QUERY_HARD; }); if (error) { table->file->print_error(error, MYF(0)); @@ -3826,7 +3818,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, join->impossible_where= false; if (conds && const_count) { + conds->update_used_tables(); conds= remove_eq_conds(join->thd, conds, &join->cond_value); + join->select_lex->where= conds; if (join->cond_value == Item::COND_FALSE) { join->impossible_where= true; @@ -4984,8 +4978,8 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) if (a->keypart != b->keypart) return (int) (a->keypart - b->keypart); // Place const values before other ones - if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) - - test((b->used_tables & ~OUTER_REF_TABLE_BIT)))) + if ((res= MY_TEST((a->used_tables & ~OUTER_REF_TABLE_BIT)) - + MY_TEST((b->used_tables & ~OUTER_REF_TABLE_BIT)))) return res; /* Place rows that are not 'OPTIMIZE_REF_OR_NULL' first */ return (int) ((a->optimize & KEY_OPTIMIZE_REF_OR_NULL) - @@ -5768,7 +5762,7 @@ best_access_path(JOIN *join, } else { - found_constraint= test(found_part); + found_constraint= MY_TEST(found_part); loose_scan_opt.check_ref_access_part1(s, key, start_key, found_part); /* Check if we found full key */ @@ -5777,7 +5771,7 @@ best_access_path(JOIN *join, { /* use eq key */ max_key_part= (uint) ~0; if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME || - test(key_flags & HA_EXT_NOSAME)) + MY_TEST(key_flags & HA_EXT_NOSAME)) { tmp = prev_record_reads(join->positions, idx, found_ref); records=1.0; @@ -5908,7 +5902,7 @@ best_access_path(JOIN *join, */ if (table->quick_keys.is_set(key) && !found_ref && //(C1) table->quick_key_parts[key] == max_key_part && //(C2) - table->quick_n_ranges[key] == 1+test(ref_or_null_part)) //(C3) + table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part)) //(C3) { tmp= records= (double) table->quick_rows[key]; } @@ -6002,8 +5996,8 @@ best_access_path(JOIN *join, table->quick_key_parts[key] <= max_key_part && const_part & ((key_part_map)1 << table->quick_key_parts[key]) && - table->quick_n_ranges[key] == 1 + test(ref_or_null_part & - const_part) && + table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part & + const_part) && records > (double) table->quick_rows[key]) { tmp= records= (double) table->quick_rows[key]; @@ -6194,8 +6188,8 @@ best_access_path(JOIN *join, best_key= 0; /* range/index_merge/ALL/index access method are "independent", so: */ best_ref_depends_map= 0; - best_uses_jbuf= test(!disable_jbuf && !((s->table->map & - join->outer_join))); + best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map & + join->outer_join))); } } @@ -6360,7 +6354,7 @@ choose_plan(JOIN *join, table_map join_tables) uint prune_level= join->thd->variables.optimizer_prune_level; uint use_cond_selectivity= join->thd->variables.optimizer_use_condition_selectivity; - bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN); + bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN); DBUG_ENTER("choose_plan"); join->cur_embedding_map= 0; @@ -8254,7 +8248,8 @@ get_best_combination(JOIN *join) sub-order */ SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info; - j->records= j->records_read= (ha_rows)(sjm->is_sj_scan? sjm->rows : 1); + j->records_read= (sjm->is_sj_scan? sjm->rows : 1); + j->records= (ha_rows) j->records_read; j->cond_selectivity= 1.0; JOIN_TAB *jt; JOIN_TAB_RANGE *jt_range; @@ -8483,7 +8478,7 @@ static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables) table_map local_tables= jtab->emb_sj_nest->nested_join->used_tables | jtab->join->const_table_map | OUTER_REF_TABLE_BIT; - return !test(used_tables & ~local_tables); + return !MY_TEST(used_tables & ~local_tables); } /* @@ -8620,7 +8615,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, !are_tables_local(j, keyuse->val->used_tables())) keyuse++; /* Skip other parts */ - uint maybe_null= test(keyinfo->key_part[i].null_bit); + uint maybe_null= MY_TEST(keyinfo->key_part[i].null_bit); j->ref.items[i]=keyuse->val; // Save for cond removal j->ref.cond_guards[i]= keyuse->cond_guard; if (keyuse->null_rejecting) @@ -8674,7 +8669,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, else if (!((keyparts == keyinfo->user_defined_key_parts && ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)) || (keyparts > keyinfo->user_defined_key_parts && // true only for extended keys - test(key_flags & HA_EXT_NOSAME) && + MY_TEST(key_flags & HA_EXT_NOSAME) && keyparts == keyinfo->ext_key_parts)) || null_ref_key) { @@ -8797,7 +8792,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) functions are handled. */ // the temporary table was explicitly requested - DBUG_ASSERT(test(select_options & OPTION_BUFFER_RESULT)); + DBUG_ASSERT(MY_TEST(select_options & OPTION_BUFFER_RESULT)); // the temporary table does not have a grouping expression DBUG_ASSERT(!temp_table->group); } @@ -10404,7 +10399,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, ((options & SELECT_DESCRIBE) || !tab->cache->init())) { tab->icp_other_tables_ok= FALSE; - return (2-test(!prev_cache)); + return (2 - MY_TEST(!prev_cache)); } goto no_join_cache; case JT_SYSTEM: @@ -10439,7 +10434,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, ((options & SELECT_DESCRIBE) || !tab->cache->init())) { tab->icp_other_tables_ok= FALSE; - return (4-test(!prev_cache)); + return (4 - MY_TEST(!prev_cache)); } goto no_join_cache; } @@ -10458,7 +10453,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, prev_cache= 0; if ((tab->cache= new JOIN_CACHE_BKA(join, tab, flags, prev_cache)) && ((options & SELECT_DESCRIBE) || !tab->cache->init())) - return (6-test(!prev_cache)); + return (6 - MY_TEST(!prev_cache)); goto no_join_cache; } else @@ -10469,7 +10464,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, ((options & SELECT_DESCRIBE) || !tab->cache->init())) { tab->idx_cond_fact_out= FALSE; - return (8-test(!prev_cache)); + return (8 - MY_TEST(!prev_cache)); } goto no_join_cache; } @@ -10564,7 +10559,7 @@ restart: no_jbuf_after, idx, prev_tab); - tab->use_join_cache= test(tab->used_join_cache_level); + tab->use_join_cache= MY_TEST(tab->used_join_cache_level); /* psergey-merge: todo: raise the question that this is really stupid that we can first allocate a join buffer, then decide not to use it and free @@ -10694,7 +10689,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) uint i; DBUG_ENTER("make_join_readinfo"); - bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); + bool statistics= MY_TEST(!(join->select_options & SELECT_DESCRIBE)); bool sorted= 1; join->complex_firstmatch_tables= table_map(0); @@ -11154,7 +11149,7 @@ ha_rows JOIN_TAB::get_examined_rows() else examined_rows= records_read; - return examined_rows; + return (ha_rows) examined_rows; } @@ -11192,7 +11187,7 @@ bool JOIN_TAB::preread_init() /* init ftfuns for just initialized derived table */ if (table->fulltext_searched) - init_ftfuncs(join->thd, join->select_lex, test(join->order)); + init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order)); return FALSE; } @@ -11248,12 +11243,12 @@ bool TABLE_REF::tmp_table_index_lookup_init(THD *thd, Item *item= it.next(); DBUG_ASSERT(item); items[i]= item; - int null_count= test(cur_key_part->field->real_maybe_null()); + int null_count= MY_TEST(cur_key_part->field->real_maybe_null()); *ref_key= new store_key_item(thd, cur_key_part->field, /* TIMOUR: the NULL byte is taken into account in cur_key_part->store_length, so instead of - cur_ref_buff + test(maybe_null), we could + cur_ref_buff + MY_TEST(maybe_null), we could use that information instead. */ cur_ref_buff + null_count, @@ -11453,6 +11448,16 @@ void JOIN::cleanup(bool full) { tab->cleanup(); } + + if (tabs_kind == WALK_OPTIMIZATION_TABS && + first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS) != + first_breadth_first_tab(this, WALK_EXECUTION_TABS)) + { + JOIN_TAB *jt= first_breadth_first_tab(this, WALK_EXECUTION_TABS); + /* We've walked optimization tabs. do execution ones too */ + if (jt) + jt->cleanup(); + } } cleaned= true; @@ -11475,7 +11480,7 @@ void JOIN::cleanup(bool full) } if (full) { - cleanup_empty_jtbm_semi_joins(this); + cleanup_empty_jtbm_semi_joins(this, join_list); /* Ensure that the following delete_elements() would not be called twice for the same list. @@ -11673,6 +11678,8 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order) Remove all constants and check if ORDER only contains simple expressions. + We also remove all duplicate expressions, keeping only the first one. + simple_order is set to 1 if sort_order only uses fields from head table and the head table is not a LEFT JOIN table. @@ -11680,9 +11687,10 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order) @param first_order List of SORT or GROUP order @param cond WHERE statement @param change_list Set to 1 if we should remove things from list. - If this is not set, then only simple_order is - calculated. - @param simple_order Set to 1 if we are only using simple expressions + If this is not set, then only simple_order is + calculated. + @param simple_order Set to 1 if we are only using simple + expressions. @return Returns new sort order @@ -11695,7 +11703,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, if (join->table_count == join->const_tables) return change_list ? 0 : first_order; // No need to sort - ORDER *order,**prev_ptr; + ORDER *order,**prev_ptr, *tmp_order; table_map first_table; table_map not_const_tables= ~join->const_table_map; table_map ref; @@ -11709,7 +11717,6 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, first_is_base_table= TRUE; } - /* Cleanup to avoid interference of calls of this function for ORDER BY and GROUP BY @@ -11778,6 +11785,17 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, } } } + /* Remove ORDER BY entries that we have seen before */ + for (tmp_order= first_order; + tmp_order != order; + tmp_order= tmp_order->next) + { + if (tmp_order->item[0]->eq(order->item[0],1)) + break; + } + if (tmp_order != order) + continue; // Duplicate order by. Remove + if (change_list) *prev_ptr= order; // use this entry prev_ptr= &order->next; @@ -12961,7 +12979,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, item= NULL; /* Don't produce equality */ } - bool produce_equality= test(item == field_item); + bool produce_equality= MY_TEST(item == field_item); if (!item_const && field_sjm && field_sjm != current_sjm) { /* Entering an SJM nest */ @@ -13579,7 +13597,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top, NESTED_JOIN *nested_join; TABLE_LIST *prev_table= 0; List_iterator<TABLE_LIST> li(*join_list); - bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN); + bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN); DBUG_ENTER("simplify_joins"); /* @@ -14202,7 +14220,7 @@ optimize_cond(JOIN *join, COND *conds, DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY);); conds= build_equal_items(join, conds, NULL, join_list, ignore_on_conds, cond_equal, - test(flags & OPT_LINK_EQUAL_FIELDS)); + MY_TEST(flags & OPT_LINK_EQUAL_FIELDS)); DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY);); /* change field = field to field = const for each found field = const */ @@ -15333,18 +15351,18 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps) { uint field_count= table->s->fields; - bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, + my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, FALSE); - bitmap_init(&table->def_vcol_set, + my_bitmap_init(&table->def_vcol_set, (my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)), field_count, FALSE); - bitmap_init(&table->tmp_set, + my_bitmap_init(&table->tmp_set, (my_bitmap_map*) (bitmaps+ 2*bitmap_buffer_size(field_count)), field_count, FALSE); - bitmap_init(&table->eq_join_set, + my_bitmap_init(&table->eq_join_set, (my_bitmap_map*) (bitmaps+ 3*bitmap_buffer_size(field_count)), field_count, FALSE); - bitmap_init(&table->cond_set, + my_bitmap_init(&table->cond_set, (my_bitmap_map*) (bitmaps+ 4*bitmap_buffer_size(field_count)), field_count, FALSE); /* write_set and all_set are copies of read_set */ @@ -15421,9 +15439,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, save_sum_fields|= param->precomputed_group_by; DBUG_ENTER("create_tmp_table"); DBUG_PRINT("enter", - ("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d", + ("table_alias: '%s' distinct: %d save_sum_fields: %d " + "rows_limit: %lu group: %d", table_alias, (int) distinct, (int) save_sum_fields, - (ulong) rows_limit,test(group))); + (ulong) rows_limit, MY_TEST(group))); thd->inc_status_created_tmp_tables(); thd->query_plan_flags|= QPLAN_TMP_TABLE; @@ -15869,23 +15888,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, if (!(field->flags & NOT_NULL_FLAG)) { - if (field->flags & GROUP_FLAG && !using_unique_constraint) - { - /* - We have to reserve one byte here for NULL bits, - as this is updated by 'end_update()' - */ - *pos++=0; // Null is stored here - recinfo->length=1; - recinfo->type=FIELD_NORMAL; - recinfo++; - bzero((uchar*) recinfo,sizeof(*recinfo)); - } - else - { - recinfo->null_bit= (uint8)1 << (null_count & 7); - recinfo->null_pos= null_count/8; - } + recinfo->null_bit= (uint8)1 << (null_count & 7); + recinfo->null_pos= null_count/8; field->move_field(pos,null_flags+null_count/8, (uint8)1 << (null_count & 7)); null_count++; @@ -15984,7 +15988,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, table->group=group; /* Table is grouped by key */ param->group_buff=group_buff; share->keys=1; - share->uniques= test(using_unique_constraint); + share->uniques= MY_TEST(using_unique_constraint); table->key_info= table->s->key_info= keyinfo; table->keys_in_use_for_query.set_bit(0); share->keys_in_use.set_bit(0); @@ -16038,7 +16042,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, if (!(cur_group->field= field->new_key_field(thd->mem_root,table, group_buff + - test(maybe_null), + MY_TEST(maybe_null), field->null_ptr, field->null_bit))) goto err; /* purecov: inspected */ @@ -16094,7 +16098,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, null_pack_length-=hidden_null_pack_length; keyinfo->user_defined_key_parts= ((field_count-param->hidden_field_count)+ - (share->uniques ? test(null_pack_length) : 0)); + (share->uniques ? MY_TEST(null_pack_length) : 0)); keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; table->distinct= 1; share->keys= 1; @@ -16856,7 +16860,8 @@ free_tmp_table(THD *thd, TABLE *entry) MEM_ROOT own_root= entry->mem_root; const char *save_proc_info; DBUG_ENTER("free_tmp_table"); - DBUG_PRINT("enter",("table: %s",entry->alias.c_ptr())); + DBUG_PRINT("enter",("table: %s alias: %s",entry->s->table_name.str, + entry->alias.c_ptr())); save_proc_info=thd->proc_info; THD_STAGE_INFO(thd, stage_removing_tmp_table); @@ -17505,7 +17510,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, if (select_cond) { - select_cond_result= test(select_cond->val_int()); + select_cond_result= MY_TEST(select_cond->val_int()); /* check for errors evaluating the condition */ if (join->thd->is_error()) @@ -17890,7 +17895,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) (*tab->on_expr_ref)->update_used_tables(); DBUG_ASSERT((*tab->on_expr_ref)->const_item()); #endif - if ((table->null_row= test((*tab->on_expr_ref)->val_int() == 0))) + if ((table->null_row= MY_TEST((*tab->on_expr_ref)->val_int() == 0))) mark_as_null_row(table); } if (!table->null_row) @@ -18535,8 +18540,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), records are read. Because of optimization in some cases it can provide only select_limit_cnt+1 records. */ - if (join->order && - join->sortorder && + if (join->order && join->filesort_found_rows && join->select_options & OPTION_FOUND_ROWS) { DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT")); @@ -18796,7 +18800,16 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), for (group=table->group ; group ; group=group->next) { Item *item= *group->item; - item->save_org_in_field(group->field); + if (group->fast_field_copier_setup != group->field) + { + DBUG_PRINT("info", ("new setup 0x%lx -> 0x%lx", + (ulong)group->fast_field_copier_setup, + (ulong)group->field)); + group->fast_field_copier_setup= group->field; + group->fast_field_copier_func= + item->setup_fast_field_copier(group->field); + } + item->save_org_in_field(group->field, group->fast_field_copier_func); /* Store in the used key if the field was 0 */ if (item->maybe_null) group->buff[-1]= (char) group->field->is_null(); @@ -18817,19 +18830,6 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), goto end; } - /* - Copy null bits from group key to table - We can't copy all data as the key may have different format - as the row data (for example as with VARCHAR keys) - */ - KEY_PART_INFO *key_part; - for (group=table->group,key_part=table->key_info[0].key_part; - group ; - group=group->next,key_part++) - { - if (key_part->null_bit) - memcpy(table->record[0]+key_part->offset, group->buff, 1); - } init_tmptable_sum_functions(join->sum_funcs); if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd)) DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -19476,14 +19476,14 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, for (; const_key_parts & 1 ; const_key_parts>>= 1) key_part++; - if (key_part == key_part_end) + if (key_part >= key_part_end) { /* We are at the end of the key. Check if the engine has the primary key as a suffix to the secondary keys. If it has continue to check the primary key as a suffix. */ - if (!on_pk_suffix && + if (!on_pk_suffix && (table->key_info[idx].ext_key_part_map & 1) && (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && table->s->primary_key != MAX_KEY && table->s->primary_key != idx) @@ -19507,11 +19507,12 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, (((key_part_map) 1) << pk_part_idx))) break; } + /* Adjust const_key_parts */ const_key_parts&= (((key_part_map) 1) << pk_part_idx) -1; - for (; const_key_parts & 1 ; const_key_parts>>= 1) - key_part++; + for (; const_key_parts & 1 ; const_key_parts>>= 1) + key_part++; /* Test if the primary key parts were all const (i.e. there's one row). The sorting doesn't matter. @@ -19520,7 +19521,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, reverse == 0) { key_parts= 0; - reverse= 1; + reverse= 1; // Key is ok to use goto ok; } } @@ -20105,7 +20106,7 @@ check_reverse_order: table->disable_keyread(); } } - else if (tab->type != JT_ALL) + else if (tab->type != JT_ALL || tab->select->quick) { /* We're about to use a quick access to the table. @@ -20394,7 +20395,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, select, filesort_limit, 0, &examined_rows, &found_rows); table->sort.found_records= filesort_retval; - tab->records= found_rows; // For SQL_CALC_ROWS + if (found_rows != HA_POS_ERROR) + { + tab->records= found_rows; // For SQL_CALC_ROWS + join->filesort_found_rows= true; + } if (quick_created) { @@ -21007,7 +21012,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, if (!order_item->fixed && (order_item->fix_fields(thd, order->item) || (order_item= *order->item)->check_cols(1) || - thd->is_fatal_error)) + thd->is_error())) return TRUE; /* Wrong field. */ uint el= all_fields.elements; @@ -21353,7 +21358,7 @@ test_if_subpart(ORDER *a,ORDER *b) else return 0; } - return test(!b); + return MY_TEST(!b); } /** @@ -23307,7 +23312,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table, double examined_rows= tab->get_examined_rows(); eta->rows_set= true; - eta->rows= examined_rows; + eta->rows= (ha_rows) examined_rows; /* "filtered" */ float f= 0.0; diff --git a/sql/sql_select.h b/sql/sql_select.h index c413d0ca023..ea60e815c07 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -524,7 +524,7 @@ typedef struct st_join_table { ha_rows get_examined_rows(); bool preread_init(); - bool is_sjm_nest() { return test(bush_children); } + bool is_sjm_nest() { return MY_TEST(bush_children); } bool access_from_tables_is_allowed(table_map used_tables, table_map sjm_lookup_tables) @@ -1119,6 +1119,12 @@ public: restore_no_rows_in_result() in ::reinit() */ bool no_rows_in_result_called; + + /** + This is set if SQL_CALC_ROWS was calculated by filesort() + and should be taken from the appropriate JOIN_TAB + */ + bool filesort_found_rows; /** Copy of this JOIN to be used with temporary tables. @@ -1141,7 +1147,8 @@ public: */ JOIN *tmp_join; ROLLUP rollup; ///< Used with rollup - + + bool mixed_implicit_grouping; bool select_distinct; ///< Set if SELECT DISTINCT /** If we have the GROUP BY statement in the query, @@ -1293,7 +1300,7 @@ public: lock= thd_arg->lock; select_lex= 0; //for safety tmp_join= 0; - select_distinct= test(select_options & SELECT_DISTINCT); + select_distinct= MY_TEST(select_options & SELECT_DISTINCT); no_order= 0; simple_order= 0; simple_group= 0; @@ -1334,6 +1341,7 @@ public: emb_sjm_nest= NULL; sjm_lookup_tables= 0; + filesort_found_rows= false; exec_saved_explain= false; /* The following is needed because JOIN::cleanup(true) may be called for @@ -1436,7 +1444,7 @@ public: void set_allowed_join_cache_types(); bool is_allowed_hash_join_access() { - return test(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && + return MY_TEST(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT; } /* @@ -1455,7 +1463,7 @@ public: return ((const_tables != table_count && ((select_distinct || !simple_order || !simple_group) || (group_list && order) || - test(select_options & OPTION_BUFFER_RESULT))) || + MY_TEST(select_options & OPTION_BUFFER_RESULT))) || (rollup.state != ROLLUP::STATE_NONE && select_distinct)); } bool choose_subquery_plan(table_map join_tables); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index eb986f16d73..475b7124a3a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -889,7 +889,7 @@ public: m_view_access_denied_message_ptr(NULL) { - m_sctx = test(m_top_view->security_ctx) ? + m_sctx= MY_TEST(m_top_view->security_ctx) ? m_top_view->security_ctx : thd->security_ctx; } @@ -1045,7 +1045,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if ((table_list->view ? view_store_create_info(thd, table_list, &buffer) : store_create_info(thd, table_list, &buffer, NULL, - FALSE /* show_database */))) + FALSE /* show_database */, FALSE))) goto exit; if (table_list->view) @@ -1530,6 +1530,8 @@ static void append_create_options(THD *thd, String *packet, to tailor the format of the statement. Can be NULL, in which case only SQL_MODE is considered when building the statement. + show_database Add database name to table name + create_or_replace Use CREATE OR REPLACE syntax NOTE Currently always return 0, but might return error code in the @@ -1540,7 +1542,8 @@ static void append_create_options(THD *thd, String *packet, */ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, - HA_CREATE_INFO *create_info_arg, bool show_database) + HA_CREATE_INFO *create_info_arg, bool show_database, + bool create_or_replace) { List<Item> field_list; char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH]; @@ -1573,10 +1576,12 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, restore_record(table, s->default_values); // Get empty record + packet->append(STRING_WITH_LEN("CREATE ")); + if (create_or_replace) + packet->append(STRING_WITH_LEN("OR REPLACE ")); if (share->tmp_table) - packet->append(STRING_WITH_LEN("CREATE TEMPORARY TABLE ")); - else - packet->append(STRING_WITH_LEN("CREATE TABLE ")); + packet->append(STRING_WITH_LEN("TEMPORARY ")); + packet->append(STRING_WITH_LEN("TABLE ")); if (create_info_arg && (create_info_arg->options & HA_LEX_CREATE_IF_NOT_EXISTS)) packet->append(STRING_WITH_LEN("IF NOT EXISTS ")); @@ -2010,7 +2015,7 @@ static void store_key_options(THD *thd, String *packet, TABLE *table, end= longlong10_to_str(key_info->block_size, buff, 10); packet->append(buff, (uint) (end - buff)); } - DBUG_ASSERT(test(key_info->flags & HA_USES_COMMENT) == + DBUG_ASSERT(MY_TEST(key_info->flags & HA_USES_COMMENT) == (key_info->comment.length > 0)); if (key_info->flags & HA_USES_COMMENT) { @@ -2224,7 +2229,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) field->maybe_null=1; field_list.push_back(field=new Item_empty_string("Info",max_query_length)); field->maybe_null=1; - if (!thd->variables.old_mode) + if (!thd->variables.old_mode && + !(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO)) { field_list.push_back(field= new Item_float("Progress", 0.0, 3, 7)); field->maybe_null= 0; @@ -2296,6 +2302,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) (double) tmp->progress.max_counter) / (double) max_stage)) * 100.0); + set_if_smaller(thd_info->progress, 100); } else thd_info->progress= 0.0; @@ -2330,7 +2337,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) protocol->store(thd_info->state_info, system_charset_info); protocol->store(thd_info->query_string.str(), thd_info->query_string.charset()); - if (!thd->variables.old_mode) + if (!thd->variables.old_mode && + !(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO)) protocol->store(thd_info->progress, 3, &store_buffer); if (protocol->write()) break; /* purecov: inspected */ @@ -2394,7 +2402,7 @@ int select_result_explain_buffer::send_data(List<Item> &items) fill_record(thd, dst_table, dst_table->field, items, TRUE, FALSE); res= dst_table->file->ha_write_tmp_row(dst_table->record[0]); set_current_thd(cur_thd); - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } bool select_result_text_buffer::send_result_set_metadata(List<Item> &fields, uint flag) @@ -4831,6 +4839,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (fill_schema_table_names(thd, tables, db_name, table_name)) continue; } + else if (schema_table_idx == SCH_TRIGGERS && + db_name == &INFORMATION_SCHEMA_NAME) + { + continue; + } else { if (!(table_open_method & ~OPEN_FRM_ONLY) && @@ -5429,7 +5442,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, #ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access; check_access(thd,SELECT_ACL, db_name->str, - &tables->grant.privilege, 0, 0, test(tables->schema_table)); + &tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table)); col_access= get_column_grant(thd, &tables->grant, db_name->str, table_name->str, field->field_name) & COL_ACLS; @@ -5568,13 +5581,13 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin, table->field[1]->store(option_name, strlen(option_name), scs); table->field[2]->store(plugin_decl(plugin)->descr, strlen(plugin_decl(plugin)->descr), scs); - tmp= &yesno[test(hton->commit)]; + tmp= &yesno[MY_TEST(hton->commit)]; table->field[3]->store(tmp->str, tmp->length, scs); table->field[3]->set_notnull(); - tmp= &yesno[test(hton->prepare)]; + tmp= &yesno[MY_TEST(hton->prepare)]; table->field[4]->store(tmp->str, tmp->length, scs); table->field[4]->set_notnull(); - tmp= &yesno[test(hton->savepoint_set)]; + tmp= &yesno[MY_TEST(hton->savepoint_set)]; table->field[5]->store(tmp->str, tmp->length, scs); table->field[5]->set_notnull(); @@ -6141,7 +6154,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, else table->field[14]->store("", 0, cs); table->field[14]->set_notnull(); - DBUG_ASSERT(test(key_info->flags & HA_USES_COMMENT) == + DBUG_ASSERT(MY_TEST(key_info->flags & HA_USES_COMMENT) == (key_info->comment.length > 0)); if (key_info->flags & HA_USES_COMMENT) table->field[15]->store(key_info->comment.str, @@ -7682,7 +7695,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(0); my_bitmap_map* bitmaps= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); - bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, + my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, FALSE); table->read_set= &table->def_read_set; bitmap_clear_all(table->read_set); @@ -8053,8 +8066,20 @@ static bool do_fill_table(THD *thd, da->push_warning_info(&wi_tmp); - bool res= table_list->schema_table->fill_table( - thd, table_list, join_table->select_cond); + Item *item= join_table->select_cond; + if (join_table->cache_select && + join_table->cache_select->cond) + { + /* + If join buffering is used, we should use the condition that is attached + to the join cache. Cache condition has a part of WHERE that can be + checked when we're populating this table. + join_tab->select_cond is of no interest, because it only has conditions + that depend on both this table and previous tables in the join order. + */ + item= join_table->cache_select->cond; + } + bool res= table_list->schema_table->fill_table(thd, table_list, item); da->pop_warning_info(); diff --git a/sql/sql_show.h b/sql/sql_show.h index 10276e8b65e..0416f2fdaba 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -75,7 +75,8 @@ typedef struct system_status_var STATUS_VAR; #define IS_FILES_EXTRA 37 int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, - HA_CREATE_INFO *create_info_arg, bool show_database); + HA_CREATE_INFO *create_info_arg, bool show_database, + bool create_or_replace); int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff); int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table); diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index c1c80921861..68aacd69d98 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -354,7 +354,7 @@ public: bool is_null(uint stat_field_no) { - return test(column_stat_nulls & (1 << stat_field_no)); + return MY_TEST(column_stat_nulls & (1 << stat_field_no)); } double get_nulls_ratio() diff --git a/sql/sql_string.cc b/sql/sql_string.cc index ddac315f80f..a2d4349f747 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1,5 +1,4 @@ -/* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ff389a96a8d..ff2e7939fb8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2031,33 +2031,29 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, bool error; Drop_table_error_handler err_handler; TABLE_LIST *table; - DBUG_ENTER("mysql_rm_table"); /* Disable drop of enabled log tables, must be done before name locking */ for (table= tables; table; table= table->next_local) { - if (check_if_log_table(table->db_length, table->db, - table->table_name_length, table->table_name, true)) - { - my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); + if (check_if_log_table(table, TRUE, "DROP")) DBUG_RETURN(true); - } } - if (!in_bootstrap) + if (!drop_temporary) { - for (table= tables; table; table= table->next_local) + if (!in_bootstrap) { - LEX_STRING db_name= { table->db, table->db_length }; - LEX_STRING table_name= { table->table_name, table->table_name_length }; - if (table->open_type == OT_BASE_ONLY || !find_temporary_table(thd, table)) - (void) delete_statistics_for_table(thd, &db_name, &table_name); + for (table= tables; table; table= table->next_local) + { + LEX_STRING db_name= { table->db, table->db_length }; + LEX_STRING table_name= { table->table_name, table->table_name_length }; + if (table->open_type == OT_BASE_ONLY || + !find_temporary_table(thd, table)) + (void) delete_statistics_for_table(thd, &db_name, &table_name); + } } - } - if (!drop_temporary) - { if (!thd->locked_tables_mode) { if (lock_table_names(thd, tables, NULL, @@ -2107,7 +2103,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, - false, false); + false, false, false); thd->pop_internal_handler(); if (error) @@ -2172,6 +2168,8 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, @param drop_view Allow to delete VIEW .frm @param dont_log_query Don't write query to log files. This will also not generate warnings if the handler files doesn't exists + @param dont_free_locks Don't do automatic UNLOCK TABLE if no more locked + tables @retval 0 ok @retval 1 Error @@ -2194,7 +2192,8 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, - bool dont_log_query) + bool dont_log_query, + bool dont_free_locks) { TABLE_LIST *table; char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL; @@ -2208,6 +2207,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; bool is_drop_tmp_if_exists_added= 0; + bool one_table= tables->next_local == 0; + bool was_view= 0; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); @@ -2286,7 +2287,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { - bool is_trans; + bool is_trans= 0; char *db=table->db; size_t db_length= table->db_length; handlerton *table_type= 0; @@ -2311,12 +2312,16 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, . 1 - a temporary table was not found. . -1 - a temporary table is used by an outer statement. */ - if (table->open_type == OT_BASE_ONLY) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) error= 1; - else if ((error= drop_temporary_table(thd, table, &is_trans)) == -1) + else { - DBUG_ASSERT(thd->in_sub_stmt); - goto err; + if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1) + { + DBUG_ASSERT(thd->in_sub_stmt); + goto err; + } + table->table= 0; } if ((drop_temporary && if_exists) || !error) @@ -2384,7 +2389,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, This handles the case where a "DROP" was executed and a regular table "may be" dropped as drop_temporary is FALSE and error is TRUE. If the error was FALSE a temporary table was dropped and - regardless of the status of drop_tempoary a "DROP TEMPORARY" + regardless of the status of drop_temporary a "DROP TEMPORARY" must be used. */ if (!dont_log_query) @@ -2412,15 +2417,15 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; - if ((drop_temporary || !ha_table_exists(thd, db, alias, &table_type) || - (!drop_view && table_type == view_pseudo_hton))) + if (drop_temporary || + (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) || + (!drop_view && (was_view= (table_type == view_pseudo_hton)))) { /* One of the following cases happened: . "DROP TEMPORARY" but a temporary table was not found. - . "DROP" but table was not found on disk and table can't be - created from engine. - . ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this. + . "DROP" but table was not found + . "DROP TABLE" statement, but it's a view. */ if (if_exists) { @@ -2544,7 +2549,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, err: if (wrong_tables.length()) { - if (!foreign_key_error) + if (one_table && was_view) + my_printf_error(ER_IT_IS_A_VIEW, ER(ER_IT_IS_A_VIEW), MYF(0), + wrong_tables.c_ptr_safe()); + else if (!foreign_key_error) my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0), wrong_tables.c_ptr_safe()); else @@ -2610,7 +2618,8 @@ err: */ if (thd->locked_tables_mode) { - if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) + if (thd->lock && thd->lock->table_count == 0 && + non_temp_tables_count > 0 && !dont_free_locks) { thd->locked_tables_list.unlock_locked_tables(thd); goto end; @@ -3793,7 +3802,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, with length (unlike blobs, where ft code takes data length from a data prefix, ignoring column->length). */ - column->length=test(f_is_blob(sql_field->pack_flag)); + column->length= MY_TEST(f_is_blob(sql_field->pack_flag)); } else { @@ -4517,12 +4526,13 @@ err: way to ensure that concurrent operations won't intervene. mysql_create_table() is a wrapper that can be used for this. - @retval false OK - @retval true error + @retval 0 OK + @retval 1 error + @retval -1 table existed but IF EXISTS was used */ static -bool create_table_impl(THD *thd, +int create_table_impl(THD *thd, const char *db, const char *table_name, const char *path, HA_CREATE_INFO *create_info, @@ -4535,7 +4545,7 @@ bool create_table_impl(THD *thd, { const char *alias; handler *file= 0; - bool error= TRUE; + int error= 1; bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY; bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only; DBUG_ENTER("mysql_create_table_no_lock"); @@ -4565,22 +4575,74 @@ bool create_table_impl(THD *thd, /* Check if table exists */ if (create_info->tmp_table()) { - if (find_temporary_table(thd, db, table_name)) + TABLE *tmp_table; + if ((tmp_table= find_temporary_table(thd, db, table_name))) { - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + if (create_info->options & HA_LEX_CREATE_REPLACE) + { + bool is_trans; + /* + We are using CREATE OR REPLACE on an existing temporary table + Remove the old table so that we can re-create it. + */ + if (drop_temporary_table(thd, tmp_table, &is_trans)) + goto err; + } + else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); - goto err; + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); + goto err; + } } } - else + else { if (!internal_tmp_table && ha_table_exists(thd, db, table_name)) { - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + if (create_info->options & HA_LEX_CREATE_REPLACE) + { + TABLE_LIST table_list; + table_list.init_one_table(db, strlen(db), table_name, + strlen(table_name), table_name, + TL_WRITE_ALLOW_WRITE); + table_list.table= create_info->table; + + if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE")) + goto err; + + /* + Rollback the empty transaction started in mysql_create_table() + call to open_and_lock_tables() when we are using LOCK TABLES. + */ + (void) trans_rollback_stmt(thd); + /* Remove normal table without logging. Keep tables locked */ + if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1)) + goto err; + + /* + We have to log this query, even if it failed later to ensure the + drop is done. + */ + thd->variables.option_bits|= OPTION_KEEP_LOG; + thd->log_current_statement= 1; + + /* + The test of query_tables is to ensure we have any tables in the + select part + */ + if (thd->lex->query_tables && + restart_trans_for_tables(thd, thd->lex->query_tables->next_global)) + goto err; + } + else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); - goto err; + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + goto err; + } } } @@ -4702,14 +4764,14 @@ bool create_table_impl(THD *thd, } #endif - error= FALSE; + error= 0; err: THD_STAGE_INFO(thd, stage_after_create); delete file; DBUG_RETURN(error); warn: - error= FALSE; + error= -1; push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); @@ -4720,7 +4782,8 @@ warn: Simple wrapper around create_table_impl() to be used in various version of CREATE TABLE statement. */ -bool mysql_create_table_no_lock(THD *thd, + +int mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, HA_CREATE_INFO *create_info, Alter_info *alter_info, bool *is_trans, @@ -4728,6 +4791,7 @@ bool mysql_create_table_no_lock(THD *thd, { KEY *not_used_1; uint not_used_2; + int res; char path[FN_REFLEN + 1]; LEX_CUSTRING frm= {0,0}; @@ -4747,9 +4811,9 @@ bool mysql_create_table_no_lock(THD *thd, } } - bool res= create_table_impl(thd, db, table_name, path, create_info, - alter_info, create_table_mode, is_trans, - ¬_used_1, ¬_used_2, &frm); + res= create_table_impl(thd, db, table_name, path, create_info, + alter_info, create_table_mode, is_trans, + ¬_used_1, ¬_used_2, &frm); my_free(const_cast<uchar*>(frm.str)); return res; } @@ -4771,16 +4835,23 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, const char *db= create_table->db; const char *table_name= create_table->table_name; bool is_trans= FALSE; + bool result= 0; int create_table_mode; + TABLE_LIST *pos_in_locked_tables= 0; DBUG_ENTER("mysql_create_table"); + DBUG_ASSERT(create_table == thd->lex->query_tables); + /* Open or obtain an exclusive metadata lock on table being created */ if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0)) { /* is_error() may be 0 if table existed and we generated a warning */ DBUG_RETURN(thd->is_error()); } - + /* The following is needed only in case of lock tables */ + if ((create_info->table= thd->lex->query_tables->table)) + pos_in_locked_tables= create_info->table->pos_in_locked_tables; + /* Got lock. */ DEBUG_SYNC(thd, "locked_table_name"); @@ -4791,15 +4862,42 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, promote_first_timestamp_column(&alter_info->create_list); if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, - &is_trans, create_table_mode)) - DBUG_RETURN(1); + &is_trans, create_table_mode) > 0) + { + result= 1; + goto err; + } + + /* + Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES + on a non temporary table + */ + if (thd->locked_tables_mode && pos_in_locked_tables && + (create_info->options & HA_LEX_CREATE_REPLACE)) + { + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + else + { + TABLE *table= pos_in_locked_tables->table; + table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } +err: /* In RBR we don't need to log CREATE TEMPORARY TABLE */ if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) - DBUG_RETURN(0); - - bool result; - result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans); + DBUG_RETURN(result); + /* Write log if no error or if we already deleted a table */ + if (!result || thd->log_current_statement) + if (write_bin_log(thd, result ? FALSE : TRUE, thd->query(), + thd->query_length(), is_trans)) + result= 1; DBUG_RETURN(result); } @@ -4986,15 +5084,19 @@ mysql_rename_table(handlerton *base, const char *old_db, TRUE error */ -bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, +bool mysql_create_like_table(THD* thd, TABLE_LIST* table, + TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { HA_CREATE_INFO local_create_info; + TABLE_LIST *pos_in_locked_tables= 0; Alter_info local_alter_info; Alter_table_ctx local_alter_ctx; // Not used bool res= TRUE; bool is_trans= FALSE; + bool do_logging= FALSE; uint not_used; + int create_res; DBUG_ENTER("mysql_create_like_table"); #ifdef WITH_WSREP @@ -5038,7 +5140,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, String query(buf, sizeof(buf), system_charset_info); query.length(0); // Have to zero it since constructor doesn't - (void) store_create_info(thd, &tbl, &query, NULL, TRUE); + (void) store_create_info(thd, &tbl, &query, NULL, TRUE, FALSE); WSREP_DEBUG("TMP TABLE: %s", query.ptr()); thd->wsrep_TOI_pre_query= query.ptr(); @@ -5068,6 +5170,18 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, res= thd->is_error(); goto err; } + /* Ensure we don't try to create something from which we select from */ + if ((create_info->options & HA_LEX_CREATE_REPLACE) && + !create_info->tmp_table()) + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, table, src_table, 0))) + { + update_non_unique_table_error(src_table, "CREATE", duplicate); + goto err; + } + } + src_table->table->use_all_columns(); DEBUG_SYNC(thd, "create_table_like_after_open"); @@ -5095,7 +5209,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, if (src_table->schema_table) local_create_info.max_rows= 0; /* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */ - local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS; + local_create_info.options|= (create_info->options & + (HA_LEX_CREATE_IF_NOT_EXISTS | + HA_LEX_CREATE_REPLACE)); /* Replace type of source table with one specified in the statement. */ local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE; local_create_info.options|= create_info->tmp_table(); @@ -5107,19 +5223,54 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, */ local_create_info.data_file_name= local_create_info.index_file_name= NULL; - if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name, - &local_create_info, &local_alter_info, - &is_trans, C_ORDINARY_CREATE))) + /* The following is needed only in case of lock tables */ + if ((local_create_info.table= thd->lex->query_tables->table)) + pos_in_locked_tables= local_create_info.table->pos_in_locked_tables; + + res= ((create_res= + mysql_create_table_no_lock(thd, table->db, table->table_name, + &local_create_info, &local_alter_info, + &is_trans, C_ORDINARY_CREATE)) > 0); + /* Remember to log if we deleted something */ + do_logging= thd->log_current_statement; + if (res) goto err; /* - Ensure that we have an exclusive lock on target table if we are creating - non-temporary table. + Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES + on a non temporary table */ - DBUG_ASSERT((create_info->tmp_table()) || - thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, - table->table_name, - MDL_EXCLUSIVE)); + if (thd->locked_tables_mode && pos_in_locked_tables && + (create_info->options & HA_LEX_CREATE_REPLACE)) + { + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + else + { + /* + Get pointer to the newly opened table. We need this to ensure we + don't reopen the table when doing statment logging below. + */ + table->table= pos_in_locked_tables->table; + table->table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } + else + { + /* + Ensure that we have an exclusive lock on target table if we are creating + non-temporary table. + */ + DBUG_ASSERT((create_info->tmp_table()) || + thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, + table->table_name, + MDL_EXCLUSIVE)); + } DEBUG_SYNC(thd, "create_table_like_before_binlog"); @@ -5138,7 +5289,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Case Target Source Write to binary log ==== ========= ========= ============================== 1 normal normal Original statement - 2 normal temporary Generated statement + 2 normal temporary Generated statement if the table + was created. 3 temporary normal Nothing 4 temporary temporary Nothing ==== ========= ========= ============================== @@ -5153,36 +5305,59 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); bool new_table= FALSE; // Whether newly created table is open. + if (create_res != 0) + { + /* + Table or view with same name already existed and we where using + IF EXISTS. Continue without logging anything. + */ + goto err; + } + if (!table->table) + { + TABLE_LIST::enum_open_strategy save_open_strategy; + int open_res; + /* Force the newly created table to be opened */ + save_open_strategy= table->open_strategy; + table->open_strategy= TABLE_LIST::OPEN_NORMAL; + + /* + In order for store_create_info() to work we need to open + destination table if it is not already open (i.e. if it + has not existed before). We don't need acquire metadata + lock in order to do this as we already hold exclusive + lock on this table. The table will be closed by + close_thread_table() at the end of this branch. + */ + open_res= open_table(thd, table, thd->mem_root, &ot_ctx); + /* Restore */ + table->open_strategy= save_open_strategy; + if (open_res) + { + res= 1; + goto err; + } + new_table= TRUE; + } /* - The condition avoids a crash as described in BUG#48506. Other - binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE - when the existing object is a view will be solved by BUG 47442. + We have to re-test if the table was a view as the view may not + have been opened until just above. */ if (!table->view) { - if (!table->table) - { - - /* - In order for store_create_info() to work we need to open - destination table if it is not already open (i.e. if it - has not existed before). We don't need acquire metadata - lock in order to do this as we already hold exclusive - lock on this table. The table will be closed by - close_thread_table() at the end of this branch. - */ - if (open_table(thd, table, thd->mem_root, &ot_ctx)) - goto err; - new_table= TRUE; - } - int result __attribute__((unused))= store_create_info(thd, table, &query, - create_info, FALSE /* show_database */); + create_info, FALSE /* show_database */, + MY_TEST(create_info->options & + HA_LEX_CREATE_REPLACE)); DBUG_ASSERT(result == 0); // store_create_info() always return 0 + do_logging= FALSE; if (write_bin_log(thd, TRUE, query.ptr(), query.length())) + { + res= 1; goto err; + } if (new_table) { @@ -5197,17 +5372,20 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } } else // Case 1 - if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) - goto err; + do_logging= TRUE; } /* Case 3 and 4 does nothing under RBR */ } - else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans)) - goto err; + else + do_logging= TRUE; err: + if (do_logging && + write_bin_log(thd, res ? FALSE : TRUE, thd->query(), + thd->query_length(), is_trans)) + res= 1; DBUG_RETURN(res); #ifdef WITH_WSREP @@ -5365,7 +5543,8 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) { alter_info->flags&= ~Alter_info::ALTER_ADD_COLUMN; if (alter_info->key_list.is_empty()) - alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; + alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX | + Alter_info::ADD_FOREIGN_KEY); } break; } @@ -5440,13 +5619,32 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) else /* Alter_drop::KEY */ { uint n_key; - for (n_key=0; n_key < table->s->keys; n_key++) + if (drop->type != Alter_drop::FOREIGN_KEY) { - if (my_strcasecmp(system_charset_info, - drop->name, table->key_info[n_key].name) == 0) + for (n_key=0; n_key < table->s->keys; n_key++) { - remove_drop= FALSE; - break; + if (my_strcasecmp(system_charset_info, + drop->name, table->key_info[n_key].name) == 0) + { + remove_drop= FALSE; + break; + } + } + } + else + { + List <FOREIGN_KEY_INFO> fk_child_key_list; + FOREIGN_KEY_INFO *f_key; + table->file->get_foreign_key_list(thd, &fk_child_key_list); + List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list); + while ((f_key= fk_key_it++)) + { + if (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + drop->name) == 0) + { + remove_drop= FALSE; + break; + } } } } @@ -5458,7 +5656,8 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) drop_it.remove(); if (alter_info->drop_list.is_empty()) alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN | - Alter_info::ALTER_DROP_INDEX); + Alter_info::ALTER_DROP_INDEX | + Alter_info::DROP_FOREIGN_KEY); } } } @@ -5469,6 +5668,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) Key *key; List_iterator<Key> key_it(alter_info->key_list); uint n_key; + bool remove_key; const char *keyname; while ((key=key_it++)) { @@ -5485,24 +5685,48 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) if (keyname == NULL) continue; } - for (n_key=0; n_key < table->s->keys; n_key++) + remove_key= FALSE; + if (key->type != Key::FOREIGN_KEY) { - if (my_strcasecmp(system_charset_info, - keyname, table->key_info[n_key].name) == 0) + for (n_key=0; n_key < table->s->keys; n_key++) { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), keyname); - key_it.remove(); - if (key->type == Key::FOREIGN_KEY) + if (my_strcasecmp(system_charset_info, + keyname, table->key_info[n_key].name) == 0) { - /* ADD FOREIGN KEY appends two items. */ - key_it.remove(); + remove_key= TRUE; + break; } - if (alter_info->key_list.is_empty()) - alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; - break; } } + else + { + List <FOREIGN_KEY_INFO> fk_child_key_list; + FOREIGN_KEY_INFO *f_key; + table->file->get_foreign_key_list(thd, &fk_child_key_list); + List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list); + while ((f_key= fk_key_it++)) + { + if (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + key->name.str) == 0) + remove_key= TRUE; + break; + } + } + if (remove_key) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), keyname); + key_it.remove(); + if (key->type == Key::FOREIGN_KEY) + { + /* ADD FOREIGN KEY appends two items. */ + key_it.remove(); + } + if (alter_info->key_list.is_empty()) + alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX | + Alter_info::ADD_FOREIGN_KEY); + break; + } } } @@ -5732,9 +5956,6 @@ static bool fill_alter_inplace_info(THD *thd, if (new_field) { - ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]= - new_field->option_struct; - /* Field is not dropped. Evaluate changes bitmap for it. */ /* @@ -5846,6 +6067,15 @@ static bool fill_alter_inplace_info(THD *thd, if (new_field->column_format() != field->column_format()) ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT; + + if (engine_options_differ(field->option_struct, new_field->option_struct, + table->file->ht->field_options)) + { + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_OPTION; + ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]= + new_field->option_struct; + } + } else { @@ -7226,7 +7456,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key= new Key(key_type, key_name, strlen(key_name), &key_create_info, - test(key_info->flags & HA_GENERATED_KEY), + MY_TEST(key_info->flags & HA_GENERATED_KEY), key_parts, key_info->option_list, FALSE); new_key_list.push_back(key); } @@ -7798,9 +8028,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, it is the case. TODO: this design is obsolete and will be removed. */ - int table_kind= check_if_log_table(table_list->db_length, table_list->db, - table_list->table_name_length, - table_list->table_name, false); + int table_kind= check_if_log_table(table_list, FALSE, NullS); if (table_kind) { @@ -8841,7 +9069,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, DBUG_ENTER("copy_data_between_tables"); /* Two or 3 stages; Sorting, copying data and update indexes */ - thd_progress_init(thd, 2 + test(order)); + thd_progress_init(thd, 2 + MY_TEST(order)); if (mysql_trans_prepare_alter_copy_data(thd)) DBUG_RETURN(-1); @@ -9342,7 +9570,7 @@ static bool check_engine(THD *thd, const char *db_name, handlerton **new_engine= &create_info->db_type; handlerton *req_engine= *new_engine; bool no_substitution= - test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION); + MY_TEST(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION); if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine), no_substitution, 1))) DBUG_RETURN(true); diff --git a/sql/sql_table.h b/sql/sql_table.h index c42f8aaa39e..7255ce68743 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -19,6 +19,7 @@ #include "my_global.h" /* my_bool */ #include "my_sys.h" // pthread_mutex_t +#include "m_string.h" // LEX_CUSTRING class Alter_info; class Alter_table_ctx; @@ -187,11 +188,11 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, #define C_ALTER_TABLE_FRM_ONLY -2 #define C_ASSISTED_DISCOVERY -3 -bool mysql_create_table_no_lock(THD *thd, const char *db, - const char *table_name, - HA_CREATE_INFO *create_info, - Alter_info *alter_info, bool *is_trans, - int create_table_mode); +int mysql_create_table_no_lock(THD *thd, const char *db, + const char *table_name, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, bool *is_trans, + int create_table_mode); handler *mysql_create_frm_image(THD *thd, const char *db, const char *table_name, @@ -238,7 +239,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, my_bool drop_temporary); int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, - bool log_query); + bool log_query, bool dont_free_locks); bool quick_rm_table(THD *thd, handlerton *base, const char *db, const char *table_name, uint flags); void close_cached_table(THD *thd, TABLE *table); diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 922423bef26..8588d6564ca 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -94,12 +94,13 @@ static void print_cached_tables(void) TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables); while ((entry= it++)) { + THD *in_use= entry->in_use; printf("%-14.14s %-32s%6ld%8ld%6d %s\n", - entry->s->db.str, entry->s->table_name.str, entry->s->version, - entry->in_use ? entry->in_use->thread_id : 0, + entry->s->db.str, entry->s->table_name.str, entry->s->tdc.version, + in_use ? in_use->thread_id : 0, entry->db_stat ? 1 : 0, - entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : - "Not in use"); + in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : + "Not in use"); } } mysql_mutex_unlock(&LOCK_open); diff --git a/sql/sql_time.cc b/sql/sql_time.cc index b194d46643e..2c4998fdf5e 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -106,9 +106,9 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) uint days; ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day); ulong first_daynr=calc_daynr(l_time->year,1,1); - bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST); - bool week_year= test(week_behaviour & WEEK_YEAR); - bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY); + bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST); + bool week_year= MY_TEST(week_behaviour & WEEK_YEAR); + bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY); uint weekday=calc_weekday(first_daynr, !monday_first); *year=l_time->year; @@ -230,6 +230,20 @@ check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, } +bool +adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) +{ + MYSQL_TIME copy= *ltime; + ErrConvTime str(©); + int warnings= 0; + if (check_time_range(ltime, dec, &warnings)) + return true; + if (warnings) + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_TIME, NullS); + return false; +} + /* Convert a string to 8-bit representation, for use in str_to_time/str_to_date/str_to_date. @@ -1022,8 +1036,8 @@ null_date: */ bool -calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out, - long *microseconds_out) +calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, + int l_sign, longlong *seconds_out, long *microseconds_out) { long days; bool neg; @@ -1049,7 +1063,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s (uint) l_time2->day); } - microseconds= ((longlong)days*86400LL + + microseconds= ((longlong)days * SECONDS_IN_24H + (longlong)(l_time1->hour*3600L + l_time1->minute*60L + l_time1->second) - @@ -1119,3 +1133,146 @@ void time_to_daytime_interval(MYSQL_TIME *ltime) ltime->hour%= 24; ltime->time_type= MYSQL_TIMESTAMP_NONE; } + + +/*** Conversion from TIME to DATETIME ***/ + +/* + Simple case: TIME is within normal 24 hours internal. + Mix DATE part of ldate and TIME part of ltime together. +*/ +static void +mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || + ldate->time_type == MYSQL_TIMESTAMP_DATETIME); + ldate->hour= ltime->hour; + ldate->minute= ltime->minute; + ldate->second= ltime->second; + ldate->second_part= ltime->second_part; + ldate->time_type= MYSQL_TIMESTAMP_DATETIME; +} + + +/* + Complex case: TIME is negative or outside of the 24 hour interval. +*/ +static void +mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || + ldate->time_type == MYSQL_TIMESTAMP_DATETIME); + longlong seconds; + long days, useconds; + int sign= ltime->neg ? 1 : -1; + ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds); + + DBUG_ASSERT(!ldate->neg); + DBUG_ASSERT(ldate->year > 0); + + days= (long) (seconds / SECONDS_IN_24H); + calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds); + get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day); + ldate->time_type= MYSQL_TIMESTAMP_DATETIME; +} + + +/** + Mix a date value and a time value. + + @param IN/OUT ldate Date value. + @param ltime Time value. +*/ +static void +mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from) +{ + if (!from->neg && from->hour < 24) + mix_date_and_time_simple(to, from); + else + mix_date_and_time_complex(to, from); +} + + +/** + Get current date in DATE format +*/ +static void +set_current_date(THD *thd, MYSQL_TIME *to) +{ + thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start()); + thd->time_zone_used= 1; + datetime_to_date(to); +} + + +/** + 5.5 compatible conversion from TIME to DATETIME +*/ +static bool +time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) +{ + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); + + if (from->neg) + return true; + + /* Set the date part */ + uint day= from->hour / 24; + to->day= day % 31; + to->month= day / 31; + to->year= 0; + /* Set the time part */ + to->hour= from->hour % 24; + to->minute= from->minute; + to->second= from->second; + to->second_part= from->second_part; + /* set sign and type */ + to->neg= 0; + to->time_type= MYSQL_TIMESTAMP_DATETIME; + return false; +} + + +/** + Convert time to datetime. + + The time value is added to the current datetime value. + @param IN ltime Time value to convert from. + @param OUT ltime2 Datetime value to convert to. +*/ +bool +time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) +{ + if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) + return time_to_datetime_old(thd, from, to); + set_current_date(thd, to); + mix_date_and_time(to, from); + return false; +} + + +bool +time_to_datetime_with_warn(THD *thd, + const MYSQL_TIME *from, MYSQL_TIME *to, + ulonglong fuzzydate) +{ + int warn= 0; + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); + /* + After time_to_datetime() we need to do check_date(), as + the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE. + Note, the SQL standard time->datetime conversion mode always returns + a valid date based on CURRENT_DATE. So we need to do check_date() + only in the old mode. + */ + if (time_to_datetime(thd, from, to) || + ((thd->variables.old_behavior && OLD_MODE_ZERO_DATE_TIME_CAST) && + check_date(to, fuzzydate, &warn))) + { + ErrConvTime str(from); + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_DATETIME, 0); + return true; + } + return false; +} diff --git a/sql/sql_time.h b/sql/sql_time.h index 6a8c78ecd5e..7513ca7c00a 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -49,6 +49,33 @@ bool int_to_datetime_with_warn(longlong value, MYSQL_TIME *ltime, ulonglong fuzzydate, const char *name); +bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt); +bool time_to_datetime_with_warn(THD *thd, + const MYSQL_TIME *tm, MYSQL_TIME *dt, + ulonglong fuzzydate); +inline void datetime_to_time(MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE || + ltime->time_type == MYSQL_TIMESTAMP_DATETIME); + DBUG_ASSERT(ltime->neg == 0); + ltime->year= ltime->month= ltime->day= 0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; +} +inline void datetime_to_date(MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE || + ltime->time_type == MYSQL_TIMESTAMP_DATETIME); + DBUG_ASSERT(ltime->neg == 0); + ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; + ltime->time_type= MYSQL_TIMESTAMP_DATE; +} +inline void date_to_datetime(MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE || + ltime->time_type == MYSQL_TIMESTAMP_DATETIME); + DBUG_ASSERT(ltime->neg == 0); + ltime->time_type= MYSQL_TIMESTAMP_DATETIME; +} void make_truncated_value_warning(THD *thd, Sql_condition::enum_warning_level level, const ErrConv *str_val, @@ -76,8 +103,8 @@ bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec); /* MYSQL_TIME operations */ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval); -bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, - longlong *seconds_out, long *microseconds_out); +bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, + int l_sign, longlong *seconds_out, long *microseconds_out); int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); @@ -127,5 +154,6 @@ check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut) } bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, timestamp_type ts_type); +bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec); #endif /* SQL_TIME_INCLUDED */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 682528f9766..d137c2ab7aa 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2004, 2011, Oracle and/or its affiliates. + Copyright (c) 2004, 2012, Oracle and/or its affiliates. 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 diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index c792dca873c..fdc932957b2 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -352,6 +352,7 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) if (!initialized) DBUG_RETURN(NULL); + DEBUG_SYNC(current_thd, "find_udf_before_lock"); /* TODO: This should be changed to reader locks someday! */ if (mark_used) mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ @@ -458,7 +459,12 @@ int mysql_create_function(THD *thd,udf_func *udf) DBUG_RETURN(1); } + tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), + "func", TL_WRITE); + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + mysql_rwlock_wrlock(&THR_LOCK_udf); + DEBUG_SYNC(current_thd, "mysql_create_function_after_lock"); if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) { my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); @@ -502,9 +508,8 @@ int mysql_create_function(THD *thd,udf_func *udf) /* create entry in mysql.func table */ - tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); /* Allow creation of functions even if we can't open func table */ - if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + if (!table) goto err; table->use_all_columns(); restore_record(table, s->default_values); // Default values for fields @@ -555,7 +560,12 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) DBUG_RETURN(1); } + tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), + "func", TL_WRITE); + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + mysql_rwlock_wrlock(&THR_LOCK_udf); + DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock"); if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str, (uint) udf_name->length))) { @@ -572,9 +582,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle); - tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); - - if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + if (!table) goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index d143930908d..9d068e464f5 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -243,7 +243,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, bool is_union_select; DBUG_ENTER("st_select_lex_unit::prepare"); - describe= test(additional_options & SELECT_DESCRIBE); + describe= MY_TEST(additional_options & SELECT_DESCRIBE); /* result object should be reassigned even if preparing already done for @@ -450,7 +450,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (global_parameters->ftfunc_list->elements) create_options= create_options | TMP_TABLE_FORCE_MYISAM; - if (union_result->create_result_table(thd, &types, test(union_distinct), + if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct), create_options, "", FALSE, TRUE)) goto err; if (fake_select_lex && !fake_select_lex->first_cond_optimization) @@ -647,13 +647,19 @@ bool st_select_lex_unit::exec() if (!saved_error && !was_executed) save_union_explain(thd->lex->explain); + if (saved_error) + DBUG_RETURN(saved_error); + if (uncacheable || !item || !item->assigned() || describe) { for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { ha_rows records_at_start= 0; thd->lex->current_select= sl; - fake_select_lex->uncacheable|= sl->uncacheable; + if (sl != &thd->lex->select_lex) + fake_select_lex->uncacheable|= sl->uncacheable; + else + fake_select_lex->uncacheable= 0; { set_limit(sl); @@ -985,7 +991,7 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result, List<Item> *st_select_lex_unit::get_unit_column_types() { SELECT_LEX *sl= first_select(); - bool is_procedure= test(sl->join->procedure); + bool is_procedure= MY_TEST(sl->join->procedure); if (is_procedure) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b85f5b90cd7..528e76d8d32 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -191,7 +191,7 @@ static void prepare_record_for_error_message(int error, TABLE *table) DBUG_VOID_RETURN; /* Create unique_map with all fields used by that index. */ - bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); + my_bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); table->mark_columns_used_by_index_no_reset(keynr, &unique_map); /* Subtract read_set and write_set. */ @@ -255,7 +255,7 @@ int mysql_update(THD *thd, ha_rows *found_return, ha_rows *updated_return) { bool using_limit= limit != HA_POS_ERROR; - bool safe_update= test(thd->variables.option_bits & OPTION_SAFE_UPDATES); + bool safe_update= MY_TEST(thd->variables.option_bits & OPTION_SAFE_UPDATES); bool used_key_is_modified= FALSE, transactional_table, will_batch; bool can_compare_record; int res; @@ -314,7 +314,7 @@ int mysql_update(THD *thd, my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); } - query_plan.updating_a_view= test(table_list->view); + query_plan.updating_a_view= MY_TEST(table_list->view); /* Calculate "table->covering_keys" based on the WHERE */ table->covering_keys= table->s->keys_in_use; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index e1c92a81604..4ca165a6d19 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2004, 2011, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab +/* Copyright (c) 2004, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2014, SkySQL 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 @@ -1093,11 +1093,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, will be TRUE as far as we make new table cache). */ old_lex= thd->lex; - arena= thd->stmt_arena; - if (arena->is_conventional()) - arena= 0; - else - thd->set_n_backup_active_arena(arena, &backup); + arena= thd->activate_stmt_arena_if_needed(&backup); /* init timestamp */ if (!table->timestamp.str) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7514b7bec63..ceb4e247848 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -344,7 +344,7 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple) (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) */ - return !test(i) || + return !MY_TEST(i) || sp->push_backpatch(i, ctx->push_label(current_thd, empty_lex_str, 0)) || sp->add_cont_backpatch(i) || sp->add_instr(i); @@ -362,7 +362,7 @@ int case_stmt_action_then(LEX *lex) sp_pcontext *ctx= lex->spcont; uint ip= sp->instructions(); sp_instr_jump *i = new sp_instr_jump(ip, ctx); - if (!test(i) || sp->add_instr(i)) + if (!MY_TEST(i) || sp->add_instr(i)) return 1; /* @@ -1155,6 +1155,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token EXISTS /* SQL-2003-R */ %token EXIT_SYM %token EXPANSION_SYM +%token EXPORT_SYM %token EXTENDED_SYM %token EXTENT_SIZE_SYM %token EXTRACT_SYM /* SQL-2003-N */ @@ -1664,7 +1665,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <num> type type_with_opt_collate int_type real_type order_dir lock_option udf_type opt_if_exists opt_local opt_table_options table_options - table_option opt_if_not_exists opt_no_write_to_binlog + table_option opt_if_not_exists create_or_replace opt_no_write_to_binlog opt_temporary all_or_any opt_distinct opt_ignore_leaves fulltext_options spatial_type union_option field_def @@ -1771,7 +1772,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <symbol> keyword keyword_sp %type <lex_user> user grant_user grant_role user_or_role current_role - admin_option_for_role + admin_option_for_role user_maybe_role %type <charset> opt_collate @@ -1828,7 +1829,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); object_privilege object_privilege_list user_list user_and_role_list rename_list clear_privileges flush_options flush_option - opt_with_read_lock flush_options_list + opt_flush_lock flush_lock flush_options_list equal optional_braces opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as @@ -1843,7 +1844,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); statement sp_suid sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa opt_field_or_var_spec fields_or_vars opt_load_data_set_spec - view_replace_or_algorithm view_replace view_algorithm view_or_trigger_or_sp_or_event definer_tail no_definer_tail view_suid view_tail view_list_opt view_list view_select @@ -2341,25 +2341,29 @@ connection_name: /* create a table */ create: - CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident + create_or_replace opt_table_options TABLE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; lex->sql_command= SQLCOM_CREATE_TABLE; + if ($1 && $4) + { + my_error(ER_WRONG_USAGE, MYF(0), "OR REPLACE", "IF NOT EXISTS"); + MYSQL_YYABORT; + } if (!lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; - /* - For CREATE TABLE, an non-existing table is not an error. - Instruct open_tables() to just take an MDL lock if the - table does not exist. - */ - lex->query_tables->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; lex->alter_info.reset(); lex->col_list.empty(); lex->change=NullS; bzero((char*) &lex->create_info,sizeof(lex->create_info)); - lex->create_info.options=$2 | $4; + /* + For CREATE TABLE we should not open the table even if it exists. + If the table exists, we should either not create it or replace it + */ + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->create_info.options= ($1 | $2 | $4); lex->create_info.default_table_charset= NULL; lex->name.str= 0; lex->name.length= 0; @@ -2428,14 +2432,22 @@ create: lex->name= $4; lex->create_info.options=$3; } - | CREATE + | create_or_replace { - Lex->create_view_mode= VIEW_CREATE_NEW; + Lex->create_view_mode= ($1 == 0 ? VIEW_CREATE_NEW : + VIEW_CREATE_OR_REPLACE); Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; } view_or_trigger_or_sp_or_event - {} + { + if ($1 && Lex->sql_command != SQLCOM_CREATE_VIEW) + { + my_error(ER_WRONG_USAGE, MYF(0), "OR REPLACE", + "TRIGGERS / SP / EVENT"); + MYSQL_YYABORT; + } + } | CREATE USER clear_privileges grant_list { Lex->sql_command = SQLCOM_CREATE_USER; @@ -5515,6 +5527,17 @@ opt_if_not_exists: } ; +create_or_replace: + CREATE /* empty */ + { + $$= 0; + } + | CREATE OR_SYM REPLACE + { + $$= HA_LEX_CREATE_REPLACE; + } + ; + opt_create_table_options: /* empty */ | create_table_options @@ -10262,7 +10285,7 @@ variable_aux: if ($$ == NULL) MYSQL_YYABORT; LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_RAND); + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); lex->set_var_list.push_back(item); } | ident_or_text @@ -10271,7 +10294,7 @@ variable_aux: if ($$ == NULL) MYSQL_YYABORT; LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_RAND); + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); } | '@' opt_var_ident_type ident_or_text opt_component { @@ -12778,24 +12801,36 @@ flush_options: YYPS->m_lock_type= TL_READ_NO_INSERT; YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO; } - opt_table_list {} - opt_with_read_lock {} + opt_table_list opt_flush_lock | flush_options_list ; -opt_with_read_lock: +opt_flush_lock: /* empty */ {} - | WITH READ_SYM LOCK_SYM optional_flush_tables_arguments + | flush_lock + { + TABLE_LIST *tables= Lex->query_tables; + for (; tables; tables= tables->next_global) { - TABLE_LIST *tables= Lex->query_tables; - Lex->type|= REFRESH_READ_LOCK | $4; - for (; tables; tables= tables->next_global) - { - tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); - tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ - } + tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); + tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ + tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ } + } + ; + +flush_lock: + WITH READ_SYM LOCK_SYM optional_flush_tables_arguments + { Lex->type|= REFRESH_READ_LOCK | $4; } + | FOR_SYM + { + if (Lex->query_tables == NULL) // Table list can't be empty + { + my_parse_error(ER(ER_NO_TABLES_USED)); + MYSQL_YYABORT; + } + Lex->type|= REFRESH_FOR_EXPORT; + } EXPORT_SYM ; flush_options_list: @@ -13916,7 +13951,7 @@ ident_or_text: | LEX_HOSTNAME { $$=$1;} ; -user: +user_maybe_role: ident_or_text { if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) @@ -13974,7 +14009,15 @@ user: } ; -user_or_role: user | current_role; +user_or_role: user_maybe_role | current_role; + +user: user_maybe_role + { + if ($1->user.str != current_user.str && $1->host.str == 0) + $1->host= host_not_specified; + $$= $1; + } + ; /* Keyword that we allow for identifiers (except SP labels) */ keyword: @@ -14131,6 +14174,7 @@ keyword_sp: | EVERY_SYM {} | EXCHANGE_SYM {} | EXPANSION_SYM {} + | EXPORT_SYM {} | EXTENDED_SYM {} | EXTENT_SIZE_SYM {} | FAULTS_SYM {} @@ -15148,6 +15192,11 @@ current_role: grant_role: ident_or_text { + if ($1.length == 0) + { + my_error(ER_INVALID_ROLE, MYF(0), ""); + MYSQL_YYABORT; + } if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; $$->user = $1; @@ -15799,7 +15848,7 @@ view_or_trigger_or_sp_or_event: {} | no_definer no_definer_tail {} - | view_replace_or_algorithm definer_opt view_tail + | view_algorithm definer_opt view_tail {} ; @@ -15858,20 +15907,6 @@ definer: **************************************************************************/ -view_replace_or_algorithm: - view_replace - {} - | view_replace view_algorithm - {} - | view_algorithm - {} - ; - -view_replace: - OR_SYM REPLACE - { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; } - ; - view_algorithm: ALGORITHM_SYM EQ UNDEFINED_SYM { Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } diff --git a/sql/strfunc.cc b/sql/strfunc.cc index a5a64c065ce..b8100e05ce5 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2003, 2012, 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 diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index f7b44af4e8b..415afa49cb2 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. - Copyright (c) 2012, Monty Program Ab +/* Copyright (c) 2002, 2013, Oracle and/or its affiliates. + Copyright (c) 2012, 2014, SkySQL 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 @@ -364,7 +364,7 @@ static Sys_var_ulong Sys_back_log( "MySQL can have. This comes into play when the main MySQL thread " "gets very many connection requests in a very short time", READ_ONLY GLOBAL_VAR(back_log), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, 65535), DEFAULT(50), BLOCK_SIZE(1)); + VALID_RANGE(1, 65535), DEFAULT(150), BLOCK_SIZE(1)); static Sys_var_charptr Sys_basedir( "basedir", "Path to installation directory. All paths are " @@ -613,7 +613,7 @@ static bool check_charset_db(sys_var *self, THD *thd, set_var *var) } static Sys_var_struct Sys_character_set_database( "character_set_database", - " The character set used by the default database", + "The character set used by the default database", SESSION_VAR(collation_database), NO_CMD_LINE, offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info), NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_charset_db)); @@ -1082,7 +1082,7 @@ static Sys_var_keycache Sys_key_cache_age_threshold( static Sys_var_mybool Sys_large_files_support( "large_files_support", "Whether mysqld was compiled with options for large file support", - READ_ONLY GLOBAL_VAR(opt_large_files), + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(opt_large_files), NO_CMD_LINE, DEFAULT(sizeof(my_off_t) > 4)); static Sys_var_uint Sys_large_page_size( @@ -1204,7 +1204,8 @@ static Sys_var_mybool Sys_lower_case_file_system( "lower_case_file_system", "Case sensitivity of file names on the file system where the " "data directory is located", - READ_ONLY GLOBAL_VAR(lower_case_file_system), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(lower_case_file_system), + NO_CMD_LINE, DEFAULT(FALSE)); static Sys_var_uint Sys_lower_case_table_names( @@ -1715,6 +1716,33 @@ static Sys_var_gtid_binlog_state Sys_gtid_binlog_state( GLOBAL_VAR(opt_gtid_binlog_state_dummy), NO_CMD_LINE); +static Sys_var_last_gtid Sys_last_gtid( + "last_gtid", "The GTID of the last commit (if binlogging was enabled), " + "or the empty string if none.", + READ_ONLY sys_var::ONLY_SESSION, NO_CMD_LINE); + + +uchar * +Sys_var_last_gtid::session_value_ptr(THD *thd, LEX_STRING *base) +{ + char buf[10+1+10+1+20+1]; + String str(buf, sizeof(buf), system_charset_info); + char *p; + bool first= true; + + str.length(0); + if ((thd->last_commit_gtid.seq_no > 0 && + rpl_slave_state_tostring_helper(&str, &thd->last_commit_gtid, &first)) || + !(p= thd->strmake(str.ptr(), str.length()))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return NULL; + } + + return (uchar *)p; +} + + static bool check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var) { @@ -1760,6 +1788,49 @@ static Sys_var_ulong Sys_slave_parallel_threads( ON_UPDATE(fix_slave_parallel_threads)); +static bool +check_slave_domain_parallel_threads(sys_var *self, THD *thd, set_var *var) +{ + bool running; + + mysql_mutex_lock(&LOCK_active_mi); + running= master_info_index->give_error_if_slave_running(); + mysql_mutex_unlock(&LOCK_active_mi); + if (running) + return true; + + return false; +} + +static bool +fix_slave_domain_parallel_threads(sys_var *self, THD *thd, enum_var_type type) +{ + bool running; + + mysql_mutex_unlock(&LOCK_global_system_variables); + mysql_mutex_lock(&LOCK_active_mi); + running= master_info_index->give_error_if_slave_running(); + mysql_mutex_unlock(&LOCK_active_mi); + mysql_mutex_lock(&LOCK_global_system_variables); + + return running ? true : false; +} + + +static Sys_var_ulong Sys_slave_domain_parallel_threads( + "slave_domain_parallel_threads", + "Maximum number of parallel threads to use on slave for events in a " + "single replication domain. When using multiple domains, this can be " + "used to limit a single domain from grabbing all threads and thus " + "stalling other domains. The default of 0 means to allow a domain to " + "grab as many threads as it wants, up to the value of " + "slave_parallel_threads.", + GLOBAL_VAR(opt_slave_domain_parallel_threads), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,16383), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD, + NOT_IN_BINLOG, ON_CHECK(check_slave_domain_parallel_threads), + ON_UPDATE(fix_slave_domain_parallel_threads)); + + static Sys_var_ulong Sys_slave_parallel_max_queued( "slave_parallel_max_queued", "Limit on how much memory SQL threads should use per parallel " @@ -1971,7 +2042,7 @@ static Sys_var_ulong Sys_net_retry_count( ON_UPDATE(fix_net_retry_count)); static Sys_var_mybool Sys_old_mode( - "old", "Use compatible behavior", + "old", "Use compatible behavior from previous MariaDB version. See also --old-mode", SESSION_VAR(old_mode), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); static Sys_var_mybool Sys_old_alter_table( @@ -2094,7 +2165,7 @@ static bool fix_optimizer_switch(sys_var *self, THD *thd, { SV *sv= (type == OPT_GLOBAL) ? &global_system_variables : &thd->variables; sv->engine_condition_pushdown= - test(sv->optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN); + MY_TEST(sv->optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN); return false; } static Sys_var_flagset Sys_optimizer_switch( @@ -2165,7 +2236,7 @@ static Sys_var_ulong Sys_preload_buff_size( static Sys_var_uint Sys_protocol_version( "protocol_version", "The version of the client/server protocol used by the MySQL server", - READ_ONLY GLOBAL_VAR(protocol_version), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(protocol_version), NO_CMD_LINE, VALID_RANGE(0, ~0), DEFAULT(PROTOCOL_VERSION), BLOCK_SIZE(1)); static Sys_var_proxy_user Sys_proxy_user( @@ -2381,9 +2452,13 @@ static Sys_var_charptr Sys_socket( static Sys_var_ulong Sys_thread_concurrency( "thread_concurrency", "Permits the application to give the threads system a hint for " - "the desired number of threads that should be run at the same time", + "the desired number of threads that should be run at the same time." + "This variable has no effect, and is deprecated. " + "It will be removed in a future release.", READ_ONLY GLOBAL_VAR(concurrency), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, 512), DEFAULT(DEFAULT_CONCURRENCY), BLOCK_SIZE(1)); + VALID_RANGE(1, 512), DEFAULT(DEFAULT_CONCURRENCY), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), + DEPRECATED("")); static Sys_var_ulonglong Sys_thread_stack( "thread_stack", "The stack size for each thread", @@ -2620,11 +2695,23 @@ static Sys_var_enum Slave_exec_mode( "Modes for how replication events should be executed. Legal values " "are STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, " "replication will not stop for operations that are idempotent. " + "For example, in row based replication attempts to delete rows that " + "doesn't exist will be ignored." "In STRICT mode, replication will stop on any unexpected difference " "between the master and the slave", GLOBAL_VAR(slave_exec_mode_options), CMD_LINE(REQUIRED_ARG), slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_STRICT)); +static Sys_var_enum Slave_ddl_exec_mode( + "slave_ddl_exec_mode", + "Modes for how replication events should be executed. Legal values " + "are STRICT and IDEMPOTENT (default). In IDEMPOTENT mode, " + "replication will not stop for DDL operations that are idempotent. " + "This means that CREATE TABLE is treated CREATE TABLE OR REPLACE and " + "DROP TABLE is threated as DROP TABLE IF EXISTS. ", + GLOBAL_VAR(slave_ddl_exec_mode_options), CMD_LINE(REQUIRED_ARG), + slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT)); + static const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0}; static Sys_var_set Slave_type_conversions( "slave_type_conversions", @@ -2807,6 +2894,32 @@ static Sys_var_set Sys_sql_mode( sql_mode_names, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_sql_mode), ON_UPDATE(fix_sql_mode)); +static const char *old_mode_names[]= +{ + "NO_DUP_KEY_WARNINGS_WITH_IGNORE", + "NO_PROGRESS_INFO", + "ZERO_DATE_TIME_CAST", + 0 +}; + +export bool old_mode_string_representation(THD *thd, ulonglong sql_mode, + LEX_STRING *ls) +{ + set_to_string(thd, ls, sql_mode, old_mode_names); + return ls->str == 0; +} +/* + sql_mode should *not* be IN_BINLOG as the slave can't remember this + anyway on restart. +*/ +static Sys_var_set Sys_old_behavior( + "old_mode", + "Used to emulate old behavior from earlier MariaDB or MySQL versions. " + "Syntax: old_mode=mode[,mode[,mode...]]. " + "See the manual for the complete list of valid old modes", + SESSION_VAR(old_behavior), CMD_LINE(REQUIRED_ARG), + old_mode_names, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG); + #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) #define SSL_OPT(X) CMD_LINE(REQUIRED_ARG,X) #else @@ -2873,7 +2986,8 @@ static Sys_var_mybool Sys_sync_frm( static char *system_time_zone_ptr; static Sys_var_charptr Sys_system_time_zone( "system_time_zone", "The server system time zone", - READ_ONLY GLOBAL_VAR(system_time_zone_ptr), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(system_time_zone_ptr), + NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(system_time_zone)); static Sys_var_ulong Sys_table_def_size( @@ -3083,27 +3197,37 @@ static Sys_var_mybool Sys_timed_mutexes( static char *server_version_ptr; static Sys_var_charptr Sys_version( "version", "Server version", - READ_ONLY GLOBAL_VAR(server_version_ptr), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(server_version_ptr), + NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(server_version)); static char *server_version_comment_ptr; static Sys_var_charptr Sys_version_comment( "version_comment", "version_comment", - READ_ONLY GLOBAL_VAR(server_version_comment_ptr), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(server_version_comment_ptr), + NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(MYSQL_COMPILATION_COMMENT)); static char *server_version_compile_machine_ptr; static Sys_var_charptr Sys_version_compile_machine( "version_compile_machine", "version_compile_machine", - READ_ONLY GLOBAL_VAR(server_version_compile_machine_ptr), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP + GLOBAL_VAR(server_version_compile_machine_ptr), NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(MACHINE_TYPE)); static char *server_version_compile_os_ptr; static Sys_var_charptr Sys_version_compile_os( "version_compile_os", "version_compile_os", - READ_ONLY GLOBAL_VAR(server_version_compile_os_ptr), NO_CMD_LINE, + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(server_version_compile_os_ptr), + NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE)); +static char *malloc_library; +static Sys_var_charptr Sys_malloc_library( + "version_malloc_library", "Version of the used malloc library", + READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(malloc_library), NO_CMD_LINE, + IN_SYSTEM_CHARSET, DEFAULT(MALLOC_LIBRARY)); + static Sys_var_ulong Sys_net_wait_timeout( "wait_timeout", "The number of seconds the server waits for activity on a " @@ -3182,10 +3306,10 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type) return false; } - if (thd->variables.option_bits & OPTION_AUTOCOMMIT && - thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT) - { // activating autocommit - + if (test_all_bits(thd->variables.option_bits, + (OPTION_AUTOCOMMIT | OPTION_NOT_AUTOCOMMIT))) + { + // activating autocommit if (trans_commit_stmt(thd) || trans_commit(thd)) { thd->variables.option_bits&= ~OPTION_AUTOCOMMIT; @@ -3204,16 +3328,17 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type) transaction implicitly at the end (@sa stmt_causes_implicitcommit()). */ thd->variables.option_bits&= - ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT); + ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT | + OPTION_GTID_BEGIN); thd->transaction.all.modified_non_trans_table= false; thd->server_status|= SERVER_STATUS_AUTOCOMMIT; return false; } - if (!(thd->variables.option_bits & OPTION_AUTOCOMMIT) && - !(thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT)) - { // disabling autocommit - + if ((thd->variables.option_bits & + (OPTION_AUTOCOMMIT |OPTION_NOT_AUTOCOMMIT)) == 0) + { + // disabling autocommit thd->transaction.all.modified_non_trans_table= false; thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; thd->variables.option_bits|= OPTION_NOT_AUTOCOMMIT; @@ -3222,6 +3347,7 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type) return false; // autocommit value wasn't changed } + static Sys_var_bit Sys_autocommit( "autocommit", "autocommit", SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_AUTOCOMMIT, DEFAULT(TRUE), @@ -4120,13 +4246,18 @@ bool update_multi_source_variable(sys_var *self_var, THD *thd, static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi) { + if (mi->using_gtid != Master_info::USE_GTID_NO) + { + my_error(ER_SLAVE_SKIP_NOT_IN_GTID, MYF(0)); + return true; + } if (mi->rli.slave_running) { my_error(ER_SLAVE_MUST_STOP, MYF(0), mi->connection_name.length, mi->connection_name.str); return true; } - /* The value was stored temporarly in thd */ + /* The value was stored temporarily in thd */ mi->rli.slave_skip_counter= thd->variables.slave_skip_counter; return false; } @@ -4707,7 +4838,7 @@ static Sys_var_ulong Sys_progress_report_time( "Seconds between sending progress reports to the client for " "time-consuming statements. Set to 0 to disable progress reporting.", SESSION_VAR(progress_report_time), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(0, UINT_MAX), DEFAULT(56), BLOCK_SIZE(1)); + VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1)); const char *use_stat_tables_modes[] = {"NEVER", "COMPLEMENTARY", "PREFERABLY", 0}; @@ -4751,7 +4882,7 @@ static Sys_var_mybool Sys_query_cache_strip_comments( static ulonglong in_transaction(THD *thd) { - return test(thd->in_active_multi_stmt_transaction()); + return MY_TEST(thd->in_active_multi_stmt_transaction()); } static Sys_var_session_special Sys_in_transaction( "in_transaction", "Whether there is an active transaction", diff --git a/sql/sys_vars.h b/sql/sys_vars.h index bef5fbdd126..495099b9c59 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -56,6 +56,7 @@ // this means that Sys_var_charptr initial value was malloc()ed #define PREALLOCATED sys_var::ALLOCATED+ #define PARSED_EARLY sys_var::PARSE_EARLY+ +#define SHOW_VALUE_IN_HELP sys_var::SHOW_VALUE_IN_HELP+ /* Sys_var_bit meaning is reversed, like in @@ -2238,3 +2239,53 @@ public: } uchar *global_value_ptr(THD *thd, LEX_STRING *base); }; + + +/** + Class for @@session.last_gtid. +*/ +class Sys_var_last_gtid: public sys_var +{ +public: + Sys_var_last_gtid(const char *name_arg, + const char *comment, int flag_args, CMD_LINE getopt) + : sys_var(&all_sys_vars, name_arg, comment, flag_args, 0, getopt.id, + getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG, + NULL, NULL, NULL) + { + option.var_type= GET_STR; + } + bool do_check(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + return true; + } + bool session_update(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + return true; + } + bool global_update(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + return true; + } + bool check_update_type(Item_result type) { + DBUG_ASSERT(false); + return false; + } + void session_save_default(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + } + void global_save_default(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + } + uchar *session_value_ptr(THD *thd, LEX_STRING *base); + uchar *global_value_ptr(THD *thd, LEX_STRING *base) + { + DBUG_ASSERT(false); + return NULL; + } +}; diff --git a/sql/table.cc b/sql/table.cc index 385de6547db..0548796a424 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,6 +1,5 @@ -/* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab. +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2014, SkySQL 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 @@ -318,8 +317,6 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, strmov(share->path.str, path); share->normalized_path.str= share->path.str; share->normalized_path.length= path_length; - /* TEMPORARY FIX: if true, this means this is mysql.gtid_slave_pos table */ - share->is_gtid_slave_pos= FALSE; share->table_category= get_table_category(& share->db, & share->table_name); share->open_errno= ENOENT; share->cached_row_logging_check= -1; @@ -778,7 +775,6 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, keyinfo->ext_key_part_map= 0; if (share->use_ext_keys && i && !(keyinfo->flags & HA_NOSAME)) { - keyinfo->ext_key_part_map= 0; for (j= 0; j < first_key_parts && keyinfo->ext_key_parts < MAX_REF_PARTS; j++) @@ -825,8 +821,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, keyinfo->comment.length); strpos+= keyinfo->comment.length; } - DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) == - (keyinfo->comment.length > 0)); + DBUG_ASSERT(MY_TEST(keyinfo->flags & HA_USES_COMMENT) == + (keyinfo->comment.length > 0)); } share->keys= keys; // do it *after* all key_info's are initialized @@ -2016,7 +2012,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, share->column_bitmap_size))) goto err; - bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); + my_bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); bitmap_set_all(&share->all_set); delete handler_file; @@ -2824,17 +2820,17 @@ partititon_err: bitmap_size= share->column_bitmap_size; if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*6))) goto err; - bitmap_init(&outparam->def_read_set, + my_bitmap_init(&outparam->def_read_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); - bitmap_init(&outparam->def_write_set, + my_bitmap_init(&outparam->def_write_set, (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE); - bitmap_init(&outparam->def_vcol_set, + my_bitmap_init(&outparam->def_vcol_set, (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE); - bitmap_init(&outparam->tmp_set, + my_bitmap_init(&outparam->tmp_set, (my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE); - bitmap_init(&outparam->eq_join_set, + my_bitmap_init(&outparam->eq_join_set, (my_bitmap_map*) (bitmaps+bitmap_size*4), share->fields, FALSE); - bitmap_init(&outparam->cond_set, + my_bitmap_init(&outparam->cond_set, (my_bitmap_map*) (bitmaps+bitmap_size*5), share->fields, FALSE); outparam->default_column_bitmaps(); @@ -2895,9 +2891,9 @@ partititon_err: else if (outparam->file) { handler::Table_flags flags= outparam->file->ha_table_flags(); - outparam->no_replicate= ! test(flags & (HA_BINLOG_STMT_CAPABLE - | HA_BINLOG_ROW_CAPABLE)) - || test(flags & HA_HAS_OWN_BINLOGGING); + outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE + | HA_BINLOG_ROW_CAPABLE)) + || MY_TEST(flags & HA_HAS_OWN_BINLOGGING); } else { @@ -3259,7 +3255,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, /* header */ fileinfo[0]=(uchar) 254; fileinfo[1]= 1; - fileinfo[2]= FRM_VER+3+ test(create_info->varchar); + fileinfo[2]= FRM_VER + 3 + MY_TEST(create_info->varchar); fileinfo[3]= (uchar) ha_legacy_type( ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0)); @@ -3278,8 +3274,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, */ for (i= 0; i < keys; i++) { - DBUG_ASSERT(test(key_info[i].flags & HA_USES_COMMENT) == - (key_info[i].comment.length > 0)); + DBUG_ASSERT(MY_TEST(key_info[i].flags & HA_USES_COMMENT) == + (key_info[i].comment.length > 0)); if (key_info[i].flags & HA_USES_COMMENT) key_comment_total_bytes += 2 + key_info[i].comment.length; } @@ -3811,7 +3807,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, bool result= TRUE; /* - To protect used_tables list from being concurrently modified + To protect all_tables list from being concurrently modified while we are iterating through it we acquire LOCK_open. This does not introduce deadlocks in the deadlock detector because we won't try to acquire LOCK_open while @@ -3838,7 +3834,8 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, while ((table= tables_it++)) { - if (table->in_use && gvisitor->inspect_edge(&table->in_use->mdl_context)) + DBUG_ASSERT(table->in_use && tdc.flushed); + if (gvisitor->inspect_edge(&table->in_use->mdl_context)) { goto end_leave_node; } @@ -3847,7 +3844,8 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, tables_it.rewind(); while ((table= tables_it++)) { - if (table->in_use && table->in_use->mdl_context.visit_subgraph(gvisitor)) + DBUG_ASSERT(table->in_use && tdc.flushed); + if (table->in_use->mdl_context.visit_subgraph(gvisitor)) { goto end_leave_node; } @@ -3896,7 +3894,7 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime, MDL_wait::enum_wait_status wait_status; mysql_mutex_assert_owner(&tdc.LOCK_table_share); - DBUG_ASSERT(has_old_version()); + DBUG_ASSERT(tdc.flushed); tdc.m_flush_tickets.push_front(&ticket); @@ -4137,8 +4135,9 @@ bool TABLE_LIST::create_field_translation(THD *thd) SELECT_LEX *select= get_single_select(); List_iterator_fast<Item> it(select->item_list); uint field_count= 0; - Query_arena *arena= thd->stmt_arena, backup; + Query_arena *arena, backup; bool res= FALSE; + DBUG_ENTER("TABLE_LIST::create_field_translation"); if (thd->stmt_arena->is_conventional() || thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) @@ -4159,7 +4158,7 @@ bool TABLE_LIST::create_field_translation(THD *thd) if (field_translation) { /* - Update items in the field translation aftet view have been prepared. + Update items in the field translation after view have been prepared. It's needed because some items in the select list, like IN subselects, might be substituted for optimized ones. */ @@ -4172,13 +4171,10 @@ bool TABLE_LIST::create_field_translation(THD *thd) field_translation_updated= TRUE; } - return FALSE; + DBUG_RETURN(FALSE); } - if (arena->is_conventional()) - arena= 0; // For easier test - else - thd->set_n_backup_active_arena(arena, &backup); + arena= thd->activate_stmt_arena_if_needed(&backup); /* Create view fields translation table */ @@ -4198,12 +4194,14 @@ bool TABLE_LIST::create_field_translation(THD *thd) } field_translation= transl; field_translation_end= transl + field_count; + /* It's safe to cache this table for prepared statements */ + cacheable_table= 1; exit: if (arena) thd->restore_active_arena(arena, &backup); - return res; + DBUG_RETURN(res); } @@ -6193,9 +6191,9 @@ bool TABLE::is_filled_at_execution() do not have a corresponding table reference. Such tables are filled during execution. */ - return test(!pos_in_table_list || - pos_in_table_list->jtbm_subselect || - pos_in_table_list->is_active_sjm()); + return MY_TEST(!pos_in_table_list || + pos_in_table_list->jtbm_subselect || + pos_in_table_list->is_active_sjm()); } diff --git a/sql/table.h b/sql/table.h index c25e2c8a83a..6f16bc55c9e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1,7 +1,7 @@ #ifndef TABLE_INCLUDED #define TABLE_INCLUDED -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2011 Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, SkySQL 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 @@ -194,10 +194,20 @@ private: /* Order clause list element */ +typedef int (*fast_field_copier)(Field *to, Field *from); + + typedef struct st_order { struct st_order *next; Item **item; /* Point at item in select fields */ Item *item_ptr; /* Storage for initial item */ + /* + Reference to the function we are trying to optimize copy to + a temporary table + */ + fast_field_copier fast_field_copier_func; + /* Field for which above optimizer function setup */ + Field *fast_field_copier_setup; int counter; /* position in SELECT list, correct only if counter_used is true*/ bool asc; /* true if ascending */ @@ -481,8 +491,6 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, struct TABLE_share; struct All_share_tables; -extern ulong tdc_refresh_version(void); - typedef struct st_table_field_type { LEX_STRING name; @@ -611,6 +619,7 @@ struct TABLE_SHARE Protects ref_count and m_flush_tickets. */ mysql_mutex_t LOCK_table_share; + mysql_cond_t COND_release; TABLE_SHARE *next, **prev; /* Link to unused shares */ uint ref_count; /* How many TABLE objects uses this */ /** @@ -623,6 +632,8 @@ struct TABLE_SHARE */ All_share_tables_list all_tables; TABLE_list free_tables; + ulong version; + bool flushed; } tdc; LEX_CUSTRING tabledef_version; @@ -660,8 +671,6 @@ struct TABLE_SHARE LEX_STRING normalized_path; /* unpack_filename(path) */ LEX_STRING connect_string; - bool is_gtid_slave_pos; - /* Set of keys in use, implemented as a Bitmap. Excludes keys disabled by ALTER TABLE ... DISABLE KEYS. @@ -670,7 +679,6 @@ struct TABLE_SHARE key_map keys_for_keyread; ha_rows min_rows, max_rows; /* create information */ ulong avg_row_length; /* create information */ - ulong version; ulong mysql_version; /* 0 if .frm is created before 5.0 */ ulong reclength; /* Recordlength */ /* Stored record length. No generated-only virtual fields are included */ @@ -734,6 +742,7 @@ struct TABLE_SHARE bool is_view; bool deleting; /* going to delete this table */ bool can_cmp_whole_record; + bool table_creation_was_logged; ulong table_map_id; /* for row-based replication */ /* @@ -849,12 +858,6 @@ struct TABLE_SHARE return table_map_id; } - /** Is this table share being expelled from the table definition cache? */ - inline bool has_old_version() const - { - return version != tdc_refresh_version(); - } - /** Convert unrelated members of TABLE_SHARE to one enum representing its type. @@ -1067,7 +1070,6 @@ public: ORDER *group; String alias; /* alias or table name */ uchar *null_flags; - my_bitmap_map *bitmap_init_value; MY_BITMAP def_read_set, def_write_set, def_vcol_set, tmp_set; MY_BITMAP eq_join_set; /* used to mark equi-joined fields */ MY_BITMAP cond_set; /* used to mark fields from sargable conditions*/ @@ -1952,7 +1954,7 @@ struct TABLE_LIST Indicates that if TABLE_LIST object corresponds to the table/view which requires special handling. */ - enum + enum enum_open_strategy { /* Normal open. */ OPEN_NORMAL= 0, @@ -2203,7 +2205,7 @@ struct TABLE_LIST */ char *get_table_name() const { return view != NULL ? view_name.str : table_name; } bool is_active_sjm(); - bool is_jtbm() { return test(jtbm_subselect!=NULL); } + bool is_jtbm() { return MY_TEST(jtbm_subselect != NULL); } st_select_lex_unit *get_unit(); st_select_lex *get_single_select(); void wrap_into_nested_join(List<TABLE_LIST> &join_list); diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 6f51ac8276c..fbafd8e0c9f 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -44,6 +44,8 @@ Table cache invariants: - TABLE_SHARE::free_tables shall not contain objects with TABLE::in_use != 0 + - TABLE_SHARE::free_tables shall not receive new objects if + TABLE_SHARE::tdc.flushed is true */ #include "my_global.h" @@ -64,12 +66,11 @@ static int64 tdc_version; /* Increments on each reload */ static int64 last_table_id; static bool tdc_inited; -static uint tc_count; /**< Number of TABLE objects in table cache. */ +static int32 tc_count; /**< Number of TABLE objects in table cache. */ /** - Protects tc_count, TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables, - TABLE::in_use. + Protects TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables. */ mysql_mutex_t LOCK_open; @@ -86,7 +87,6 @@ mysql_mutex_t LOCK_open; static mysql_mutex_t LOCK_unused_shares; static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */ -static mysql_rwlock_t LOCK_flush; /**< Sync tc_purge() and tdc_remove_table(). */ my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */ #ifdef HAVE_PSI_INTERFACE @@ -99,11 +99,17 @@ static PSI_mutex_info all_tc_mutexes[]= { &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 } }; -static PSI_rwlock_key key_rwlock_LOCK_tdc, key_rwlock_LOCK_flush; +static PSI_rwlock_key key_rwlock_LOCK_tdc; static PSI_rwlock_info all_tc_rwlocks[]= { - { &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL }, - { &key_rwlock_LOCK_flush, "LOCK_flush", PSI_FLAG_GLOBAL } + { &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL } +}; + + +static PSI_cond_key key_TABLE_SHARE_COND_release; +static PSI_cond_info all_tc_conds[]= +{ + { &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 } }; @@ -117,13 +123,16 @@ static void init_tc_psi_keys(void) count= array_elements(all_tc_rwlocks); mysql_rwlock_register(category, all_tc_rwlocks, count); + + count= array_elements(all_tc_conds); + mysql_cond_register(category, all_tc_conds, count); } #endif /* - Auxiliary routines for manipulating with per-share used/unused and - global unused lists of TABLE objects and tc_count counter. + Auxiliary routines for manipulating with per-share all/unused lists + and tc_count counter. Responsible for preserving invariants between those lists, counter and TABLE::in_use member. In fact those routines implement sort of implicit table cache as @@ -133,13 +142,31 @@ static void init_tc_psi_keys(void) /** Get number of TABLE objects (used and unused) in table cache. - - @todo Protect tc_count so it is read atomically. */ uint tc_records(void) { - return tc_count; + uint count; + my_atomic_rwlock_rdlock(&LOCK_tdc_atomics); + count= my_atomic_load32(&tc_count); + my_atomic_rwlock_rdunlock(&LOCK_tdc_atomics); + return count; +} + + +/** + Remove TABLE object from table cache. + + - decrement tc_count + - remove object from TABLE_SHARE::tdc.all_tables +*/ + +static void tc_remove_table(TABLE *table) +{ + my_atomic_rwlock_wrlock(&LOCK_tdc_atomics); + my_atomic_add32(&tc_count, -1); + my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics); + table->s->tdc.all_tables.remove(table); } @@ -158,7 +185,7 @@ uint tc_records(void) periodicly flush all not used tables. */ -void tc_purge(void) +void tc_purge(bool mark_flushed) { TABLE_SHARE *share; TABLE *table; @@ -169,65 +196,23 @@ void tc_purge(void) mysql_mutex_lock(&LOCK_open); while ((share= tdc_it.next())) { + if (mark_flushed) + share->tdc.flushed= true; while ((table= share->tdc.free_tables.pop_front())) { - share->tdc.all_tables.remove(table); + tc_remove_table(table); purge_tables.push_front(table); - tc_count--; } } tdc_it.deinit(); - mysql_rwlock_rdlock(&LOCK_flush); mysql_mutex_unlock(&LOCK_open); while ((table= purge_tables.pop_front())) intern_close_table(table); - mysql_rwlock_unlock(&LOCK_flush); } /** - Verify consistency of used/unused lists (for debugging). -*/ - -#ifdef EXTRA_DEBUG -static void check_unused(THD *thd) -{ - TABLE *entry; - TABLE_SHARE *share; - TDC_iterator tdc_it; - - tdc_it.init(); - mysql_mutex_lock(&LOCK_open); - while ((share= tdc_it.next())) - { - TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables); - while ((entry= it++)) - { - /* - We must not have TABLEs in the free list that have their file closed. - */ - DBUG_ASSERT(entry->db_stat && entry->file); - /* Merge children should be detached from a merge parent */ - if (entry->in_use) - { - DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */ - } - /* extra() may assume that in_use is set */ - entry->in_use= thd; - DBUG_ASSERT(!thd || !entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); - entry->in_use= 0; - } - } - mysql_mutex_unlock(&LOCK_open); - tdc_it.deinit(); -} -#else -#define check_unused(A) -#endif - - -/** Add new TABLE object to table cache. @pre TABLE object is used by caller. @@ -245,47 +230,45 @@ static void check_unused(THD *thd) void tc_add_table(THD *thd, TABLE *table) { + bool need_purge; DBUG_ASSERT(table->in_use == thd); mysql_mutex_lock(&LOCK_open); table->s->tdc.all_tables.push_front(table); + mysql_mutex_unlock(&LOCK_open); + /* If we have too many TABLE instances around, try to get rid of them */ - if (tc_count == tc_size) + my_atomic_rwlock_wrlock(&LOCK_tdc_atomics); + need_purge= my_atomic_add32(&tc_count, 1) >= (int32) tc_size; + my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics); + + if (need_purge) { + TABLE *purge_table= 0; + TABLE_SHARE *share; TDC_iterator tdc_it; - mysql_mutex_unlock(&LOCK_open); tdc_it.init(); mysql_mutex_lock(&LOCK_open); - if (tc_count == tc_size) + while ((share= tdc_it.next())) { - TABLE *purge_table= 0; - TABLE_SHARE *share; - while ((share= tdc_it.next())) - { - TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables); - TABLE *entry; - while ((entry= it++)) - if (!purge_table || entry->tc_time < purge_table->tc_time) - purge_table= entry; - } - if (purge_table) - { - tdc_it.deinit(); - purge_table->s->tdc.free_tables.remove(purge_table); - purge_table->s->tdc.all_tables.remove(purge_table); - mysql_rwlock_rdlock(&LOCK_flush); - mysql_mutex_unlock(&LOCK_open); - intern_close_table(purge_table); - mysql_rwlock_unlock(&LOCK_flush); - check_unused(thd); - return; - } + TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables); + TABLE *entry; + while ((entry= it++)) + if (!purge_table || entry->tc_time < purge_table->tc_time) + purge_table= entry; } tdc_it.deinit(); + + if (purge_table) + { + purge_table->s->tdc.free_tables.remove(purge_table); + tc_remove_table(purge_table); + mysql_mutex_unlock(&LOCK_open); + intern_close_table(purge_table); + } + else + mysql_mutex_unlock(&LOCK_open); } - /* Nothing to evict, increment tc_count. */ - tc_count++; - mysql_mutex_unlock(&LOCK_open); } @@ -297,7 +280,9 @@ void tc_add_table(THD *thd, TABLE *table) Acquired object cannot be evicted or acquired again. While locked: - - pop object from TABLE_SHARE::tdc.free_tables() + - pop object from TABLE_SHARE::tdc.free_tables + + While unlocked: - mark object used by thd @return TABLE object, or NULL if no unused objects. @@ -308,19 +293,18 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share) TABLE *table; mysql_mutex_lock(&LOCK_open); - if (!(table= share->tdc.free_tables.pop_front())) - { - mysql_mutex_unlock(&LOCK_open); - return 0; - } - DBUG_ASSERT(!table->in_use); - table->in_use= thd; + table= share->tdc.free_tables.pop_front(); mysql_mutex_unlock(&LOCK_open); - /* The ex-unused table must be fully functional. */ - DBUG_ASSERT(table->db_stat && table->file); - /* The children must be detached from the table. */ - DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); + if (table) + { + DBUG_ASSERT(!table->in_use); + table->in_use= thd; + /* The ex-unused table must be fully functional. */ + DBUG_ASSERT(table->db_stat && table->file); + /* The children must be detached from the table. */ + DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); + } return table; } @@ -333,12 +317,12 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share) Released object may be evicted or acquired again. While locked: - - mark object not in use by any thread - if object is marked for purge, decrement tc_count - add object to TABLE_SHARE::tdc.free_tables - evict LRU object from table cache if we reached threshold While unlocked: + - mark object not in use by any thread - free evicted/purged object @note Another thread may mark share for purge any moment (even @@ -353,29 +337,38 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share) bool tc_release_table(TABLE *table) { - THD *thd __attribute__((unused))= table->in_use; DBUG_ASSERT(table->in_use); DBUG_ASSERT(table->file); + if (table->needs_reopen() || tc_records() > tc_size) + { + mysql_mutex_lock(&LOCK_open); + goto purge; + } + table->tc_time= my_interval_timer(); mysql_mutex_lock(&LOCK_open); + if (table->s->tdc.flushed) + goto purge; + /* + in_use doesn't really need protection of LOCK_open, but must be reset after + checking tdc.flushed and before this table appears in free_tables. + Resetting in_use is needed only for print_cached_tables() and + list_open_tables(). + */ table->in_use= 0; - if (table->s->has_old_version() || table->needs_reopen() || tc_count > tc_size) - { - tc_count--; - table->s->tdc.all_tables.remove(table); - mysql_rwlock_rdlock(&LOCK_flush); - mysql_mutex_unlock(&LOCK_open); - intern_close_table(table); - mysql_rwlock_unlock(&LOCK_flush); - return true; - } /* Add table to the list of unused TABLE objects for this share. */ table->s->tdc.free_tables.push_front(table); mysql_mutex_unlock(&LOCK_open); - check_unused(thd); return false; + +purge: + tc_remove_table(table); + mysql_mutex_unlock(&LOCK_open); + table->in_use= 0; + intern_close_table(table); + return true; } @@ -403,6 +396,7 @@ static int tdc_delete_share_from_hash(TABLE_SHARE *share) mysql_mutex_lock(&share->tdc.LOCK_table_share); if (--share->tdc.ref_count) { + mysql_cond_broadcast(&share->tdc.COND_release); mysql_mutex_unlock(&share->tdc.LOCK_table_share); mysql_rwlock_unlock(&LOCK_tdc); DBUG_RETURN(1); @@ -462,7 +456,6 @@ int tdc_init(void) mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares, MY_MUTEX_INIT_FAST); mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc); - mysql_rwlock_init(key_rwlock_LOCK_flush, &LOCK_flush); my_atomic_rwlock_init(&LOCK_tdc_atomics); oldest_unused_share= &end_of_unused_share; end_of_unused_share.tdc.prev= &oldest_unused_share; @@ -510,7 +503,6 @@ void tdc_deinit(void) tdc_inited= false; my_hash_free(&tdc_hash); my_atomic_rwlock_destroy(&LOCK_tdc_atomics); - mysql_rwlock_destroy(&LOCK_flush); mysql_rwlock_destroy(&LOCK_tdc); mysql_mutex_destroy(&LOCK_unused_shares); mysql_mutex_destroy(&LOCK_open); @@ -576,11 +568,13 @@ void tdc_init_share(TABLE_SHARE *share) DBUG_ENTER("tdc_init_share"); mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share, &share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_TABLE_SHARE_COND_release, &share->tdc.COND_release, 0); share->tdc.m_flush_tickets.empty(); share->tdc.all_tables.empty(); share->tdc.free_tables.empty(); tdc_assign_new_table_id(share); - share->version= tdc_refresh_version(); + share->tdc.version= tdc_refresh_version(); + share->tdc.flushed= false; DBUG_VOID_RETURN; } @@ -596,6 +590,7 @@ void tdc_deinit_share(TABLE_SHARE *share) DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty()); DBUG_ASSERT(share->tdc.all_tables.is_empty()); DBUG_ASSERT(share->tdc.free_tables.is_empty()); + mysql_cond_destroy(&share->tdc.COND_release); mysql_mutex_destroy(&share->tdc.LOCK_table_share); DBUG_VOID_RETURN; } @@ -669,16 +664,14 @@ void tdc_unlock_share(TABLE_SHARE *share) */ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, - const char *key, uint key_length, uint flags, + const char *key, uint key_length, + my_hash_value_type hash_value, uint flags, TABLE **out_table) { TABLE_SHARE *share; bool was_unused; - my_hash_value_type hash_value; DBUG_ENTER("tdc_acquire_share"); - hash_value= my_calc_hash(&tdc_hash, (uchar*) key, key_length); - mysql_rwlock_rdlock(&LOCK_tdc); share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value, (uchar*) key, @@ -740,7 +733,6 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, if ((*out_table= tc_acquire_table(thd, share))) { mysql_rwlock_unlock(&LOCK_tdc); - check_unused(thd); DBUG_ASSERT(!(flags & GTS_NOLOCK)); DBUG_ASSERT(!share->error); DBUG_ASSERT(!share->is_view); @@ -828,12 +820,13 @@ void tdc_release_share(TABLE_SHARE *share) DBUG_PRINT("enter", ("share: 0x%lx table: %s.%s ref_count: %u version: %lu", (ulong) share, share->db.str, share->table_name.str, - share->tdc.ref_count, share->version)); + share->tdc.ref_count, share->tdc.version)); DBUG_ASSERT(share->tdc.ref_count); if (share->tdc.ref_count > 1) { share->tdc.ref_count--; + mysql_cond_broadcast(&share->tdc.COND_release); mysql_mutex_unlock(&share->tdc.LOCK_table_share); DBUG_VOID_RETURN; } @@ -841,7 +834,7 @@ void tdc_release_share(TABLE_SHARE *share) mysql_mutex_lock(&LOCK_unused_shares); mysql_mutex_lock(&share->tdc.LOCK_table_share); - if (share->has_old_version()) + if (share->tdc.flushed) { mysql_mutex_unlock(&share->tdc.LOCK_table_share); mysql_mutex_unlock(&LOCK_unused_shares); @@ -959,19 +952,9 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, if ((share= tdc_delete_share(db, table_name))) { I_P_List <TABLE, TABLE_share> purge_tables; + uint my_refs= 1; mysql_mutex_lock(&LOCK_open); - if (kill_delayed_threads) - kill_delayed_threads_for_table(share); - -#ifndef DBUG_OFF - if (remove_type == TDC_RT_REMOVE_NOT_OWN) - { - TABLE_SHARE::All_share_tables_list::Iterator it2(share->tdc.all_tables); - while ((table= it2++)) - DBUG_ASSERT(!table->in_use || table->in_use == thd); - } -#endif /* Set share's version to zero in order to ensure that it gets automatically deleted once it is no longer referenced. @@ -981,28 +964,62 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, shares. */ if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE) - share->version= 0; + share->tdc.flushed= true; while ((table= share->tdc.free_tables.pop_front())) { - share->tdc.all_tables.remove(table); - tc_count--; + tc_remove_table(table); purge_tables.push_front(table); } - mysql_rwlock_rdlock(&LOCK_flush); + if (kill_delayed_threads) + kill_delayed_threads_for_table(share); + + if (remove_type == TDC_RT_REMOVE_NOT_OWN || + remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE) + { + TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables); + while ((table= it++)) + { + my_refs++; + DBUG_ASSERT(table->in_use == thd); + } + } + DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL); mysql_mutex_unlock(&LOCK_open); while ((table= purge_tables.pop_front())) intern_close_table(table); - mysql_rwlock_unlock(&LOCK_flush); - check_unused(thd); - DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL); - tdc_release_share(share); + if (remove_type != TDC_RT_REMOVE_UNUSED) + { + /* + Even though current thread holds exclusive metadata lock on this share + (asserted above), concurrent FLUSH TABLES threads may be in process of + closing unused table instances belonging to this share. E.g.: + thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front(); + thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table); + thr2 (ALTER TABLE): tdc_remove_table(); + thr1 (FLUSH TABLES): intern_close_table(table); + + Current remove type assumes that all table instances (except for those + that are owned by current thread) must be closed before + thd_remove_table() returns. Wait for such tables now. + + intern_close_table() decrements ref_count and signals COND_release. When + ref_count drops down to number of references owned by current thread + waiting is completed. + + Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here + because it waits for all table instances, whereas we have to wait only + for those that are not owned by current thread. + */ + mysql_mutex_lock(&share->tdc.LOCK_table_share); + while (share->tdc.ref_count > my_refs) + mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share); + mysql_mutex_unlock(&share->tdc.LOCK_table_share); + } - /* Wait for concurrent threads to free unused objects. */ - mysql_rwlock_wrlock(&LOCK_flush); - mysql_rwlock_unlock(&LOCK_flush); + tdc_release_share(share); found= true; } @@ -1026,14 +1043,15 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, */ int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name, - ulong wait_timeout, uint deadlock_weight) + ulong wait_timeout, uint deadlock_weight, + ulong refresh_version) { TABLE_SHARE *share; int res= FALSE; if ((share= tdc_lock_share(db, table_name))) { - if (share->has_old_version()) + if (share->tdc.flushed && refresh_version > share->tdc.version) { struct timespec abstime; set_timespec(abstime, wait_timeout); @@ -1050,21 +1068,18 @@ ulong tdc_refresh_version(void) { my_atomic_rwlock_rdlock(&LOCK_tdc_atomics); ulong v= my_atomic_load64(&tdc_version); - my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics); + my_atomic_rwlock_rdunlock(&LOCK_tdc_atomics); return v; } -void tdc_increment_refresh_version(void) +ulong tdc_increment_refresh_version(void) { my_atomic_rwlock_wrlock(&LOCK_tdc_atomics); -#ifndef DBUG_OFF ulong v= my_atomic_add64(&tdc_version, 1); -#else - my_atomic_add64(&tdc_version, 1); -#endif my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics); DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", v)); + return v + 1; } diff --git a/sql/table_cache.h b/sql/table_cache.h index 7b7fb239131..6da6a667792 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -40,6 +40,7 @@ extern void tdc_unlock_share(TABLE_SHARE *share); extern TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name, const char *key, uint key_length, + my_hash_value_type hash_value, uint flags, TABLE **out_table); extern void tdc_release_share(TABLE_SHARE *share); extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, @@ -47,13 +48,14 @@ extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, bool kill_delayed_threads); extern int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name, - ulong wait_timeout, uint deadlock_weight); + ulong wait_timeout, uint deadlock_weight, + ulong refresh_version= ULONG_MAX); extern ulong tdc_refresh_version(void); -extern void tdc_increment_refresh_version(void); +extern ulong tdc_increment_refresh_version(void); extern void tdc_assign_new_table_id(TABLE_SHARE *share); extern uint tc_records(void); -extern void tc_purge(void); +extern void tc_purge(bool mark_flushed= false); extern void tc_add_table(THD *thd, TABLE *table); extern bool tc_release_table(TABLE *table); @@ -87,7 +89,9 @@ static inline TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *key, uint key_length, uint flags) { - return tdc_acquire_share(thd, db, table_name, key, key_length, flags, 0); + return tdc_acquire_share(thd, db, table_name, key, key_length, + my_hash_sort(&my_charset_bin, (uchar*) key, + key_length), flags, 0); } @@ -119,7 +123,8 @@ static inline TABLE_SHARE *tdc_acquire_share_shortlived(THD *thd, TABLE_LIST *tl { const char *key; uint key_length= get_table_def_key(tl, &key); - return tdc_acquire_share(thd, tl->db, tl->table_name, key, key_length, flags); + return tdc_acquire_share(thd, tl->db, tl->table_name, key, key_length, + tl->mdl_request.key.tc_hash_value(), flags, 0); } diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index f0454cfedb0..68c032fb67b 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -19,6 +19,9 @@ #include <sql_class.h> #include <my_pthread.h> #include <scheduler.h> + +#ifdef HAVE_POOL_OF_THREADS + #include <sql_connect.h> #include <mysqld.h> #include <debug_sync.h> @@ -1678,3 +1681,5 @@ static void print_pool_blocked_message(bool max_threads_reached) msg_written= true; } } + +#endif /* HAVE_POOL_OF_THREADS */ diff --git a/sql/transaction.cc b/sql/transaction.cc index 54d36a771a9..8770f729b88 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -144,7 +144,7 @@ bool trans_begin(THD *thd, uint flags) thd->server_status&= ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY); DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); - res= test(ha_commit_trans(thd, TRUE)); + res= MY_TEST(ha_commit_trans(thd, TRUE)); #ifdef WITH_WSREP wsrep_post_commit(thd, TRUE); #endif /* WITH_WSREP */ @@ -181,7 +181,7 @@ bool trans_begin(THD *thd, uint flags) compatibility. */ const bool user_is_super= - test(thd->security_ctx->master_access & SUPER_ACL); + MY_TEST(thd->security_ctx->master_access & SUPER_ACL); if (opt_readonly && !user_is_super) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); @@ -206,7 +206,7 @@ bool trans_begin(THD *thd, uint flags) if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) res= ha_start_consistent_snapshot(thd); - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -249,7 +249,7 @@ bool trans_commit(THD *thd) thd->transaction.all.modified_non_trans_table= FALSE; thd->lex->start_transaction_opt= 0; - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -272,6 +272,10 @@ bool trans_commit_implicit(THD *thd) if (trans_check(thd)) DBUG_RETURN(TRUE); + if (thd->variables.option_bits & OPTION_GTID_BEGIN) + DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. " + "Master and slave will have different GTID values")); + if (thd->in_multi_stmt_transaction_mode() || (thd->variables.option_bits & OPTION_TABLE_LOCK)) { @@ -284,7 +288,7 @@ bool trans_commit_implicit(THD *thd) thd->server_status&= ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY); DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); - res= test(ha_commit_trans(thd, TRUE)); + res= MY_TEST(ha_commit_trans(thd, TRUE)); #ifdef WITH_WSREP wsrep_post_commit(thd, TRUE); #endif /* WITH_WSREP */ @@ -335,10 +339,12 @@ bool trans_rollback(THD *thd) res= ha_rollback_trans(thd, TRUE); (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + /* Reset the binlog transaction marker */ + thd->variables.option_bits&= ~OPTION_GTID_BEGIN; thd->transaction.all.modified_non_trans_table= FALSE; thd->lex->start_transaction_opt= 0; - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -384,7 +390,7 @@ bool trans_rollback_implicit(THD *thd) /* Rollback should clear transaction_rollback_request flag. */ DBUG_ASSERT(! thd->transaction_rollback_request); - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -442,7 +448,7 @@ bool trans_commit_stmt(THD *thd) thd->transaction.stmt.reset(); - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -631,7 +637,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) if (!res && !binlog_on) thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint); - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -666,7 +672,7 @@ bool trans_release_savepoint(THD *thd, LEX_STRING name) thd->transaction.savepoints= sv->prev; - DBUG_RETURN(test(res)); + DBUG_RETURN(MY_TEST(res)); } @@ -833,7 +839,7 @@ bool trans_xa_commit(THD *thd) wsrep_register_hton(thd, TRUE); #endif /* WITH_WSREP */ int r= ha_commit_trans(thd, TRUE); - if ((res= test(r))) + if ((res= MY_TEST(r))) my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); #ifdef WITH_WSREP wsrep_post_commit(thd, TRUE); @@ -866,7 +872,7 @@ bool trans_xa_commit(THD *thd) { DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock"); - res= test(ha_commit_one_phase(thd, 1)); + res= MY_TEST(ha_commit_one_phase(thd, 1)); if (res) my_error(ER_XAER_RMERR, MYF(0)); } diff --git a/sql/transaction.h b/sql/transaction.h index abe7823cf9b..54b25f1de2a 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -1,5 +1,4 @@ -/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. - Use is subject to license terms. +/* Copyright (c) 2008, 2013, 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 diff --git a/sql/tztime.cc b/sql/tztime.cc index b0607293296..857039eb1e8 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -41,6 +41,8 @@ #include <my_time.h> #include "tztime.h" #include <my_sys.h> +#include <mysql_version.h> +#include <my_getopt.h> #endif #include "tzfile.h" @@ -63,6 +65,8 @@ #endif /* !defined(DBUG_OFF) */ #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ +#define PROGRAM_VERSION "1.1" + /* Structure describing local time type (e.g. Moscow summer time (MSD)) */ typedef struct ttinfo { @@ -2387,7 +2391,6 @@ void Time_zone::adjust_leap_second(MYSQL_TIME *t) tables. */ - /* Print info about time zone described by TIME_ZONE_INFO struct as SQL statements populating mysql.time_zone* tables. @@ -2472,6 +2475,15 @@ MEM_ROOT tz_storage; char fullname[FN_REFLEN + 1]; char *root_name_end; +/* + known file types that exist in the zoneinfo directory that are safe to + silently skip +*/ +const char *known_extensions[]= { + ".tab", + NullS +}; + /* Recursively scan zoneinfo directory and print all found time zone @@ -2480,6 +2492,8 @@ char *root_name_end; SYNOPSIS scan_tz_dir() name_end - pointer to end of path to directory to be searched. + symlink_recursion_level How many symlink directory levels are used + verbose >0 if we should print warnings DESCRIPTION This auxiliary recursive function also uses several global @@ -2495,7 +2509,7 @@ char *root_name_end; */ my_bool -scan_tz_dir(char * name_end, uint symlink_recursion_level) +scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose) { MY_DIR *cur_dir; char *name_end_tmp; @@ -2536,12 +2550,21 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level) following such symlinks infinitely: /usr/share/zoneinfo/posix/posix/posix/.../posix/ */ - fflush(stdout); - fprintf(stderr, "Warning: Skipping directory '%s': " - "to avoid infinite symlink recursion.\n", fullname); + + /* + This is a normal case and not critical. only print warning if + verbose mode is choosen. + */ + if (verbose > 0) + { + fflush(stdout); + fprintf(stderr, "Warning: Skipping directory '%s': " + "to avoid infinite symlink recursion.\n", fullname); + } continue; } - if (scan_tz_dir(name_end_tmp, symlink_recursion_level + is_symlink)) + if (scan_tz_dir(name_end_tmp, symlink_recursion_level + is_symlink, + verbose)) { my_dirend(cur_dir); return 1; @@ -2554,10 +2577,28 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level) print_tz_as_sql(root_name_end + 1, &tz_info); else { - fflush(stdout); - fprintf(stderr, - "Warning: Unable to load '%s' as time zone. Skipping it.\n", - fullname); + /* + Some systems (like debian, opensuse etc) have description + files (.tab). We skip these silently if verbose is > 0 + */ + const char *current_ext= fn_ext(fullname); + my_bool known_ext= 0; + + for (const char **ext= known_extensions ; *ext ; ext++) + { + if (!strcmp(*ext, current_ext)) + { + known_ext= 1; + break; + } + } + if (verbose > 0 || !known_ext) + { + fflush(stdout); + fprintf(stderr, + "Warning: Unable to load '%s' as time zone. Skipping it.\n", + fullname); + } } free_root(&tz_storage, MYF(0)); } @@ -2576,37 +2617,118 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level) } +my_bool opt_leap, opt_verbose; + +static const char *load_default_groups[]= +{ "mysql_tzinfo_to_sql", 0}; + +static struct my_option my_long_options[] = +{ + {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, +#ifdef DBUG_OFF + {"debug", '#', "This is a non-debug version. Catch this and exit", + 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#else + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", + 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"leap", 'l', "Print the leap second information from the given time zone file. By convention, when --leap is used the next argument is the timezonefile", + &opt_leap, &opt_leap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"verbose", 'v', "Write non critical warnings", + &opt_verbose, &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"version", 'V', "Output version information and exit.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + +C_MODE_START +static my_bool get_one_option(int optid, const struct my_option *, + char *argument); +C_MODE_END + +static void print_version(void) +{ + printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname, PROGRAM_VERSION, + MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); +} + +static void print_usage(void) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options] timezonedir\n", my_progname); + fprintf(stderr, " %s [options] timezonefile timezonename\n", my_progname); + print_defaults("my",load_default_groups); + puts(""); + my_print_help(my_long_options); + my_print_variables(my_long_options); +} + + +static my_bool +get_one_option(int optid, const struct my_option *opt, char *argument) +{ + switch(optid) { + case '#': +#ifndef DBUG_OFF + DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysq_tzinfo_to_sql.trace"); +#endif + break; + case '?': + print_version(); + puts(""); + print_usage(); + exit(0); + case 'V': + print_version(); + exit(0); + } + return 0; +} + + int main(int argc, char **argv) { + char **default_argv; MY_INIT(argv[0]); - if (argc != 2 && argc != 3) + if (load_defaults("my",load_default_groups,&argc,&argv)) + exit(1); + + default_argv= argv; + + if ((handle_options(&argc, &argv, my_long_options, get_one_option))) + exit(1); + + if ((argc != 1 && argc != 2) || (opt_leap && argc != 1)) { - fprintf(stderr, "Usage:\n"); - fprintf(stderr, " %s timezonedir\n", argv[0]); - fprintf(stderr, " %s timezonefile timezonename\n", argv[0]); - fprintf(stderr, " %s --leap timezonefile\n", argv[0]); + print_usage(); + free_defaults(default_argv); return 1; } // Replicate MyISAM DDL for this session, cf. lp:1161432 printf("SET SESSION wsrep_replicate_myisam=ON;\n"); - if (argc == 2) + if (argc == 1 && !opt_leap) { - root_name_end= strmake_buf(fullname, argv[1]); + /* Argument is timezonedir */ + + root_name_end= strmake_buf(fullname, argv[0]); printf("TRUNCATE TABLE time_zone;\n"); printf("TRUNCATE TABLE time_zone_name;\n"); printf("TRUNCATE TABLE time_zone_transition;\n"); printf("TRUNCATE TABLE time_zone_transition_type;\n"); - if (scan_tz_dir(root_name_end, 0)) + if (scan_tz_dir(root_name_end, 0, opt_verbose)) { fflush(stdout); - fprintf(stderr, "There were fatal errors during processing " - "of zoneinfo directory\n"); + fprintf(stderr, + "There were fatal errors during processing " + "of zoneinfo directory '%s'\n", fullname); return 1; } @@ -2617,32 +2739,27 @@ main(int argc, char **argv) } else { + /* + First argument is timezonefile. + The second is timezonename if opt_leap is not given + */ init_alloc_root(&tz_storage, 32768, 0, MYF(0)); - if (strcmp(argv[1], "--leap") == 0) + if (tz_load(argv[0], &tz_info, &tz_storage)) { - if (tz_load(argv[2], &tz_info, &tz_storage)) - { - fflush(stdout); - fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]); - return 1; - } - print_tz_leaps_as_sql(&tz_info); + fflush(stdout); + fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[0]); + return 1; } + if (opt_leap) + print_tz_leaps_as_sql(&tz_info); else - { - if (tz_load(argv[1], &tz_info, &tz_storage)) - { - fflush(stdout); - fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]); - return 1; - } - print_tz_as_sql(argv[2], &tz_info); - } + print_tz_as_sql(argv[1], &tz_info); free_root(&tz_storage, MYF(0)); } + free_defaults(default_argv); my_end(0); return 0; } @@ -2709,7 +2826,7 @@ main(int argc, char **argv) if (TYPE_SIGNED(time_t)) { t= -100; - localtime_negative= test(localtime_r(&t, &tmp) != 0); + localtime_negative= MY_TEST(localtime_r(&t, &tmp) != 0); printf("localtime_r %s negative params \ (time_t=%d is %d-%d-%d %d:%d:%d)\n", (localtime_negative ? "supports" : "doesn't support"), (int)t, diff --git a/sql/uniques.cc b/sql/uniques.cc index 0c1c34d495b..0990182dbdb 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -86,7 +86,7 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, full_size= size; if (min_dupl_count_arg) full_size+= sizeof(element_count); - with_counters= test(min_dupl_count_arg); + with_counters= MY_TEST(min_dupl_count_arg); my_b_clear(&file); init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func, NULL, comp_func_fixed_arg, MYF(MY_THREAD_SPECIFIC)); diff --git a/sql/unireg.cc b/sql/unireg.cc index 7bb943dc9b0..ad2d711be99 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -280,8 +280,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, int2store(forminfo+2, frm.length - filepos); int4store(fileinfo+10, frm.length); - fileinfo[26]= (uchar) test((create_info->max_rows == 1) && - (create_info->min_rows == 1) && (keys == 0)); + fileinfo[26]= (uchar) MY_TEST((create_info->max_rows == 1) && + (create_info->min_rows == 1) && (keys == 0)); int2store(fileinfo+28,key_info_length); if (part_info) @@ -898,7 +898,7 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields, */ if (field->vcol_info && field->vcol_info->expr_str.length) { - *buff++= (uchar)(1 + test(field->interval)); + *buff++= (uchar) (1 + MY_TEST(field->interval)); *buff++= (uchar) field->sql_type; *buff++= (uchar) field->stored_in_db; if (field->interval) |