summaryrefslogtreecommitdiffstats
path: root/sql
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 13:39:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 13:39:13 +0000
commit86fbb58c3ac0865482819c10a3e81f2eea001c36 (patch)
tree28c9e526ea739c6f9b89e36115e1e2698bddf981 /sql
parentReleasing progress-linux version 1:10.11.6-2~progress7.99u1. (diff)
downloadmariadb-86fbb58c3ac0865482819c10a3e81f2eea001c36.tar.xz
mariadb-86fbb58c3ac0865482819c10a3e81f2eea001c36.zip
Merging upstream version 1:10.11.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt3
-rw-r--r--sql/debug.cc3
-rw-r--r--sql/event_queue.cc1
-rw-r--r--sql/field.cc76
-rw-r--r--sql/field.h51
-rw-r--r--sql/filesort.h7
-rw-r--r--sql/ha_partition.cc39
-rw-r--r--sql/ha_partition.h3
-rw-r--r--sql/ha_sequence.cc27
-rw-r--r--sql/handler.cc14
-rw-r--r--sql/handler.h2
-rw-r--r--sql/item.cc34
-rw-r--r--sql/item.h25
-rw-r--r--sql/item_cmpfunc.cc94
-rw-r--r--sql/item_cmpfunc.h34
-rw-r--r--sql/item_create.cc445
-rw-r--r--sql/item_create.h8
-rw-r--r--sql/item_func.cc110
-rw-r--r--sql/item_func.h90
-rw-r--r--sql/item_jsonfunc.cc72
-rw-r--r--sql/item_jsonfunc.h22
-rw-r--r--sql/item_strfunc.cc66
-rw-r--r--sql/item_strfunc.h199
-rw-r--r--sql/item_subselect.cc10
-rw-r--r--sql/item_sum.cc17
-rw-r--r--sql/item_timefunc.cc7
-rw-r--r--sql/key.cc2
-rw-r--r--sql/lex.h6
-rw-r--r--sql/log.cc129
-rw-r--r--sql/log.h2
-rw-r--r--sql/log_event.cc100
-rw-r--r--sql/log_event.h50
-rw-r--r--sql/log_event_client.cc12
-rw-r--r--sql/log_event_data_type.h74
-rw-r--r--sql/log_event_old.cc40
-rw-r--r--sql/log_event_old.h12
-rw-r--r--sql/log_event_server.cc123
-rw-r--r--sql/mysqld.cc164
-rw-r--r--sql/mysqld.h17
-rw-r--r--sql/net_serv.cc33
-rw-r--r--sql/opt_range.cc69
-rw-r--r--sql/opt_range.h14
-rw-r--r--sql/opt_trace.cc3
-rw-r--r--sql/protocol.cc1
-rw-r--r--sql/protocol.h6
-rw-r--r--sql/rpl_gtid.cc1
-rw-r--r--sql/rpl_mi.cc3
-rw-r--r--sql/rpl_mi.h16
-rw-r--r--sql/rpl_parallel.cc24
-rw-r--r--sql/rpl_parallel.h3
-rw-r--r--sql/rpl_rli.h2
-rw-r--r--sql/semisync_master.cc175
-rw-r--r--sql/semisync_master.h2
-rw-r--r--sql/semisync_master_ack_receiver.cc130
-rw-r--r--sql/semisync_master_ack_receiver.h177
-rw-r--r--sql/semisync_slave.cc152
-rw-r--r--sql/semisync_slave.h22
-rw-r--r--sql/service_wsrep.cc13
-rw-r--r--sql/slave.cc104
-rw-r--r--sql/socketpair.c156
-rw-r--r--sql/socketpair.h21
-rw-r--r--sql/sp.cc10
-rw-r--r--sql/sp_cache.cc4
-rw-r--r--sql/sp_head.cc6
-rw-r--r--sql/sql_acl.cc86
-rw-r--r--sql/sql_admin.cc2
-rw-r--r--sql/sql_alter.cc22
-rw-r--r--sql/sql_analyse.cc3
-rw-r--r--sql/sql_base.cc9
-rw-r--r--sql/sql_binlog.cc5
-rw-r--r--sql/sql_class.cc64
-rw-r--r--sql/sql_class.h153
-rw-r--r--sql/sql_connect.cc3
-rw-r--r--sql/sql_cte.cc42
-rw-r--r--sql/sql_cte.h6
-rw-r--r--sql/sql_delete.cc10
-rw-r--r--sql/sql_derived.cc7
-rw-r--r--sql/sql_help.cc3
-rw-r--r--sql/sql_insert.cc87
-rw-r--r--sql/sql_lex.cc388
-rw-r--r--sql/sql_lex.h72
-rw-r--r--sql/sql_list.h18
-rw-r--r--sql/sql_load.cc4
-rw-r--r--sql/sql_parse.cc136
-rw-r--r--sql/sql_plugin.cc3
-rw-r--r--sql/sql_plugin_services.inl18
-rw-r--r--sql/sql_prepare.cc27
-rw-r--r--sql/sql_profile.cc7
-rw-r--r--sql/sql_profile.h8
-rw-r--r--sql/sql_repl.cc140
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_schema.cc27
-rw-r--r--sql/sql_schema.h22
-rw-r--r--sql/sql_select.cc108
-rw-r--r--sql/sql_sequence.cc2
-rw-r--r--sql/sql_show.cc11
-rw-r--r--sql/sql_table.cc63
-rw-r--r--sql/sql_test.cc11
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--sql/sql_tvc.cc5
-rw-r--r--sql/sql_type.cc52
-rw-r--r--sql/sql_type.h19
-rw-r--r--sql/sql_type_fixedbin.h32
-rw-r--r--sql/sql_type_geom.h7
-rw-r--r--sql/sql_update.cc6
-rw-r--r--sql/sql_view.cc8
-rw-r--r--sql/sql_yacc.yy324
-rw-r--r--sql/structs.h7
-rw-r--r--sql/sys_vars.cc60
-rw-r--r--sql/sys_vars.inl17
-rw-r--r--sql/table.cc40
-rw-r--r--sql/table.h13
-rw-r--r--sql/table_cache.cc4
-rw-r--r--sql/table_cache.h4
-rw-r--r--sql/temporary_tables.cc5
-rw-r--r--sql/wsrep_applier.cc5
-rw-r--r--sql/wsrep_client_service.cc10
-rw-r--r--sql/wsrep_dummy.cc2
-rw-r--r--sql/wsrep_high_priority_service.cc3
-rw-r--r--sql/wsrep_mysqld.cc84
-rw-r--r--sql/wsrep_mysqld.h18
-rw-r--r--sql/wsrep_plugin.cc16
-rw-r--r--sql/wsrep_server_service.cc2
-rw-r--r--sql/wsrep_sst.cc11
-rw-r--r--sql/wsrep_thd.cc4
-rw-r--r--sql/wsrep_thd.h2
-rw-r--r--sql/wsrep_trans_observer.h8
127 files changed, 4051 insertions, 1626 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 13d9d02c..66c7e385 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -55,7 +55,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${LIBFMT_INCLUDE_DIR}
-${PCRE_INCLUDES}
+${PCRE_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${SSL_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/sql
@@ -180,6 +180,7 @@ SET (SQL_SOURCE
table_cache.cc encryption.cc temporary_tables.cc
json_table.cc
proxy_protocol.cc backup.cc xa.cc
+ socketpair.c socketpair.h
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
${GEN_SOURCES}
diff --git a/sql/debug.cc b/sql/debug.cc
index a0e2340e..beb66775 100644
--- a/sql/debug.cc
+++ b/sql/debug.cc
@@ -40,7 +40,8 @@ static bool debug_decrement_counter(const LEX_CSTRING *name)
THD *thd= current_thd;
user_var_entry *entry= (user_var_entry*)
my_hash_search(&thd->user_vars, (uchar*) name->str, name->length);
- if (!entry || entry->type != INT_RESULT || ! entry->value)
+ if (!entry || !entry->value ||
+ entry->type_handler()->result_type() != INT_RESULT)
return 0;
(*(ulonglong*) entry->value)= (*(ulonglong*) entry->value)-1;
return !*(ulonglong*) entry->value;
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index ebd2dfee..7ed4d8c6 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -656,7 +656,6 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
top->status= Event_parse_data::DISABLED;
DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
- top->execution_count++;
(*event_name)->dropped= top->dropped;
/*
Save new values of last_executed timestamp and event status on stack
diff --git a/sql/field.cc b/sql/field.cc
index e94d5c19..76b7ff61 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -4741,30 +4741,6 @@ bool Field_longlong::is_max()
single precision float
****************************************************************************/
-Field_float::Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg,
- const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg),
- zero_arg, unsigned_arg)
-{
-}
-
-Field_float::Field_float(uint32 len_arg, bool maybe_null_arg,
- const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg)
- :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
- NONE, field_name_arg,
- (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg),
- 0, 0)
-{
-}
-
-
int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
{
int error;
@@ -4913,40 +4889,6 @@ Binlog_type_info Field_float::binlog_type_info() const
double precision floating point numbers
****************************************************************************/
-Field_double::Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg,
- const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg),
- zero_arg, unsigned_arg)
-{
-}
-
-Field_double::Field_double(uint32 len_arg, bool maybe_null_arg,
- const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg)
- :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
- NONE, field_name_arg,
- (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg),
- 0, 0)
-{
-}
-
-Field_double::Field_double(uint32 len_arg, bool maybe_null_arg,
- const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg, bool not_fixed_arg)
- :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
- NONE, field_name_arg,
- (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg),
- 0, 0)
-{
- not_fixed= not_fixed_arg;
-}
-
int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
{
int error;
@@ -7743,7 +7685,20 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const
a_ptr, field_length,
b_ptr, field_length,
Field_string::char_length(),
- MY_STRNNCOLLSP_NCHARS_EMULATE_TRIMMED_TRAILING_SPACES);
+ 0);
+}
+
+
+int Field_string::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
+ size_t prefix_char_len) const
+{
+ size_t field_len= table->field[field_index]->field_length;
+
+ return field_charset()->coll->strnncollsp_nchars(field_charset(),
+ a_ptr, field_len,
+ b_ptr, field_len,
+ prefix_char_len,
+ 0);
}
@@ -11396,6 +11351,7 @@ void Field::set_warning_truncated_wrong_value(const char *type_arg,
void Field::raise_note_cannot_use_key_part(THD *thd,
uint keynr, uint part,
const LEX_CSTRING &op,
+ CHARSET_INFO *op_collation,
Item *value,
Data_type_compatibility reason)
const
@@ -11416,7 +11372,7 @@ void Field::raise_note_cannot_use_key_part(THD *thd,
case Data_type_compatibility::INCOMPATIBLE_COLLATION:
{
const LEX_CSTRING colf(charset()->coll_name);
- const LEX_CSTRING colv(value->collation.collation->coll_name);
+ const LEX_CSTRING colv(op_collation->coll_name);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_ERROR,
"Cannot use key %`.*s part[%u] for lookup: "
diff --git a/sql/field.h b/sql/field.h
index c456b2e7..8563375f 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1657,6 +1657,7 @@ public:
void print_key_value_binary(String *out, const uchar* key, uint32 length);
void raise_note_cannot_use_key_part(THD *thd, uint keynr, uint part,
const LEX_CSTRING &op,
+ CHARSET_INFO *op_collation,
Item *value,
const Data_type_compatibility reason)
const;
@@ -1836,6 +1837,16 @@ public:
return flags & (VERS_ROW_START | VERS_ROW_END);
}
+ bool vers_sys_start() const
+ {
+ return flags & VERS_ROW_START;
+ }
+
+ bool vers_sys_end() const
+ {
+ return flags & VERS_ROW_END;
+ }
+
bool vers_update_unversioned() const
{
return flags & VERS_UPDATE_UNVERSIONED_FLAG;
@@ -2137,7 +2148,7 @@ public:
const LEX_CSTRING *field_name_arg,
const DTCollation &collation);
decimal_digits_t decimals() const override
- { return is_created_from_null_item ? 0 : NOT_FIXED_DEC; }
+ { return is_created_from_null_item ? 0 : DECIMAL_NOT_SPECIFIED; }
int save_in_field(Field *to) override { return save_in_field_str(to); }
bool memcpy_field_possible(const Field *from) const override
{
@@ -2307,7 +2318,7 @@ public:
Information_schema_numeric_attributes
information_schema_numeric_attributes() const override
{
- return dec == NOT_FIXED_DEC ?
+ return dec == DECIMAL_NOT_SPECIFIED ?
Information_schema_numeric_attributes(field_length) :
Information_schema_numeric_attributes(field_length, dec);
}
@@ -2879,15 +2890,24 @@ public:
integers. But in all other cases we treat it as TIME_RESULT! */
};
+static inline decimal_digits_t fix_dec_arg(decimal_digits_t dec_arg)
+{ return dec_arg >= FLOATING_POINT_DECIMALS ? DECIMAL_NOT_SPECIFIED : dec_arg; }
class Field_float final :public Field_real {
public:
Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg);
+ decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg)
+ :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ fix_dec_arg(dec_arg), zero_arg, unsigned_arg)
+ { }
Field_float(uint32 len_arg, bool maybe_null_arg,
- const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg);
+ const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
+ NONE, field_name_arg, fix_dec_arg(dec_arg), 0, 0)
+ { }
const Type_handler *type_handler() const override
{ return &type_handler_float; }
enum ha_base_keytype key_type() const override { return HA_KEYTYPE_FLOAT; }
@@ -2920,12 +2940,24 @@ public:
Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg);
+ decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg)
+ :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ fix_dec_arg(dec_arg), zero_arg, unsigned_arg)
+ { }
Field_double(uint32 len_arg, bool maybe_null_arg,
- const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg);
+ const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
+ NONE, field_name_arg, fix_dec_arg(dec_arg), 0, 0)
+ { }
Field_double(uint32 len_arg, bool maybe_null_arg,
const LEX_CSTRING *field_name_arg,
- decimal_digits_t dec_arg, bool not_fixed_arg);
+ decimal_digits_t dec_arg, bool not_fixed_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
+ NONE, field_name_arg, fix_dec_arg(dec_arg), 0, 0)
+ {
+ not_fixed= not_fixed_arg;
+ }
void init_for_tmp_table(Field *org_field, TABLE *new_table) override
{
Field::init_for_tmp_table(org_field, new_table);
@@ -4056,6 +4088,8 @@ public:
String *val_str(String *, String *) override;
my_decimal *val_decimal(my_decimal *) override;
int cmp(const uchar *,const uchar *) const override;
+ int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_char_len) const
+ override;
void sort_string(uchar *buff,uint length) override;
void update_data_type_statistics(Data_type_statistics *st) const override
{
@@ -4078,9 +4112,6 @@ public:
bool compatible_field_size(uint field_metadata, const Relay_log_info *rli,
uint16 mflags, int *order_var) const override;
uint row_pack_length() const override { return field_length; }
- int pack_cmp(const uchar *a,const uchar *b,uint key_length,
- bool insert_or_update);
- int pack_cmp(const uchar *b,uint key_length,bool insert_or_update);
uint packed_col_length(const uchar *to, uint length) override;
uint max_packed_col_length(uint max_length) override;
uint size_of() const override { return sizeof *this; }
diff --git a/sql/filesort.h b/sql/filesort.h
index ebb521e2..8c7931e7 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -56,8 +56,11 @@ public:
bool using_pq;
/*
TRUE means sort operation must produce table rowids.
- FALSE means that it halso has an option of producing {sort_key,
- addon_fields} pairs.
+ FALSE means that it also has an option of producing {sort_key, addon_fields}
+ pairs.
+
+ Usually initialized with value of join_tab->keep_current_rowid to allow for
+ a call to table->file->position() using these table rowids.
*/
bool sort_positions;
/*
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 5eadbe7e..e86badd5 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -4312,9 +4312,12 @@ int ha_partition::external_lock(THD *thd, int lock_type)
(void) (*file)->ha_external_lock(thd, lock_type);
} while (*(++file));
}
- if (lock_type == F_WRLCK && m_part_info->part_expr)
- m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0);
-
+ if (lock_type == F_WRLCK)
+ {
+ if (m_part_info->part_expr)
+ m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0);
+ need_info_for_auto_inc();
+ }
DBUG_RETURN(0);
err_handler:
@@ -4628,33 +4631,8 @@ int ha_partition::write_row(const uchar * buf)
*/
if (have_auto_increment)
{
- if (!table_share->next_number_keypart)
- if (unlikely(error= update_next_auto_inc_val()))
- goto exit;
-
- /*
- If we have failed to set the auto-increment value for this row,
- it is highly likely that we will not be able to insert it into
- the correct partition. We must check and fail if necessary.
- */
if (unlikely(error= update_auto_increment()))
goto exit;
-
- /*
- Don't allow generation of auto_increment value the partitions handler.
- If a partitions handler would change the value, then it might not
- match the partition any longer.
- This can occur if 'SET INSERT_ID = 0; INSERT (NULL)',
- So allow this by adding 'MODE_NO_AUTO_VALUE_ON_ZERO' to sql_mode.
- The partitions handler::next_insert_id must always be 0. Otherwise
- we need to forward release_auto_increment, or reset it for all
- partitions.
- */
- if (table->next_number_field->val_int() == 0)
- {
- table->auto_increment_field_not_null= TRUE;
- thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
- }
}
old_map= dbug_tmp_use_all_columns(table, &table->read_set);
error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
@@ -11017,10 +10995,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
else
{
THD *thd= ha_thd();
- /*
- This is initialized in the beginning of the first write_row call.
- */
- DBUG_ASSERT(part_share->auto_inc_initialized);
+ update_next_auto_inc_val();
/*
Get a lock for handling the auto_increment in part_share
for avoiding two concurrent statements getting the same number.
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 49e212f6..4b82bfb3 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -1408,9 +1408,8 @@ private:
{
ulonglong nr= (((Field_num*) field)->unsigned_flag ||
field->val_int() > 0) ? field->val_int() : 0;
+ update_next_auto_inc_val();
lock_auto_increment();
- DBUG_ASSERT(part_share->auto_inc_initialized ||
- !can_use_for_auto_inc_init());
/* must check when the mutex is taken */
if (nr >= part_share->next_auto_inc_val)
part_share->next_auto_inc_val= nr + 1;
diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc
index bab06147..92b307ff 100644
--- a/sql/ha_sequence.cc
+++ b/sql/ha_sequence.cc
@@ -271,13 +271,26 @@ int ha_sequence::write_row(const uchar *buf)
}
#ifdef WITH_WSREP
- /* We need to start Galera transaction for select NEXT VALUE FOR
- sequence if it is not yet started. Note that ALTER is handled
- as TOI. */
- if (WSREP_ON && WSREP(thd) &&
- !thd->wsrep_trx().active() &&
- wsrep_thd_is_local(thd))
- wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
+ if (WSREP_ON && WSREP(thd) && wsrep_thd_is_local(thd))
+ {
+ if (sequence_locked &&
+ (wsrep_thd_is_SR(thd) || wsrep_streaming_enabled(thd)))
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "SEQUENCEs with streaming replication in Galera cluster");
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ }
+
+ /*
+ We need to start Galera transaction for select NEXT VALUE FOR
+ sequence if it is not yet started. Note that ALTER is handled
+ as TOI.
+ */
+ if (!thd->wsrep_trx().active())
+ {
+ wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
+ }
+ }
#endif
if (likely(!(error= file->update_first_row(buf))))
diff --git a/sql/handler.cc b/sql/handler.cc
index 230bcf5e..17cf018b 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -619,7 +619,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
const char *hton_no_exts[]= { 0 };
-
+static bool ddl_recovery_done= false;
int ha_initialize_handlerton(st_plugin_int *plugin)
{
@@ -769,6 +769,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
resolve_sysvar_table_options(hton);
update_discovery_counters(hton, 1);
+ if (ddl_recovery_done && hton->signal_ddl_recovery_done)
+ hton->signal_ddl_recovery_done(hton);
+
DBUG_RETURN(ret);
err_deinit:
@@ -955,7 +958,8 @@ static my_bool signal_ddl_recovery_done(THD *, plugin_ref plugin, void *)
{
handlerton *hton= plugin_hton(plugin);
if (hton->signal_ddl_recovery_done)
- (hton->signal_ddl_recovery_done)(hton);
+ if ((hton->signal_ddl_recovery_done)(hton))
+ plugin_ref_to_int(plugin)->state= PLUGIN_IS_DELETED;
return 0;
}
@@ -965,6 +969,7 @@ void ha_signal_ddl_recovery_done()
DBUG_ENTER("ha_signal_ddl_recovery_done");
plugin_foreach(NULL, signal_ddl_recovery_done, MYSQL_STORAGE_ENGINE_PLUGIN,
NULL);
+ ddl_recovery_done= true;
DBUG_VOID_RETURN;
}
@@ -7642,7 +7647,12 @@ int handler::ha_write_row(const uchar *buf)
{
DBUG_ASSERT(inited == NONE || lookup_handler != this);
if ((error= check_duplicate_long_entries(buf)))
+ {
+ if (table->next_number_field && buf == table->record[0])
+ if (int err= update_auto_increment())
+ error= err;
DBUG_RETURN(error);
+ }
}
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
diff --git a/sql/handler.h b/sql/handler.h
index 50ec0ed1..a8484e2a 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1495,7 +1495,7 @@ struct handlerton
const LEX_CUSTRING *version, ulonglong create_id);
/* Called for all storage handlers after ddl recovery is done */
- void (*signal_ddl_recovery_done)(handlerton *hton);
+ int (*signal_ddl_recovery_done)(handlerton *hton);
/*
Optional clauses in the CREATE/ALTER TABLE
diff --git a/sql/item.cc b/sql/item.cc
index c6d51e47..6f9eb9d1 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -2674,7 +2674,11 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll,
if (conv->fix_fields_if_needed(thd, arg))
return TRUE;
- if (!thd->stmt_arena->is_conventional())
+ if (!thd->stmt_arena->is_conventional() &&
+ ((!thd->lex->current_select &&
+ (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute() ||
+ thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute())) ||
+ thd->lex->current_select->first_cond_optimization))
{
Query_arena *arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup);
@@ -2798,11 +2802,11 @@ Item_sp::func_name_cstring(THD *thd, bool is_package_function) const
quoted `pkg` and `func` separately, so the entire result looks like:
`db`.`pkg`.`func`
*/
- Database_qualified_name tmp= Database_qualified_name::split(m_name->m_name);
- DBUG_ASSERT(tmp.m_db.length);
- append_identifier(thd, &qname, &tmp.m_db);
+ Identifier_chain2 tmp= Identifier_chain2::split(m_name->m_name);
+ DBUG_ASSERT(tmp[0].length);
+ append_identifier(thd, &qname, &tmp[0]);
qname.append('.');
- append_identifier(thd, &qname, &tmp.m_name);
+ append_identifier(thd, &qname, &tmp[1]);
}
else
append_identifier(thd, &qname, &m_name->m_name);
@@ -7040,7 +7044,25 @@ Item *Item_float::neg(THD *thd)
else if (value < 0 && max_length)
max_length--;
value= -value;
- presentation= 0;
+ if (presentation)
+ {
+ if (*presentation == '-')
+ {
+ // Strip double minus: -(-1) -> '1' instead of '--1'
+ presentation++;
+ }
+ else
+ {
+ size_t presentation_length= strlen(presentation);
+ if (char *tmp= (char*) thd->alloc(presentation_length + 2))
+ {
+ tmp[0]= '-';
+ // Copy with the trailing '\0'
+ memcpy(tmp + 1, presentation, presentation_length + 1);
+ presentation= tmp;
+ }
+ }
+ }
name= null_clex_str;
return this;
}
diff --git a/sql/item.h b/sql/item.h
index b967b4cc..6f552385 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -28,6 +28,7 @@
#include "field.h" /* Derivation */
#include "sql_type.h"
#include "sql_time.h"
+#include "sql_schema.h"
#include "mem_root_array.h"
#include "cset_narrowing.h"
@@ -1015,6 +1016,19 @@ public:
expressions with subqueries in the ORDER/GROUP clauses.
*/
String *val_str() { return val_str(&str_value); }
+ String *val_str_null_to_empty(String *to)
+ {
+ String *res= val_str(to);
+ if (res)
+ return res;
+ to->set_charset(collation.collation);
+ to->length(0);
+ return to;
+ }
+ String *val_str_null_to_empty(String *to, bool null_to_empty)
+ {
+ return null_to_empty ? val_str_null_to_empty(to) : val_str(to);
+ }
virtual Item_func *get_item_func() { return NULL; }
const MY_LOCALE *locale_from_val_str();
@@ -1986,7 +2000,8 @@ public:
QT_ITEM_IDENT_SKIP_DB_NAMES |
QT_ITEM_IDENT_SKIP_TABLE_NAMES |
QT_NO_DATA_EXPANSION |
- QT_TO_SYSTEM_CHARSET),
+ QT_TO_SYSTEM_CHARSET |
+ QT_FOR_FRM),
LOWEST_PRECEDENCE);
}
virtual void print(String *str, enum_query_type query_type);
@@ -5482,6 +5497,14 @@ public:
return (this->*processor)(arg);
}
/*
+ Built-in schema, e.g. mariadb_schema, oracle_schema, maxdb_schema
+ */
+ virtual const Schema *schema() const
+ {
+ // A function does not belong to a built-in schema by default
+ return NULL;
+ }
+ /*
This method is used for debug purposes to print the name of an
item to the debug log. The second use of this method is as
a helper function of print() and error messages, where it is
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index bfe03e1c..c1654bfd 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -457,40 +457,6 @@ void Item_bool_func::raise_note_if_key_become_unused(THD *thd, const Item_args &
}
-bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp)
-{
- DBUG_ASSERT(arg_count >= 2); // Item_func_nullif has arg_count == 3
-
- if (args[0]->cmp_type() == STRING_RESULT &&
- args[1]->cmp_type() == STRING_RESULT)
- {
- CHARSET_INFO *tmp;
- /*
- Use charset narrowing only for equalities, as that would allow
- to construct ref access.
- Non-equality comparisons with constants work without charset narrowing,
- the constant gets converted.
- Non-equality comparisons with non-constants would need narrowing to
- enable range optimizer to handle e.g.
- t1.mb3key_col <= const_table.mb4_col
- But this doesn't look important.
- */
- bool allow_narrowing= MY_TEST(functype()==Item_func::EQ_FUNC ||
- functype()==Item_func::EQUAL_FUNC);
-
- if (agg_arg_charsets_for_comparison(&tmp, &args[0], &args[1],
- allow_narrowing))
- return true;
- cmp->m_compare_collation= tmp;
- }
- // Convert constants when compared to int/year field
- DBUG_ASSERT(functype() != LIKE_FUNC);
- convert_const_compared_to_int_field(thd);
-
- return cmp->set_cmp_func(thd, this, &args[0], &args[1], true);
-}
-
-
/*
Comparison operators remove arguments' dependency on PAD_CHAR_TO_FULL_LENGTH
in case of PAD SPACE comparison collations: trailing spaces do not affect
@@ -519,8 +485,15 @@ bool Item_bool_rowready_func2::fix_length_and_dec(THD *thd)
if (!args[0] || !args[1])
return FALSE;
Item_args old_args(args[0], args[1]);
- if (setup_args_and_comparator(thd, &cmp))
+ convert_const_compared_to_int_field(thd);
+ Type_handler_hybrid_field_type tmp;
+ if (tmp.aggregate_for_comparison(func_name_cstring(), args, 2, false) ||
+ tmp.type_handler()->Item_bool_rowready_func2_fix_length_and_dec(thd,
+ this))
+ {
+ DBUG_ASSERT(thd->is_error());
return true;
+ }
raise_note_if_key_become_unused(thd, old_args);
return false;
}
@@ -540,21 +513,14 @@ bool Item_bool_rowready_func2::fix_length_and_dec(THD *thd)
*/
int Arg_comparator::set_cmp_func(THD *thd, Item_func_or_sum *owner_arg,
+ const Type_handler *compare_handler,
Item **a1, Item **a2)
{
owner= owner_arg;
set_null= set_null && owner_arg;
a= a1;
b= a2;
- Item *tmp_args[2]= {*a1, *a2};
- Type_handler_hybrid_field_type tmp;
- if (tmp.aggregate_for_comparison(owner_arg->func_name_cstring(), tmp_args, 2,
- false))
- {
- DBUG_ASSERT(thd->is_error());
- return 1;
- }
- m_compare_handler= tmp.type_handler();
+ m_compare_handler= compare_handler;
return m_compare_handler->set_comparator_func(thd, this);
}
@@ -605,6 +571,14 @@ bool Arg_comparator::set_cmp_func_string(THD *thd)
We must set cmp_collation here as we may be called from for an automatic
generated item, like in natural join.
Allow reinterpted superset as subset.
+ Use charset narrowing only for equalities, as that would allow
+ to construct ref access.
+ Non-equality comparisons with constants work without charset narrowing,
+ the constant gets converted.
+ Non-equality comparisons with non-constants would need narrowing to
+ enable range optimizer to handle e.g.
+ t1.mb3key_col <= const_table.mb4_col
+ But this doesn't look important.
*/
bool allow_narrowing= false;
if (owner->type() == Item::FUNC_ITEM)
@@ -1509,6 +1483,23 @@ bool Item_in_optimizer::invisible_mode()
}
+bool Item_in_optimizer::walk(Item_processor processor,
+ bool walk_subquery,
+ void *arg)
+{
+ bool res= FALSE;
+ if (args[1]->type() == Item::SUBSELECT_ITEM &&
+ ((Item_subselect *)args[1])->substype() != Item_subselect::EXISTS_SUBS &&
+ !(((Item_subselect *)args[1])->substype() == Item_subselect::IN_SUBS &&
+ ((Item_in_subselect *)args[1])->test_strategy(SUBS_IN_TO_EXISTS)))
+ res= args[0]->walk(processor, walk_subquery, arg);
+ if (!res)
+ res= args[1]->walk(processor, walk_subquery, arg);
+
+ return res || (this->*processor)(arg);
+}
+
+
/**
Add an expression cache for this subquery if it is needed
@@ -2812,8 +2803,9 @@ Item_func_nullif::fix_length_and_dec(THD *thd)
fix_char_length(args[2]->max_char_length());
set_maybe_null();
m_arg0= args[0];
- if (setup_args_and_comparator(thd, &cmp))
- return TRUE;
+ convert_const_compared_to_int_field(thd);
+ if (cmp.set_cmp_func(thd, this, &args[0], &args[1], true/*set_null*/))
+ return true;
/*
A special code for EXECUTE..PREPARE.
@@ -3484,7 +3476,13 @@ void Item_func_case_simple::print(String *str, enum_query_type query_type)
void Item_func_decode_oracle::print(String *str, enum_query_type query_type)
{
- str->append(func_name_cstring());
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(STRING_WITH_LEN("decode_oracle"));
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type);
str->append('(');
args[0]->print(str, query_type);
for (uint i= 1, count= when_count() ; i <= count; i++)
@@ -6092,7 +6090,7 @@ void Regexp_processor_pcre::init(CHARSET_INFO *data_charset, int extra_flags)
// Convert text data to utf-8.
m_library_charset= data_charset == &my_charset_bin ?
- &my_charset_bin : &my_charset_utf8mb3_general_ci;
+ &my_charset_bin : &my_charset_utf8mb4_general_ci;
m_conversion_is_needed= (data_charset != &my_charset_bin) &&
!my_charset_same(data_charset, m_library_charset);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index a67dfb5d..3d5710cd 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -57,6 +57,7 @@ class Arg_comparator: public Sql_alloc
// when one of arguments is NULL.
int set_cmp_func(THD *thd, Item_func_or_sum *owner_arg,
+ const Type_handler *compare_handler,
Item **a1, Item **a2);
int compare_not_null_values(longlong val1, longlong val2)
@@ -95,11 +96,24 @@ public:
bool set_cmp_func_decimal(THD *thd);
inline int set_cmp_func(THD *thd, Item_func_or_sum *owner_arg,
- Item **a1, Item **a2, bool set_null_arg)
+ const Type_handler *compare_handler,
+ Item **a1, Item **a2, bool set_null_arg)
{
set_null= set_null_arg;
- return set_cmp_func(thd, owner_arg, a1, a2);
+ return set_cmp_func(thd, owner_arg, compare_handler, a1, a2);
}
+ int set_cmp_func(THD *thd, Item_func_or_sum *owner_arg,
+ Item **a1, Item **a2, bool set_null_arg)
+ {
+ Item *tmp_args[2]= { *a1, *a2 };
+ Type_handler_hybrid_field_type tmp;
+ if (tmp.aggregate_for_comparison(owner_arg->func_name_cstring(),
+ tmp_args, 2, false))
+ return 1;
+ return set_cmp_func(thd, owner_arg, tmp.type_handler(),
+ a1, a2, set_null_arg);
+ }
+
inline int compare() { return (this->*func)(); }
int compare_string(); // compare args[0] & args[1]
@@ -414,6 +428,7 @@ public:
void fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge) override;
bool invisible_mode();
+ bool walk(Item_processor processor, bool walk_subquery, void *arg) override;
void reset_cache() { cache= NULL; }
void print(String *str, enum_query_type query_type) override;
void restore_first_argument();
@@ -561,9 +576,17 @@ public:
return this;
}
bool fix_length_and_dec(THD *thd) override;
+ bool fix_length_and_dec_generic(THD *thd,
+ const Type_handler *compare_handler)
+ {
+ DBUG_ASSERT(args == tmp_arg);
+ return cmp.set_cmp_func(thd, this, compare_handler,
+ tmp_arg, tmp_arg + 1, true/*set_null*/);
+ }
int set_cmp_func(THD *thd)
{
- return cmp.set_cmp_func(thd, this, tmp_arg, tmp_arg + 1, true);
+ DBUG_ASSERT(args == tmp_arg);
+ return cmp.set_cmp_func(thd, this, tmp_arg, tmp_arg + 1, true/*set_null*/);
}
CHARSET_INFO *compare_collation() const override
{ return cmp.compare_collation(); }
@@ -2439,9 +2462,10 @@ public:
Item_func_decode_oracle(THD *thd, List<Item> &list)
:Item_func_case_simple(thd, list)
{ }
+ const Schema *schema() const override { return &oracle_schema_ref; }
LEX_CSTRING func_name_cstring() const override
{
- static LEX_CSTRING name= {STRING_WITH_LEN("decode_oracle") };
+ static LEX_CSTRING name= {STRING_WITH_LEN("decode") };
return name;
}
void print(String *str, enum_query_type query_type) override;
@@ -3020,7 +3044,7 @@ public:
m_pcre(NULL), m_pcre_match_data(NULL),
m_conversion_is_needed(true), m_is_const(0),
m_library_flags(0),
- m_library_charset(&my_charset_utf8mb3_general_ci)
+ m_library_charset(&my_charset_utf8mb4_general_ci)
{}
int default_regex_flags();
void init(CHARSET_INFO *data_charset, int extra_flags);
diff --git a/sql/item_create.cc b/sql/item_create.cc
index b26610a5..156a6b3f 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -127,6 +127,19 @@ protected:
};
+class Create_func_addmonths : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_addmonths s_singleton;
+
+protected:
+ Create_func_addmonths() = default;
+ virtual ~Create_func_addmonths() = default;
+};
+
+
class Create_func_aes_encrypt : public Create_func_arg2
{
public:
@@ -258,6 +271,19 @@ protected:
};
+class Create_func_collation : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_collation s_singleton;
+
+protected:
+ Create_func_collation() = default;
+ virtual ~Create_func_collation() = default;
+};
+
+
class Create_func_chr : public Create_func_arg1
{
public:
@@ -345,6 +371,20 @@ protected:
};
+class Create_func_coalesce : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
+ List<Item> *item_list);
+
+ static Create_func_coalesce s_singleton;
+
+protected:
+ Create_func_coalesce() = default;
+ virtual ~Create_func_coalesce() = default;
+};
+
+
class Create_func_compress : public Create_func_arg1
{
public:
@@ -427,9 +467,6 @@ public:
virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
List<Item> *item_list)
{
- if (thd->variables.sql_mode & MODE_ORACLE)
- return Create_func_decode_oracle::s_singleton.create_native(thd, name,
- item_list);
if (unlikely(!item_list || item_list->elements != 2))
{
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
@@ -475,6 +512,19 @@ protected:
};
+class Create_func_database : public Create_func_arg0
+{
+public:
+ virtual Item *create_builder(THD *thd);
+
+ static Create_func_database s_singleton;
+
+protected:
+ Create_func_database() = default;
+ virtual ~Create_func_database() = default;
+};
+
+
class Create_func_nvl2 : public Create_func_arg3
{
public:
@@ -581,6 +631,22 @@ protected:
};
+class Create_func_date_format : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
+ List<Item> *item_list);
+
+ static Create_func_date_format s_singleton;
+
+protected:
+ Create_func_date_format() = default;
+ virtual ~Create_func_date_format() = default;
+};
+
+
+
+
class Create_func_dayname : public Create_func_arg1
{
public:
@@ -1439,6 +1505,31 @@ protected:
virtual ~Create_func_octet_length() = default;
};
+class Create_func_old_password : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_old_password s_singleton;
+
+protected:
+ Create_func_old_password() = default;
+ virtual ~Create_func_old_password() = default;
+};
+
+
+class Create_func_password : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_password s_singleton;
+
+protected:
+ Create_func_password() = default;
+ virtual ~Create_func_password() = default;
+};
+
#ifndef DBUG_OFF
class Create_func_like_range_min : public Create_func_arg2
@@ -1554,9 +1645,7 @@ public:
virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
List<Item> *item_list)
{
- return thd->variables.sql_mode & MODE_ORACLE ?
- create_native_oracle(thd, name, item_list) :
- create_native_std(thd, name, item_list);
+ return create_native_std(thd, name, item_list);
}
static Create_func_lpad s_singleton;
@@ -1689,6 +1778,32 @@ protected:
};
+class Create_func_microsecond : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_microsecond s_singleton;
+
+protected:
+ Create_func_microsecond() = default;
+ virtual ~Create_func_microsecond() = default;
+};
+
+
+class Create_func_mod : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_mod s_singleton;
+
+protected:
+ Create_func_mod() = default;
+ virtual ~Create_func_mod() = default;
+};
+
+
class Create_func_monthname : public Create_func_arg1
{
public:
@@ -1815,6 +1930,19 @@ protected:
};
+class Create_func_quarter : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_quarter s_singleton;
+
+protected:
+ Create_func_quarter() = default;
+ virtual ~Create_func_quarter() = default;
+};
+
+
class Create_func_quote : public Create_func_arg1
{
public:
@@ -1844,7 +1972,10 @@ protected:
class Create_func_regexp_replace : public Create_func_arg3
{
public:
- virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+ Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override
+ {
+ return new (thd->mem_root) Item_func_regexp_replace(thd, arg1, arg2, arg3);
+ }
static Create_func_regexp_replace s_singleton;
@@ -1853,6 +1984,28 @@ protected:
virtual ~Create_func_regexp_replace() = default;
};
+Create_func_regexp_replace Create_func_regexp_replace::s_singleton;
+
+
+class Create_func_regexp_replace_oracle : public Create_func_arg3
+{
+public:
+ Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override
+ {
+ return new (thd->mem_root) Item_func_regexp_replace_oracle(thd, arg1,
+ arg2, arg3);
+ }
+
+ static Create_func_regexp_replace_oracle s_singleton;
+
+protected:
+ Create_func_regexp_replace_oracle() = default;
+ virtual ~Create_func_regexp_replace_oracle() = default;
+};
+
+Create_func_regexp_replace_oracle
+ Create_func_regexp_replace_oracle::s_singleton;
+
class Create_func_regexp_substr : public Create_func_arg2
{
@@ -1969,15 +2122,26 @@ protected:
};
+class Create_func_row_count : public Create_func_arg0
+{
+public:
+ virtual Item *create_builder(THD *thd);
+
+ static Create_func_row_count s_singleton;
+
+protected:
+ Create_func_row_count() = default;
+ virtual ~Create_func_row_count() = default;
+};
+
+
class Create_func_rpad : public Create_native_func
{
public:
virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
List<Item> *item_list)
{
- return thd->variables.sql_mode & MODE_ORACLE ?
- create_native_oracle(thd, name, item_list) :
- create_native_std(thd, name, item_list);
+ return create_native_std(thd, name, item_list);
}
static Create_func_rpad s_singleton;
@@ -2418,6 +2582,20 @@ protected:
};
+class Create_func_week : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
+ List<Item> *item_list);
+
+ static Create_func_week s_singleton;
+
+protected:
+ Create_func_week() = default;
+ virtual ~Create_func_week() = default;
+};
+
+
class Create_func_weekday : public Create_func_arg1
{
public:
@@ -2897,6 +3075,16 @@ Create_func_addtime::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
+Create_func_addmonths Create_func_addmonths::s_singleton;
+
+Item*
+Create_func_addmonths::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root)
+ Item_date_add_interval(thd, arg1, arg2, INTERVAL_MONTH, false);
+}
+
+
Create_func_aes_encrypt Create_func_aes_encrypt::s_singleton;
Item*
@@ -3026,6 +3214,15 @@ Create_func_ceiling::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_collation Create_func_collation::s_singleton;
+
+Item*
+Create_func_collation::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_collation(thd, arg1);
+}
+
+
Create_func_chr Create_func_chr::s_singleton;
Item*
@@ -3086,6 +3283,26 @@ Create_func_dyncol_json::create_1_arg(THD *thd, Item *arg1)
return new (thd->mem_root) Item_func_dyncol_json(thd, arg1);
}
+Create_func_coalesce Create_func_coalesce::s_singleton;
+
+Item*
+Create_func_coalesce::create_native(THD *thd, const LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (unlikely(arg_count < 1))
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_coalesce(thd, *item_list);
+}
+
Create_func_concat Create_func_concat::s_singleton;
Item*
@@ -3103,9 +3320,7 @@ Create_func_concat::create_native(THD *thd, const LEX_CSTRING *name,
return NULL;
}
- return thd->variables.sql_mode & MODE_ORACLE ?
- new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list) :
- new (thd->mem_root) Item_func_concat(thd, *item_list);
+ return new (thd->mem_root) Item_func_concat(thd, *item_list);
}
Create_func_concat_operator_oracle
@@ -3182,6 +3397,16 @@ Create_func_connection_id::create_builder(THD *thd)
}
+Create_func_database Create_func_database::s_singleton;
+
+Item*
+Create_func_database::create_builder(THD *thd)
+{
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_database(thd);
+}
+
+
Create_func_nvl2 Create_func_nvl2::s_singleton;
Item*
@@ -3290,6 +3515,37 @@ Create_func_datediff::create_2_arg(THD *thd, Item *arg1, Item *arg2)
return new (thd->mem_root) Item_func_minus(thd, i1, i2);
}
+Create_func_date_format Create_func_date_format::s_singleton;
+
+Item*
+Create_func_date_format::create_native(THD *thd, const LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ return new (thd->mem_root) Item_func_date_format(thd, param_1, param_2);
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ return new (thd->mem_root) Item_func_date_format(thd,
+ param_1, param_2, param_3);
+ }
+ }
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ return NULL;
+}
+
Create_func_dayname Create_func_dayname::s_singleton;
@@ -4430,10 +4686,24 @@ Create_func_length Create_func_length::s_singleton;
Item*
Create_func_length::create_1_arg(THD *thd, Item *arg1)
{
- if (thd->variables.sql_mode & MODE_ORACLE)
- return new (thd->mem_root) Item_func_char_length(thd, arg1);
- else
- return new (thd->mem_root) Item_func_octet_length(thd, arg1);
+ return new (thd->mem_root) Item_func_octet_length(thd, arg1);
+}
+
+Create_func_old_password Create_func_old_password::s_singleton;
+
+Item*
+Create_func_old_password::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_password(thd, arg1,
+ Item_func_password::OLD);
+}
+
+Create_func_password Create_func_password::s_singleton;
+
+Item*
+Create_func_password::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_password(thd, arg1);
}
Create_func_octet_length Create_func_octet_length::s_singleton;
@@ -4651,7 +4921,7 @@ Create_func_ltrim Create_func_ltrim::s_singleton;
Item*
Create_func_ltrim::create_1_arg(THD *thd, Item *arg1)
{
- return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim(thd);
+ return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim_std(thd);
}
@@ -4810,6 +5080,24 @@ Item *Create_func_natural_sort_key::create_1_arg(THD *thd, Item* arg1)
return new (thd->mem_root) Item_func_natural_sort_key(thd, arg1);
}
+Create_func_microsecond Create_func_microsecond::s_singleton;
+
+Item*
+Create_func_microsecond::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_microsecond(thd, arg1);
+}
+
+
+Create_func_mod Create_func_mod::s_singleton;
+
+Item*
+Create_func_mod::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_mod(thd, arg1, arg2);
+}
+
+
Create_func_monthname Create_func_monthname::s_singleton;
Item*
@@ -4912,30 +5200,30 @@ Create_func_pow::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
-Create_func_quote Create_func_quote::s_singleton;
+Create_func_quarter Create_func_quarter::s_singleton;
Item*
-Create_func_quote::create_1_arg(THD *thd, Item *arg1)
+Create_func_quarter::create_1_arg(THD *thd, Item *arg1)
{
- return new (thd->mem_root) Item_func_quote(thd, arg1);
+ return new (thd->mem_root) Item_func_quarter(thd, arg1);
}
-Create_func_regexp_instr Create_func_regexp_instr::s_singleton;
+Create_func_quote Create_func_quote::s_singleton;
Item*
-Create_func_regexp_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+Create_func_quote::create_1_arg(THD *thd, Item *arg1)
{
- return new (thd->mem_root) Item_func_regexp_instr(thd, arg1, arg2);
+ return new (thd->mem_root) Item_func_quote(thd, arg1);
}
-Create_func_regexp_replace Create_func_regexp_replace::s_singleton;
+Create_func_regexp_instr Create_func_regexp_instr::s_singleton;
Item*
-Create_func_regexp_replace::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
+Create_func_regexp_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
- return new (thd->mem_root) Item_func_regexp_replace(thd, arg1, arg2, arg3);
+ return new (thd->mem_root) Item_func_regexp_instr(thd, arg1, arg2);
}
@@ -5097,6 +5385,17 @@ Create_func_round::create_native(THD *thd, const LEX_CSTRING *name,
}
+Create_func_row_count Create_func_row_count::s_singleton;
+
+Item*
+Create_func_row_count::create_builder(THD *thd)
+{
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_row_count(thd);
+}
+
+
Create_func_rpad Create_func_rpad::s_singleton;
Create_func_rpad_oracle Create_func_rpad_oracle::s_singleton;
@@ -5166,7 +5465,7 @@ Create_func_rtrim Create_func_rtrim::s_singleton;
Item*
Create_func_rtrim::create_1_arg(THD *thd, Item *arg1)
{
- return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim(thd);
+ return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim_std(thd);
}
@@ -5551,6 +5850,43 @@ Create_func_version::create_builder(THD *thd)
}
+Create_func_week Create_func_week::s_singleton;
+
+Item*
+Create_func_week::create_native(THD *thd, const LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item* func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_week(thd, param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_week(thd, param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
Create_func_weekday Create_func_weekday::s_singleton;
Item*
@@ -5701,6 +6037,7 @@ const Native_func_registry func_array[] =
{ { STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)},
{ { STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)},
{ { STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)},
+ { { STRING_WITH_LEN("ADD_MONTHS") }, BUILDER(Create_func_addmonths)},
{ { STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)},
{ { STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)},
{ { STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)},
@@ -5716,7 +6053,9 @@ const Native_func_registry func_array[] =
{ { STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)},
{ { STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)},
{ { STRING_WITH_LEN("CHR") }, BUILDER(Create_func_chr)},
+ { { STRING_WITH_LEN("COALESCE") }, BUILDER(Create_func_coalesce)},
{ { STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)},
+ { { STRING_WITH_LEN("COLLATION") }, BUILDER(Create_func_collation)},
{ { STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)},
{ { STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)},
{ { STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)},
@@ -5732,7 +6071,9 @@ const Native_func_registry func_array[] =
{ { STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)},
{ { STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)},
{ { STRING_WITH_LEN("CRC32C") }, BUILDER(Create_func_crc32c)},
+ { { STRING_WITH_LEN("DATABASE") }, BUILDER(Create_func_database)},
{ { STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)},
+ { { STRING_WITH_LEN("DATE_FORMAT") }, BUILDER(Create_func_date_format)},
{ { STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)},
{ { STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)},
{ { STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)},
@@ -5824,6 +6165,8 @@ const Native_func_registry func_array[] =
{ { STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)},
{ { STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)},
{ { STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)},
+ { { STRING_WITH_LEN("MICROSECOND") }, BUILDER(Create_func_microsecond)},
+ { { STRING_WITH_LEN("MOD") }, BUILDER(Create_func_mod)},
{ { STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)},
{ { STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)},
{ {STRING_WITH_LEN("NATURAL_SORT_KEY")}, BUILDER(Create_func_natural_sort_key)},
@@ -5832,12 +6175,15 @@ const Native_func_registry func_array[] =
{ { STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)},
{ { STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)},
{ { STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_octet_length)},
+ { { STRING_WITH_LEN("OLD_PASSWORD") }, BUILDER(Create_func_old_password)},
{ { STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)},
+ { { STRING_WITH_LEN("PASSWORD") }, BUILDER(Create_func_password)},
{ { STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)},
{ { STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)},
{ { STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)},
{ { STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)},
{ { STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)},
+ { { STRING_WITH_LEN("QUARTER") }, BUILDER(Create_func_quarter)},
{ { STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)},
{ { STRING_WITH_LEN("RANDOM_BYTES")}, BUILDER(Create_func_random_bytes)},
{ { STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)},
@@ -5852,12 +6198,15 @@ const Native_func_registry func_array[] =
BUILDER(Create_func_replace_oracle)},
{ { STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)},
{ { STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)},
+ { { STRING_WITH_LEN("ROW_COUNT") }, BUILDER(Create_func_row_count)},
{ { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)},
{ { STRING_WITH_LEN("RPAD_ORACLE") }, BUILDER(Create_func_rpad_oracle)},
{ { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)},
{ { STRING_WITH_LEN("RTRIM_ORACLE") }, BUILDER(Create_func_rtrim_oracle)},
{ { STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)},
{ { STRING_WITH_LEN("SFORMAT") }, BUILDER(Create_func_sformat)},
+ { { STRING_WITH_LEN("SCHEMA") }, BUILDER(Create_func_database)},
+ { { STRING_WITH_LEN("SCHEMAS") }, BUILDER(Create_func_database)},
{ { STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)},
{ { STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)},
{ { STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)},
@@ -5890,6 +6239,7 @@ const Native_func_registry func_array[] =
{ { STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)},
{ { STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)},
{ { STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)},
+ { { STRING_WITH_LEN("WEEK") }, BUILDER(Create_func_week)},
{ { STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)},
{ { STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)},
#ifdef WITH_WSREP
@@ -5903,9 +6253,27 @@ const Native_func_registry func_array[] =
Native_func_registry_array
native_func_registry_array(func_array, array_elements(func_array));
-const size_t func_array_length= sizeof(func_array) / sizeof(Native_func_registry) - 1;
+const Native_func_registry func_array_oracle_overrides[] =
+{
+ { { STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat_operator_oracle)},
+ { { STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode_oracle)},
+ { { STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_char_length)},
+ { { STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad_oracle)},
+ { { STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim_oracle)},
+ { { STRING_WITH_LEN("REGEXP_REPLACE") },
+ BUILDER(Create_func_regexp_replace_oracle)},
+ { { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad_oracle)},
+ { { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim_oracle)},
+ { {0, 0}, NULL}
+};
+
+Native_func_registry_array
+ oracle_func_registry_array(func_array_oracle_overrides,
+ array_elements(func_array_oracle_overrides));
Native_functions_hash native_functions_hash;
+Native_functions_hash native_functions_hash_oracle;
+
/*
Load the hash table for native functions.
@@ -6034,13 +6402,30 @@ int item_create_init()
native_func_registry_array_geom.count()))
return true;
#endif
- return false;
+
+ count+= oracle_func_registry_array.count();
+
+ if (native_functions_hash_oracle.init(count) ||
+ native_functions_hash_oracle.append(native_func_registry_array.elements(),
+ native_func_registry_array.count()))
+ return true;
+
+#ifdef HAVE_SPATIAL
+ if (native_functions_hash_oracle.append(native_func_registry_array_geom.elements(),
+ native_func_registry_array_geom.count()))
+ return true;
+#endif
+
+ return
+ native_functions_hash_oracle.replace(oracle_func_registry_array.elements(),
+ oracle_func_registry_array.count());
}
void item_create_cleanup()
{
native_functions_hash.cleanup();
+ native_functions_hash_oracle.cleanup();
}
diff --git a/sql/item_create.h b/sql/item_create.h
index 80395960..ea32d661 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -324,6 +324,12 @@ public:
bool init(size_t count);
bool append(const Native_func_registry array[], size_t count);
bool remove(const Native_func_registry array[], size_t count);
+ bool replace(const Native_func_registry array[], size_t count)
+ {
+ DBUG_ENTER("Native_functions_hash::replace");
+ remove(array, count);
+ DBUG_RETURN(append(array, count));
+ }
void cleanup();
/**
Find the native function builder associated with a given function name.
@@ -335,6 +341,7 @@ public:
};
extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash;
+extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash_oracle;
extern const Native_func_registry func_array[];
extern const size_t func_array_length;
@@ -377,4 +384,3 @@ public:
#endif
-
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a2bc4752..dd056ac4 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -131,6 +131,16 @@ Item_args::Item_args(THD *thd, const Item_args *other)
}
+void Item_func::wrong_param_count_error(const LEX_CSTRING &schema_name,
+ const LEX_CSTRING &func_name)
+{
+ DBUG_ASSERT(schema_name.length);
+ Database_qualified_name qname(schema_name, func_name);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0),
+ ErrConvDQName(&qname).ptr());
+}
+
+
void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
{
List_iterator_fast<Item> li(list);
@@ -618,13 +628,12 @@ table_map Item_func::not_null_tables() const
void Item_func::print(String *str, enum_query_type query_type)
{
str->append(func_name_cstring());
- str->append('(');
- print_args(str, 0, query_type);
- str->append(')');
+ print_args_parenthesized(str, query_type);
}
-void Item_func::print_args(String *str, uint from, enum_query_type query_type)
+void Item_func::print_args(String *str, uint from,
+ enum_query_type query_type) const
{
for (uint i=from ; i < arg_count ; i++)
{
@@ -1814,7 +1823,7 @@ void Item_func_neg::fix_length_and_dec_int()
Use val() to get value as arg_type doesn't mean that item is
Item_int or Item_float due to existence of Item_param.
*/
- if (args[0]->const_item())
+ if (args[0]->const_item() && !args[0]->is_expensive())
{
longlong val= args[0]->val_int();
if ((ulonglong) val >= (ulonglong) LONGLONG_MIN &&
@@ -2793,8 +2802,17 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref)
No need to send a Rand log event if seed was given eg: RAND(seed),
as it will be replicated in the query as such.
*/
+ DBUG_ASSERT((!rand &&
+ (thd->active_stmt_arena_to_use()->
+ is_stmt_prepare_or_first_stmt_execute() ||
+ thd->active_stmt_arena_to_use()->
+ is_conventional() ||
+ thd->active_stmt_arena_to_use()->state ==
+ Query_arena::STMT_SP_QUERY_ARGUMENTS
+ )
+ ) || rand);
if (!rand && !(rand= (struct my_rnd_struct*)
- thd->stmt_arena->alloc(sizeof(*rand))))
+ thd->active_stmt_arena_to_use()->alloc(sizeof(*rand))))
return TRUE;
}
else
@@ -4633,7 +4651,6 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
entry->length=0;
entry->update_query_id=0;
entry->set_charset(NULL);
- entry->unsigned_flag= 0;
/*
If we are here, we were called from a SET or a query which sets a
variable. Imagine it is this:
@@ -4645,7 +4662,7 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
by Item_func_get_user_var (because that's not necessary).
*/
entry->used_query_id=current_thd->query_id;
- entry->type=STRING_RESULT;
+ entry->set_handler(&type_handler_long_blob);
memcpy((char*) entry->name.str, name->str, name->length+1);
if (my_hash_insert(hash,(uchar*) entry))
{
@@ -4721,9 +4738,12 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
switch (args[0]->result_type()) {
case STRING_RESULT:
case TIME_RESULT:
- set_handler(type_handler_long_blob.
- type_handler_adjusted_to_max_octet_length(max_length,
- collation.collation));
+ if (args[0]->field_type() == MYSQL_TYPE_GEOMETRY)
+ set_handler(args[0]->type_handler());
+ else
+ set_handler(type_handler_long_blob.
+ type_handler_adjusted_to_max_octet_length(max_length,
+ collation.collation));
break;
case REAL_RESULT:
set_handler(&type_handler_double);
@@ -4848,9 +4868,9 @@ bool Item_func_set_user_var::register_field_in_bitmap(void *arg)
bool
update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
- Item_result type, CHARSET_INFO *cs,
- bool unsigned_arg)
+ const Type_handler *th, CHARSET_INFO *cs)
{
+ entry->set_handler(th);
if (set_null)
{
char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
@@ -4861,7 +4881,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
}
else
{
- if (type == STRING_RESULT)
+ if (th->result_type() == STRING_RESULT)
length++; // Store strings with end \0
if (length <= extra_size)
{
@@ -4890,20 +4910,18 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
return 1;
}
}
- if (type == STRING_RESULT)
+ if (th->result_type() == STRING_RESULT)
{
length--; // Fix length change above
entry->value[length]= 0; // Store end \0
}
if (length)
memmove(entry->value, ptr, length);
- if (type == DECIMAL_RESULT)
+ if (th->result_type() == DECIMAL_RESULT)
((my_decimal*)entry->value)->fix_buffer_pointer();
entry->length= length;
entry->set_charset(cs);
- entry->unsigned_flag= unsigned_arg;
}
- entry->type=type;
#ifdef USER_VAR_TRACKING
#ifndef EMBEDDED_LIBRARY
THD *thd= current_thd;
@@ -4916,9 +4934,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
bool
Item_func_set_user_var::update_hash(void *ptr, size_t length,
- Item_result res_type,
- CHARSET_INFO *cs,
- bool unsigned_arg)
+ const Type_handler *th,
+ CHARSET_INFO *cs)
{
/*
If we set a variable explicitly to NULL then keep the old
@@ -4932,9 +4949,8 @@ Item_func_set_user_var::update_hash(void *ptr, size_t length,
else
null_value= args[0]->null_value;
if (null_value && null_item)
- res_type= m_var_entry->type; // Don't change type of item
- if (::update_hash(m_var_entry, null_value,
- ptr, length, res_type, cs, unsigned_arg))
+ th= m_var_entry->type_handler(); // Don't change type of item
+ if (::update_hash(m_var_entry, null_value, ptr, length, th, cs))
{
null_value= 1;
return 1;
@@ -4950,7 +4966,7 @@ double user_var_entry::val_real(bool *null_value)
if ((*null_value= (value == 0)))
return 0.0;
- switch (type) {
+ switch (type_handler()->result_type()) {
case REAL_RESULT:
return *(double*) value;
case INT_RESULT:
@@ -4975,7 +4991,7 @@ longlong user_var_entry::val_int(bool *null_value) const
if ((*null_value= (value == 0)))
return 0;
- switch (type) {
+ switch (type_handler()->result_type()) {
case REAL_RESULT:
return (longlong) *(double*) value;
case INT_RESULT:
@@ -5004,12 +5020,12 @@ String *user_var_entry::val_str(bool *null_value, String *str,
if ((*null_value= (value == 0)))
return (String*) 0;
- switch (type) {
+ switch (type_handler()->result_type()) {
case REAL_RESULT:
str->set_real(*(double*) value, decimals, charset());
break;
case INT_RESULT:
- if (!unsigned_flag)
+ if (!type_handler()->is_unsigned())
str->set(*(longlong*) value, charset());
else
str->set(*(ulonglong*) value, charset());
@@ -5036,7 +5052,7 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
if ((*null_value= (value == 0)))
return 0;
- switch (type) {
+ switch (type_handler()->result_type()) {
case REAL_RESULT:
double2my_decimal(E_DEC_FATAL_ERROR, *(double*) value, val);
break;
@@ -5175,33 +5191,37 @@ Item_func_set_user_var::update()
case REAL_RESULT:
{
res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
- REAL_RESULT, &my_charset_numeric, 0);
+ &type_handler_double, &my_charset_numeric);
break;
}
case INT_RESULT:
{
res= update_hash((void*) &save_result.vint, sizeof(save_result.vint),
- INT_RESULT, &my_charset_numeric, unsigned_flag);
+ unsigned_flag ? (Type_handler *) &type_handler_ulonglong :
+ (Type_handler *) &type_handler_slonglong,
+ &my_charset_numeric);
break;
}
case STRING_RESULT:
{
if (!save_result.vstr) // Null value
- res= update_hash((void*) 0, 0, STRING_RESULT, &my_charset_bin, 0);
+ res= update_hash((void*) 0, 0, &type_handler_long_blob, &my_charset_bin);
else
res= update_hash((void*) save_result.vstr->ptr(),
- save_result.vstr->length(), STRING_RESULT,
- save_result.vstr->charset(), 0);
+ save_result.vstr->length(),
+ field_type() == MYSQL_TYPE_GEOMETRY ?
+ type_handler() : &type_handler_long_blob,
+ save_result.vstr->charset());
break;
}
case DECIMAL_RESULT:
{
if (!save_result.vdec) // Null value
- res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin, 0);
+ res= update_hash((void*) 0, 0, &type_handler_newdecimal, &my_charset_bin);
else
res= update_hash((void*) save_result.vdec,
- sizeof(my_decimal), DECIMAL_RESULT,
- &my_charset_numeric, 0);
+ sizeof(my_decimal), &type_handler_newdecimal,
+ &my_charset_numeric);
break;
}
case ROW_RESULT:
@@ -5593,9 +5613,8 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command,
user_var_event->value= (char*) user_var_event +
ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT));
user_var_event->user_var_event= var_entry;
- user_var_event->type= var_entry->type;
+ user_var_event->th= var_entry->type_handler();
user_var_event->charset_number= var_entry->charset()->number;
- user_var_event->unsigned_flag= var_entry->unsigned_flag;
if (!var_entry->value)
{
/* NULL value*/
@@ -5637,9 +5656,9 @@ bool Item_func_get_user_var::fix_length_and_dec(THD *thd)
*/
if (likely(!error && m_var_entry))
{
- unsigned_flag= m_var_entry->unsigned_flag;
+ unsigned_flag= m_var_entry->type_handler()->is_unsigned();
max_length= (uint32)m_var_entry->length;
- switch (m_var_entry->type) {
+ switch (m_var_entry->type_handler()->result_type()) {
case REAL_RESULT:
collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
fix_char_length(DBL_DIG + 8);
@@ -5658,6 +5677,8 @@ bool Item_func_get_user_var::fix_length_and_dec(THD *thd)
collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT);
max_length= MAX_BLOB_WIDTH - 1;
set_handler(&type_handler_long_blob);
+ if (m_var_entry->type_handler()->field_type() == MYSQL_TYPE_GEOMETRY)
+ set_handler(m_var_entry->type_handler());
break;
case DECIMAL_RESULT:
collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
@@ -5730,7 +5751,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(thd->lex->exchange);
if (!(entry= get_variable(&thd->user_vars, &org_name, 1)))
return TRUE;
- entry->type= STRING_RESULT;
+ entry->set_handler(&type_handler_long_blob);
/*
Let us set the same collation which is used for loading
of fields in LOAD DATA INFILE.
@@ -5746,15 +5767,14 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
void Item_user_var_as_out_param::set_null_value(CHARSET_INFO* cs)
{
- ::update_hash(entry, TRUE, 0, 0, STRING_RESULT, cs, 0 /* unsigned_arg */);
+ ::update_hash(entry, TRUE, 0, 0, &type_handler_long_blob, cs);
}
void Item_user_var_as_out_param::set_value(const char *str, uint length,
CHARSET_INFO* cs)
{
- ::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs,
- 0 /* unsigned_arg */);
+ ::update_hash(entry, FALSE, (void*)str, length, &type_handler_long_blob, cs);
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 435875bd..1f185eff 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -55,8 +55,40 @@ protected:
bool check_argument_types_can_return_date(uint start, uint end) const;
bool check_argument_types_can_return_time(uint start, uint end) const;
void print_cast_temporal(String *str, enum_query_type query_type);
+
+ void print_schema_qualified_name(String *to,
+ const LEX_CSTRING &schema_name,
+ const LEX_CSTRING &function_name) const
+ {
+ // e.g. oracle_schema.func()
+ to->append(schema_name);
+ to->append('.');
+ to->append(function_name);
+ }
+
+ void print_sql_mode_qualified_name(String *to,
+ enum_query_type query_type,
+ const LEX_CSTRING &function_name) const
+ {
+ const Schema *func_schema= schema();
+ if (!func_schema || func_schema == Schema::find_implied(current_thd))
+ to->append(function_name);
+ else
+ print_schema_qualified_name(to, func_schema->name(), function_name);
+ }
+
+ void print_sql_mode_qualified_name(String *to, enum_query_type query_type)
+ const
+ {
+ return print_sql_mode_qualified_name(to, query_type, func_name_cstring());
+ }
+
public:
+ // Print an error message for a builtin-schema qualified function call
+ static void wrong_param_count_error(const LEX_CSTRING &schema_name,
+ const LEX_CSTRING &func_name);
+
table_map not_null_tables_cache;
enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
@@ -79,6 +111,38 @@ public:
CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider
CASE_SIMPLE_FUNC, // Used by ColumnStore/spider,
};
+
+ /*
+ A function bitmap. Useful when some operation needs to be applied only
+ to certain functions. For now we only need to distinguish some
+ comparison predicates.
+ */
+ enum Bitmap : ulonglong
+ {
+ BITMAP_NONE= 0,
+ BITMAP_EQ= 1ULL << EQ_FUNC,
+ BITMAP_EQUAL= 1ULL << EQUAL_FUNC,
+ BITMAP_NE= 1ULL << NE_FUNC,
+ BITMAP_LT= 1ULL << LT_FUNC,
+ BITMAP_LE= 1ULL << LE_FUNC,
+ BITMAP_GE= 1ULL << GE_FUNC,
+ BITMAP_GT= 1ULL << GT_FUNC,
+ BITMAP_LIKE= 1ULL << LIKE_FUNC,
+ BITMAP_BETWEEN= 1ULL << BETWEEN,
+ BITMAP_IN= 1ULL << IN_FUNC,
+ BITMAP_MULT_EQUAL= 1ULL << MULT_EQUAL_FUNC,
+ BITMAP_OTHER= 1ULL << 63,
+ BITMAP_ALL= 0xFFFFFFFFFFFFFFFFULL,
+ BITMAP_ANY_EQUALITY= BITMAP_EQ | BITMAP_EQUAL | BITMAP_MULT_EQUAL,
+ BITMAP_EXCEPT_ANY_EQUALITY= BITMAP_ALL & ~BITMAP_ANY_EQUALITY,
+ };
+
+ ulonglong bitmap_bit() const
+ {
+ Functype type= functype();
+ return 1ULL << (type > 63 ? 63 : type);
+ }
+
static scalar_comparison_op functype_to_scalar_comparison_op(Functype type)
{
switch (type) {
@@ -170,9 +234,15 @@ public:
List<Item> &fields, uint flags) override;
void print(String *str, enum_query_type query_type) override;
void print_op(String *str, enum_query_type query_type);
- void print_args(String *str, uint from, enum_query_type query_type);
+ void print_args(String *str, uint from, enum_query_type query_type) const;
+ void print_args_parenthesized(String *str, enum_query_type query_type) const
+ {
+ str->append('(');
+ print_args(str, 0, query_type);
+ str->append(')');
+ }
bool is_null() override
- {
+ {
update_null_value();
return null_value;
}
@@ -388,15 +458,6 @@ public:
}
}
void convert_const_compared_to_int_field(THD *thd);
- /**
- Prepare arguments and setup a comparator.
- Used in Item_func_xxx with two arguments and a comparator,
- e.g. Item_bool_func2 and Item_func_nullif.
- args[0] or args[1] can be modified:
- - converted to character set and collation of the operation
- - or replaced to an Item_int_with_ref
- */
- bool setup_args_and_comparator(THD *thd, Arg_comparator *cmp);
Item_func *get_item_func() override { return this; }
bool is_simplified_cond_processor(void *arg) override
{ return const_item() && !val_int(); }
@@ -3386,8 +3447,8 @@ public:
String *str_result(String *str) override;
my_decimal *val_decimal_result(my_decimal *) override;
bool is_null_result() override;
- bool update_hash(void *ptr, size_t length, enum Item_result type,
- CHARSET_INFO *cs, bool unsigned_arg);
+ bool update_hash(void *ptr, size_t length, const Type_handler *th,
+ CHARSET_INFO *cs);
bool send(Protocol *protocol, st_value *buffer) override;
void make_send_field(THD *thd, Send_field *tmp_field) override;
bool check(bool use_result_field);
@@ -4234,7 +4295,6 @@ double my_double_round(double value, longlong dec, bool dec_unsigned,
extern bool volatile mqh_used;
bool update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
- Item_result type, CHARSET_INFO *cs,
- bool unsigned_arg);
+ const Type_handler *th, CHARSET_INFO *cs);
#endif /* ITEM_FUNC_INCLUDED */
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index 590fde88..97d7b89f 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -911,7 +911,7 @@ static int alloc_tmp_paths(THD *thd, uint n_paths,
{
if (*tmp_paths == 0)
{
- MEM_ROOT *root= thd->stmt_arena->mem_root;
+ MEM_ROOT *root= thd->active_stmt_arena_to_use()->mem_root;
*paths= (json_path_with_flags *) alloc_root(root,
sizeof(json_path_with_flags) * n_paths);
@@ -943,21 +943,47 @@ static void mark_constant_paths(json_path_with_flags *p,
}
-bool Item_json_str_multipath::fix_fields(THD *thd, Item **ref)
+Item_json_str_multipath::~Item_json_str_multipath()
{
- return alloc_tmp_paths(thd, get_n_paths(), &paths, &tmp_paths) ||
- Item_str_func::fix_fields(thd, ref);
+ if (tmp_paths)
+ {
+ for (uint i= n_paths; i>0; i--)
+ tmp_paths[i-1].free();
+ }
}
-void Item_json_str_multipath::cleanup()
+bool Item_json_str_multipath::fix_fields(THD *thd, Item **ref)
{
- if (tmp_paths)
+ if (!tmp_paths)
{
- for (uint i= get_n_paths(); i>0; i--)
- tmp_paths[i-1].free();
+ /*
+ Remember the number of paths and allocate required memory on first time
+ the method fix_fields() is invoked. For prepared statements the method
+ fix_fields can be called several times for the same item because its
+ clean up is performed every item a prepared statement finishing its
+ execution. In result, the data member fixed is reset and the method
+ fix_field() is invoked on next time the same prepared statement be
+ executed. On the other side, any memory allocations on behalf of
+ the prepared statement must be performed only once on its first execution.
+ The data member tmp_path is kind a guard to do these activities only once
+ on first time the method fix_field() is called.
+ */
+ n_paths= get_n_paths();
+
+ if (alloc_tmp_paths(thd, n_paths, &paths, &tmp_paths))
+ return true;
}
- Item_str_func::cleanup();
+
+#ifdef PROTECT_STATEMENT_MEMROOT
+ /*
+ Check that the number of paths remembered on first run of a statement
+ never changed later.
+ */
+ DBUG_ASSERT(n_paths == get_n_paths());
+#endif
+
+ return Item_str_func::fix_fields(thd, ref);
}
@@ -1498,10 +1524,19 @@ return_null:
bool Item_func_json_contains_path::fix_fields(THD *thd, Item **ref)
{
- return alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) ||
- (p_found= (bool *) alloc_root(thd->mem_root,
- (arg_count-2)*sizeof(bool))) == NULL ||
- Item_int_func::fix_fields(thd, ref);
+ /*
+ See comments on Item_json_str_multipath::fix_fields regarding
+ the aim of the condition 'if (!tmp_paths)'.
+ */
+ if (!tmp_paths)
+ {
+ if (alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) ||
+ (p_found= (bool *) alloc_root(thd->active_stmt_arena_to_use()->mem_root,
+ (arg_count-2)*sizeof(bool))) == NULL)
+ return true;
+ }
+
+ return Item_int_func::fix_fields(thd, ref);
}
@@ -1514,8 +1549,7 @@ bool Item_func_json_contains_path::fix_length_and_dec(THD *thd)
return Item_bool_func::fix_length_and_dec(thd);
}
-
-void Item_func_json_contains_path::cleanup()
+Item_func_json_contains_path::~Item_func_json_contains_path()
{
if (tmp_paths)
{
@@ -1523,7 +1557,6 @@ void Item_func_json_contains_path::cleanup()
tmp_paths[i-1].free();
tmp_paths= 0;
}
- Item_int_func::cleanup();
}
@@ -4088,6 +4121,13 @@ int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s)
return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0);
}
+bool Item_func_json_arrayagg::fix_fields(THD *thd, Item **ref)
+{
+ bool res= Item_func_group_concat::fix_fields(thd, ref);
+ m_tmp_json.set_charset(collation.collation);
+ return res;
+}
+
String *Item_func_json_arrayagg::get_str_from_item(Item *i, String *tmp)
{
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
index 6f6b6a7a..1694013f 100644
--- a/sql/item_jsonfunc.h
+++ b/sql/item_jsonfunc.h
@@ -276,11 +276,26 @@ class Item_json_str_multipath: public Item_json_func
protected:
json_path_with_flags *paths;
String *tmp_paths;
+private:
+ /**
+ Number of paths returned by calling virtual method get_n_paths() and
+ remembered inside fix_fields(). It is used by the virtual destructor
+ ~Item_json_str_multipath() to iterate along allocated memory chunks stored
+ in the array tmp_paths and free every of them. The virtual method
+ get_n_paths() can't be used for this goal from within virtual destructor.
+ We could get rid of the virtual method get_n_paths() and store the number
+ of paths directly in the constructor of classes derived from the class
+ Item_json_str_multipath but presence of the method get_n_paths() allows
+ to check invariant that the number of arguments not changed between
+ sequential runs of the same prepared statement that seems to be useful.
+ */
+ uint n_paths;
public:
Item_json_str_multipath(THD *thd, List<Item> &list):
- Item_json_func(thd, list), tmp_paths(0) {}
+ Item_json_func(thd, list), paths(NULL), tmp_paths(0), n_paths(0) {}
+ virtual ~Item_json_str_multipath();
+
bool fix_fields(THD *thd, Item **ref);
- void cleanup();
virtual uint get_n_paths() const = 0;
};
@@ -347,6 +362,7 @@ protected:
public:
Item_func_json_contains_path(THD *thd, List<Item> &list):
Item_bool_func(thd, list), tmp_paths(0) {}
+ virtual ~Item_func_json_contains_path();
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("json_contains_path") };
@@ -354,7 +370,6 @@ public:
}
bool fix_fields(THD *thd, Item **ref) override;
bool fix_length_and_dec(THD *thd) override;
- void cleanup() override;
longlong val_int() override;
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_json_contains_path>(thd, this); }
@@ -717,6 +732,7 @@ public:
static LEX_CSTRING name= {STRING_WITH_LEN("json_arrayagg(") };
return name;
}
+ bool fix_fields(THD *thd, Item **ref) override;
enum Sumfunctype sum_func() const override { return JSON_ARRAYAGG_FUNC; }
String* val_str(String *str) override;
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 4bbf36ec..ce094bec 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1184,8 +1184,7 @@ bool Item_func_reverse::fix_length_and_dec(THD *thd)
Fix that this works with binary strings when using USE_MB
*/
-String *Item_func_replace::val_str_internal(String *str,
- String *empty_string_for_null)
+String *Item_func_replace::val_str_internal(String *str, bool null_to_empty)
{
DBUG_ASSERT(fixed());
String *res,*res2,*res3;
@@ -1203,13 +1202,8 @@ String *Item_func_replace::val_str_internal(String *str,
res=args[0]->val_str(str);
if (args[0]->null_value)
goto null;
- res2=args[1]->val_str(&tmp_value);
- if (args[1]->null_value)
- {
- if (!empty_string_for_null)
- goto null;
- res2= empty_string_for_null;
- }
+ if (!(res2= args[1]->val_str_null_to_empty(&tmp_value, null_to_empty)))
+ goto null;
res->set_charset(collation.collation);
#ifdef USE_MB
@@ -1226,12 +1220,8 @@ String *Item_func_replace::val_str_internal(String *str,
if (binary_cmp && (offset=res->strstr(*res2)) < 0)
return res;
#endif
- if (!(res3=args[2]->val_str(&tmp_value2)))
- {
- if (!empty_string_for_null)
- goto null;
- res3= empty_string_for_null;
- }
+ if (!(res3= args[2]->val_str_null_to_empty(&tmp_value2, null_to_empty)))
+ goto null;
from_length= res2->length();
to_length= res3->length();
@@ -1314,7 +1304,7 @@ redo:
}
while ((offset=res->strstr(*res2,(uint) offset)) >= 0);
}
- if (empty_string_for_null && !res->length())
+ if (null_to_empty && !res->length())
goto null;
return res;
@@ -1638,20 +1628,22 @@ bool Item_func_regexp_replace::append_replacement(String *str,
}
-String *Item_func_regexp_replace::val_str(String *str)
+String *Item_func_regexp_replace::val_str_internal(String *str,
+ bool null_to_empty)
{
DBUG_ASSERT(fixed());
char buff0[MAX_FIELD_WIDTH];
char buff2[MAX_FIELD_WIDTH];
String tmp0(buff0,sizeof(buff0),&my_charset_bin);
String tmp2(buff2,sizeof(buff2),&my_charset_bin);
- String *source= args[0]->val_str(&tmp0);
- String *replace= args[2]->val_str(&tmp2);
+ String *source, *replace;
LEX_CSTRING src, rpl;
size_t startoffset= 0;
- if ((null_value= (args[0]->null_value || args[2]->null_value ||
- re.recompile(args[1]))))
+ if ((null_value=
+ (!(source= args[0]->val_str(&tmp0)) ||
+ !(replace= args[2]->val_str_null_to_empty(&tmp2, null_to_empty)) ||
+ re.recompile(args[1]))))
return (String *) 0;
if (!(source= re.convert_if_needed(source, &re.subject_converter)) ||
@@ -2417,13 +2409,31 @@ bool Item_func_trim::fix_length_and_dec(THD *thd)
void Item_func_trim::print(String *str, enum_query_type query_type)
{
+ LEX_CSTRING suffix= {STRING_WITH_LEN("_oracle")};
if (arg_count == 1)
{
- Item_func::print(str, query_type);
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(func_name_cstring());
+ if (schema() == &oracle_schema_ref)
+ str->append(suffix);
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type, func_name_cstring());
+ print_args_parenthesized(str, query_type);
return;
}
- str->append(Item_func_trim::func_name_cstring());
- str->append(func_name_ext());
+
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(Item_func_trim::func_name_cstring());
+ if (schema() == &oracle_schema_ref)
+ str->append(suffix);
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type, Item_func_trim::func_name_cstring());
str->append('(');
str->append(mode_name());
str->append(' ');
@@ -3463,13 +3473,13 @@ String *Item_func_binlog_gtid_pos::val_str(String *str)
String name_str, *name;
longlong pos;
- if (args[0]->null_value || args[1]->null_value)
- goto err;
-
name= args[0]->val_str(&name_str);
pos= args[1]->val_int();
- if (pos < 0 || pos > UINT_MAX32)
+ if (args[0]->null_value || args[1]->null_value)
+ goto err;
+
+ if (pos < 0 || pos > (longlong) UINT_MAX32)
goto err;
if (gtid_state_from_binlog_pos(name->c_ptr_safe(), (uint32)pos, str))
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 3a3c5338..340847dd 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -307,6 +307,12 @@ protected:
public:
Item_func_concat(THD *thd, List<Item> &list): Item_str_func(thd, list) {}
Item_func_concat(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
String *val_str(String *) override;
bool fix_length_and_dec(THD *thd) override;
LEX_CSTRING func_name_cstring() const override
@@ -333,10 +339,17 @@ public:
:Item_func_concat(thd, a, b)
{ }
String *val_str(String *) override;
- LEX_CSTRING func_name_cstring() const override
+ const Schema *schema() const override { return &oracle_schema_ref; }
+ void print(String *str, enum_query_type query_type) override
{
- static LEX_CSTRING name= {STRING_WITH_LEN("concat_operator_oracle") };
- return name;
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(STRING_WITH_LEN("concat_operator_oracle"));
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd) override
{
@@ -430,12 +443,19 @@ public:
class Item_func_replace :public Item_str_func
{
String tmp_value,tmp_value2;
+protected:
+ String *val_str_internal(String *str, bool null_to_empty);
public:
Item_func_replace(THD *thd, Item *org, Item *find, Item *replace):
Item_str_func(thd, org, find, replace) {}
- String *val_str(String *to) override { return val_str_internal(to, NULL); };
+ String *val_str(String *to) override { return val_str_internal(to, false); };
bool fix_length_and_dec(THD *thd) override;
- String *val_str_internal(String *str, String *empty_string_for_null);
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("replace") };
@@ -453,11 +473,18 @@ public:
Item_func_replace_oracle(THD *thd, Item *org, Item *find, Item *replace):
Item_func_replace(thd, org, find, replace) {}
String *val_str(String *to) override
- { return val_str_internal(to, &tmp_emtpystr); };
- LEX_CSTRING func_name_cstring() const override
+ { return val_str_internal(to, true); };
+ const Schema *schema() const override { return &oracle_schema_ref; }
+ void print(String *str, enum_query_type query_type) override
{
- static LEX_CSTRING name= {STRING_WITH_LEN("replace_oracle") };
- return name;
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(STRING_WITH_LEN("replace_oracle"));
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_replace_oracle>(thd, this); }
@@ -470,10 +497,18 @@ class Item_func_regexp_replace :public Item_str_func
bool append_replacement(String *str,
const LEX_CSTRING *source,
const LEX_CSTRING *replace);
+protected:
+ String *val_str_internal(String *str, bool null_to_empty);
public:
Item_func_regexp_replace(THD *thd, Item *a, Item *b, Item *c):
Item_str_func(thd, a, b, c)
{}
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
void cleanup() override
{
DBUG_ENTER("Item_func_regexp_replace::cleanup");
@@ -481,7 +516,10 @@ public:
re.cleanup();
DBUG_VOID_RETURN;
}
- String *val_str(String *str) override;
+ String *val_str(String *str) override
+ {
+ return val_str_internal(str, false);
+ }
bool fix_length_and_dec(THD *thd) override;
LEX_CSTRING func_name_cstring() const override
{
@@ -492,6 +530,26 @@ public:
};
+class Item_func_regexp_replace_oracle: public Item_func_regexp_replace
+{
+public:
+ Item_func_regexp_replace_oracle(THD *thd, Item *a, Item *b, Item *c)
+ :Item_func_regexp_replace(thd, a, b, c)
+ {}
+ const Schema *schema() const { return &oracle_schema_ref; }
+ bool fix_length_and_dec(THD *thd)
+ {
+ bool rc= Item_func_regexp_replace::fix_length_and_dec(thd);
+ set_maybe_null(); // Empty result is converted to NULL
+ return rc;
+ }
+ String *val_str(String *str)
+ {
+ return val_str_internal(str, true);
+ }
+};
+
+
class Item_func_regexp_substr :public Item_str_func
{
Regexp_processor_pcre re;
@@ -621,8 +679,16 @@ public:
Item_func_substr(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
Item_func_substr(THD *thd, Item *a, Item *b, Item *c):
Item_str_func(thd, a, b, c) {}
+ Item_func_substr(THD *thd, List<Item> &list)
+ :Item_str_func(thd, list) {}
String *val_str(String *) override;
bool fix_length_and_dec(THD *thd) override;
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("substr") };
@@ -661,16 +727,25 @@ public:
Item_func_substr(thd, a, b) {}
Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c):
Item_func_substr(thd, a, b, c) {}
+ Item_func_substr_oracle(THD *thd, List<Item> &list)
+ :Item_func_substr(thd, list) {}
bool fix_length_and_dec(THD *thd) override
{
bool res= Item_func_substr::fix_length_and_dec(thd);
set_maybe_null();
return res;
}
- LEX_CSTRING func_name_cstring() const override
+ const Schema *schema() const override { return &oracle_schema_ref; }
+ void print(String *str, enum_query_type query_type) override
{
- static LEX_CSTRING name= {STRING_WITH_LEN("substr_oracle") };
- return name;
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(STRING_WITH_LEN("substr_oracle"));
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_substr_oracle>(thd, this); }
@@ -718,17 +793,13 @@ protected:
{
return trimmed_value(res, 0, res->length());
}
- virtual LEX_CSTRING func_name_ext() const
- {
- static LEX_CSTRING name_ext= {STRING_WITH_LEN("") };
- return name_ext;
- }
public:
Item_func_trim(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
Item_func_trim(THD *thd, Item *a): Item_str_func(thd, a) {}
Sql_mode_dependency value_depends_on_sql_mode() const override;
String *val_str(String *) override;
bool fix_length_and_dec(THD *thd) override;
+ const Schema *schema() const override { return &mariadb_schema; }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("trim") };
@@ -746,20 +817,11 @@ class Item_func_trim_oracle :public Item_func_trim
protected:
String *make_empty_result(String *str) override
{ null_value= 1; return NULL; }
- LEX_CSTRING func_name_ext() const override
- {
- static LEX_CSTRING name_ext= {STRING_WITH_LEN("_oracle") };
- return name_ext;
- }
public:
Item_func_trim_oracle(THD *thd, Item *a, Item *b):
Item_func_trim(thd, a, b) {}
Item_func_trim_oracle(THD *thd, Item *a): Item_func_trim(thd, a) {}
- LEX_CSTRING func_name_cstring() const override
- {
- static LEX_CSTRING name= {STRING_WITH_LEN("trim_oracle") };
- return name;
- }
+ const Schema *schema() const override { return &oracle_schema_ref; }
bool fix_length_and_dec(THD *thd) override
{
bool res= Item_func_trim::fix_length_and_dec(thd);
@@ -781,6 +843,7 @@ public:
return Item_func::value_depends_on_sql_mode();
}
String *val_str(String *) override;
+ const Schema *schema() const override { return &mariadb_schema; }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("ltrim") };
@@ -798,20 +861,11 @@ class Item_func_ltrim_oracle :public Item_func_ltrim
protected:
String *make_empty_result(String *str) override
{ null_value= 1; return NULL; }
- LEX_CSTRING func_name_ext() const override
- {
- static LEX_CSTRING name_ext= {STRING_WITH_LEN("_oracle") };
- return name_ext;
- }
public:
Item_func_ltrim_oracle(THD *thd, Item *a, Item *b):
Item_func_ltrim(thd, a, b) {}
Item_func_ltrim_oracle(THD *thd, Item *a): Item_func_ltrim(thd, a) {}
- LEX_CSTRING func_name_cstring() const override
- {
- static LEX_CSTRING name= {STRING_WITH_LEN("ltrim_oracle") };
- return name;
- }
+ const Schema *schema() const override { return &oracle_schema_ref; }
bool fix_length_and_dec(THD *thd) override
{
bool res= Item_func_ltrim::fix_length_and_dec(thd);
@@ -829,6 +883,7 @@ public:
Item_func_rtrim(THD *thd, Item *a, Item *b): Item_func_trim(thd, a, b) {}
Item_func_rtrim(THD *thd, Item *a): Item_func_trim(thd, a) {}
String *val_str(String *) override;
+ const Schema *schema() const override { return &mariadb_schema; }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("rtrim") };
@@ -846,20 +901,11 @@ class Item_func_rtrim_oracle :public Item_func_rtrim
protected:
String *make_empty_result(String *str) override
{ null_value= 1; return NULL; }
- LEX_CSTRING func_name_ext() const override
- {
- static LEX_CSTRING name_ext= {STRING_WITH_LEN("_oracle") };
- return name_ext;
- }
public:
Item_func_rtrim_oracle(THD *thd, Item *a, Item *b):
Item_func_rtrim(thd, a, b) {}
Item_func_rtrim_oracle(THD *thd, Item *a): Item_func_rtrim(thd, a) {}
- LEX_CSTRING func_name_cstring() const override
- {
- static LEX_CSTRING name= {STRING_WITH_LEN("rtrim_oracle") };
- return name;
- }
+ const Schema *schema() const override { return &oracle_schema_ref; }
bool fix_length_and_dec(THD *thd) override
{
bool res= Item_func_rtrim::fix_length_and_dec(thd);
@@ -1032,6 +1078,12 @@ class Item_func_decode :public Item_func_encode
{
public:
Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {}
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("decode") };
@@ -1402,6 +1454,8 @@ public:
Item_str_func(thd, arg1, arg2, arg3) {}
Item_func_pad(THD *thd, Item *arg1, Item *arg2):
Item_str_func(thd, arg1, arg2) {}
+ Item_func_pad(THD *thd, List<Item> &list):
+ Item_str_func(thd,list) {}
bool fix_length_and_dec(THD *thd) override;
};
@@ -1413,7 +1467,15 @@ public:
Item_func_pad(thd, arg1, arg2, arg3) {}
Item_func_rpad(THD *thd, Item *arg1, Item *arg2):
Item_func_pad(thd, arg1, arg2) {}
+ Item_func_rpad(THD *thd, List<Item> &list):
+ Item_func_pad(thd,list) {}
String *val_str(String *) override;
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("rpad") };
@@ -1434,16 +1496,25 @@ public:
Item_func_rpad(thd, arg1, arg2, arg3) {}
Item_func_rpad_oracle(THD *thd, Item *arg1, Item *arg2):
Item_func_rpad(thd, arg1, arg2) {}
+ Item_func_rpad_oracle(THD *thd, List<Item> &list):
+ Item_func_rpad(thd,list) {}
bool fix_length_and_dec(THD *thd) override
{
bool res= Item_func_rpad::fix_length_and_dec(thd);
set_maybe_null();
return res;
}
- LEX_CSTRING func_name_cstring() const override
+ const Schema *schema() const override { return &oracle_schema_ref; }
+ void print(String *str, enum_query_type query_type) override
{
- static LEX_CSTRING name= {STRING_WITH_LEN("rpad_oracle") };
- return name;
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(STRING_WITH_LEN("rpad_oracle"));
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_rpad_oracle>(thd, this); }
@@ -1457,7 +1528,15 @@ public:
Item_func_pad(thd, arg1, arg2, arg3) {}
Item_func_lpad(THD *thd, Item *arg1, Item *arg2):
Item_func_pad(thd, arg1, arg2) {}
+ Item_func_lpad(THD *thd, List<Item> &list):
+ Item_func_pad(thd,list) {}
String *val_str(String *) override;
+ const Schema *schema() const override { return &mariadb_schema; }
+ void print(String *str, enum_query_type query_type) override
+ {
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
+ }
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("lpad") };
@@ -1477,16 +1556,25 @@ public:
Item_func_lpad(thd, arg1, arg2, arg3) {}
Item_func_lpad_oracle(THD *thd, Item *arg1, Item *arg2):
Item_func_lpad(thd, arg1, arg2) {}
+ Item_func_lpad_oracle(THD *thd, List<Item> &list):
+ Item_func_lpad(thd,list) {}
bool fix_length_and_dec(THD *thd) override
{
bool res= Item_func_lpad::fix_length_and_dec(thd);
set_maybe_null();
return res;
}
- LEX_CSTRING func_name_cstring() const override
+ const Schema *schema() const override { return &oracle_schema_ref; }
+ void print(String *str, enum_query_type query_type) override
{
- static LEX_CSTRING name= {STRING_WITH_LEN("lpad_oracle") };
- return name;
+ if (query_type & QT_FOR_FRM)
+ {
+ // 10.3 downgrade compatibility for FRM
+ str->append(STRING_WITH_LEN("lpad_oracle"));
+ }
+ else
+ print_sql_mode_qualified_name(str, query_type);
+ print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_lpad_oracle>(thd, this); }
@@ -1732,6 +1820,9 @@ public:
collation.set(args[0]->collation);
ulonglong max_result_length= (ulonglong) args[0]->max_length * 2 +
2 * collation.collation->mbmaxlen;
+ // NULL argument is returned as a string "NULL" without quotes
+ if (args[0]->maybe_null())
+ set_if_bigger(max_result_length, 4 * collation.collation->mbmaxlen);
max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH);
return FALSE;
}
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 8b473278..7d1a72b9 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -3281,8 +3281,12 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
if (eqs.at(i).outer_exp->
walk(&Item::find_item_processor, TRUE, upper->item))
break;
+ DBUG_ASSERT(thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute() ||
+ thd->stmt_arena->is_conventional());
+ DBUG_ASSERT(thd->stmt_arena->mem_root == thd->mem_root);
if (i == (uint)eqs.elements() &&
- (in_subs->upper_refs.push_back(upper, thd->stmt_arena->mem_root)))
+ (in_subs->upper_refs.push_back(
+ upper, thd->mem_root)))
goto out;
}
}
@@ -3989,14 +3993,14 @@ bool subselect_union_engine::fix_length_and_dec(Item_cache **row)
if (unit->first_select()->item_list.elements == 1)
{
- if (set_row(unit->types, row))
+ if (set_row(unit->item_list, row))
return TRUE;
item->collation.set(row[0]->collation);
}
else
{
bool maybe_null_saved= maybe_null;
- if (set_row(unit->types, row))
+ if (set_row(unit->item_list, row))
return TRUE;
maybe_null= maybe_null_saved;
}
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index bbd09a59..bcaf229d 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1296,9 +1296,14 @@ void Item_sum_min_max::setup_hybrid(THD *thd, Item *item, Item *value_arg)
/* Don't cache value, as it will change */
if (!item->const_item())
arg_cache->set_used_tables(RAND_TABLE_BIT);
+ DBUG_ASSERT(item->type_handler_for_comparison() ==
+ value->type_handler_for_comparison());
+ DBUG_ASSERT(item->type_handler_for_comparison() ==
+ arg_cache->type_handler_for_comparison());
cmp= new (thd->mem_root) Arg_comparator();
if (cmp)
- cmp->set_cmp_func(thd, this, (Item**)&arg_cache, (Item**)&value, FALSE);
+ cmp->set_cmp_func(thd, this, item->type_handler_for_comparison(),
+ (Item**)&arg_cache, (Item**)&value, FALSE);
DBUG_VOID_RETURN;
}
@@ -4287,8 +4292,14 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
char *buf;
String *new_separator;
- if (!(buf= (char*) thd->stmt_arena->alloc(buflen)) ||
- !(new_separator= new(thd->stmt_arena->mem_root)
+ DBUG_ASSERT(thd->active_stmt_arena_to_use()->
+ is_stmt_prepare_or_first_stmt_execute() ||
+ thd->active_stmt_arena_to_use()->
+ is_conventional() ||
+ thd->active_stmt_arena_to_use()->state ==
+ Query_arena::STMT_SP_QUERY_ARGUMENTS);
+ if (!(buf= (char*) thd->active_stmt_arena_to_use()->alloc(buflen)) ||
+ !(new_separator= new(thd->active_stmt_arena_to_use()->mem_root)
String(buf, buflen, collation.collation)))
return TRUE;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index b624a381..db38e44d 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -3080,6 +3080,13 @@ void Item_char_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN(" charset "));
str->append(cast_cs->cs_name);
+ /*
+ Print the "binary" keyword in cases like:
+ CAST('str' AS CHAR CHARACTER SET latin1 BINARY)
+ */
+ if ((cast_cs->state & MY_CS_BINSORT) &&
+ Charset(cast_cs).can_have_collate_clause())
+ str->append(STRING_WITH_LEN(" binary"));
}
str->append(')');
}
diff --git a/sql/key.cc b/sql/key.cc
index 4e40a335..93b172c3 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -609,7 +609,7 @@ int key_rec_cmp(void *key_p, uchar *first_rec, uchar *second_rec)
}
/*
No null values in the fields
- We use the virtual method cmp_max with a max length parameter.
+ We use the virtual method cmp_prefix with a max length parameter.
For most field types this translates into a cmp without
max length. The exceptions are the BLOB and VARCHAR field types
that take the max length into account.
diff --git a/sql/lex.h b/sql/lex.h
index 89e055a4..b1f5718d 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -79,9 +79,9 @@ SYMBOL symbols[] = {
{ "AT", SYM(AT_SYM)},
{ "ATOMIC", SYM(ATOMIC_SYM)},
{ "AUTHORS", SYM(AUTHORS_SYM)},
+ { "AUTO", SYM(AUTO_SYM)},
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
{ "AUTOEXTEND_SIZE", SYM(AUTOEXTEND_SIZE_SYM)},
- { "AUTO", SYM(AUTO_SYM)},
{ "AVG", SYM(AVG_SYM)},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
{ "BACKUP", SYM(BACKUP_SYM)},
@@ -428,7 +428,6 @@ SYMBOL symbols[] = {
{ "NCHAR", SYM(NCHAR_SYM)},
{ "NESTED", SYM(NESTED_SYM)},
{ "NEVER", SYM(NEVER_SYM)},
- { "NEW", SYM(NEW_SYM)},
{ "NEXT", SYM(NEXT_SYM)},
{ "NEXTVAL", SYM(NEXTVAL_SYM)},
{ "NO", SYM(NO_SYM)},
@@ -686,7 +685,6 @@ SYMBOL symbols[] = {
{ "TRUE", SYM(TRUE_SYM)},
{ "TRUNCATE", SYM(TRUNCATE_SYM)},
{ "TYPE", SYM(TYPE_SYM)},
- { "TYPES", SYM(TYPES_SYM)},
{ "UNBOUNDED", SYM(UNBOUNDED_SYM)},
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM)},
{ "UNDEFINED", SYM(UNDEFINED_SYM)},
@@ -752,7 +750,6 @@ SYMBOL symbols[] = {
SYMBOL sql_functions[] = {
{ "ADDDATE", SYM(ADDDATE_SYM)},
- { "ADD_MONTHS", SYM(ADD_MONTHS_SYM)},
{ "BIT_AND", SYM(BIT_AND)},
{ "BIT_OR", SYM(BIT_OR)},
{ "BIT_XOR", SYM(BIT_XOR)},
@@ -763,7 +760,6 @@ SYMBOL sql_functions[] = {
{ "CURTIME", SYM(CURTIME)},
{ "DATE_ADD", SYM(DATE_ADD_INTERVAL)},
{ "DATE_SUB", SYM(DATE_SUB_INTERVAL)},
- { "DATE_FORMAT", SYM(DATE_FORMAT_SYM)},
{ "DENSE_RANK", SYM(DENSE_RANK_SYM)},
{ "EXTRACT", SYM(EXTRACT_SYM)},
{ "FIRST_VALUE", SYM(FIRST_VALUE_SYM)},
diff --git a/sql/log.cc b/sql/log.cc
index d3879aad..4b9e5ebc 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1457,7 +1457,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length,
query_utime= (current_utime - thd->start_utime);
lock_utime= (thd->utime_after_lock - thd->start_utime);
my_hrtime_t current_time= { hrtime_from_time(thd->start_time) +
- thd->start_time_sec_part + query_utime };
+ thd->start_time_sec_part };
if (!query || thd->get_command() == COM_STMT_PREPARE)
{
@@ -2539,6 +2539,23 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd)
}
+/*
+ Check if there was an error while writing the statement cache.
+ If the cache content is corrupt due to an error, we should write an incident
+ event to the binlog rather than write corrupt data to it.
+*/
+bool
+MYSQL_BIN_LOG::check_cache_error(THD *thd, binlog_cache_data *cache_data)
+{
+ if (!cache_data)
+ return false;
+ if (check_write_error(thd))
+ return true;
+ if (!cache_data->empty() && cache_data->cache_log.error)
+ return true;
+ return false;
+}
+
/**
@note
How do we handle this (unlikely but legal) case:
@@ -3796,7 +3813,6 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
bool null_created_arg,
bool need_mutex)
{
- File file= -1;
xid_count_per_binlog *new_xid_list_entry= NULL, *b;
DBUG_ENTER("MYSQL_BIN_LOG::open");
@@ -4192,8 +4208,6 @@ err:
sql_print_error(fatal_log_error, (name) ? name : log_name, tmp_errno);
if (new_xid_list_entry)
delete new_xid_list_entry;
- if (file >= 0)
- mysql_file_close(file, MYF(0));
close(LOG_CLOSE_INDEX);
DBUG_RETURN(1);
}
@@ -5465,8 +5479,6 @@ int MYSQL_BIN_LOG::new_file_without_locking()
/**
Start writing to a new log file or reopen the old file.
- @param need_lock Set to 1 if caller has not locked LOCK_log
-
@retval
nonzero - error
@@ -6269,12 +6281,13 @@ bool THD::binlog_write_table_map(TABLE *table, bool with_annotate)
int error= 1;
bool is_transactional= table->file->row_logging_has_trans;
DBUG_ENTER("THD::binlog_write_table_map");
- DBUG_PRINT("enter", ("table: %p (%s: #%lu)",
+ DBUG_PRINT("enter", ("table: %p (%s: #%llu)",
table, table->s->table_name.str,
table->s->table_map_id));
/* Pre-conditions */
- DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
+ DBUG_ASSERT((table->s->table_map_id & MAX_TABLE_MAP_ID) != UINT32_MAX &&
+ (table->s->table_map_id & MAX_TABLE_MAP_ID) != 0);
/* Ensure that all events in a GTID group are in the same cache */
if (variables.option_bits & OPTION_GTID_BEGIN)
@@ -6315,7 +6328,7 @@ write_err:
engines, data is written to table but writing to binary log failed. In
these scenarios rollback is not possible. Hence report an incident.
*/
- if (mysql_bin_log.check_write_error(this) && cache_data &&
+ if (mysql_bin_log.check_cache_error(this, cache_data) &&
lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE) &&
table->current_lock == F_WRLCK)
cache_data->set_incident();
@@ -6447,20 +6460,37 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
/*
Write pending event to the cache.
*/
+#ifndef DBUG_OFF
+ bool clear_dbug= false;
+#endif
DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
- {DBUG_SET("+d,simulate_file_write_error");});
+ {
+ if (my_b_tell(&cache_data->cache_log) > 10000)
+ {
+ DBUG_SET("+d,simulate_file_write_error");
+ clear_dbug= true;
+ }
+ });
if (writer.write(pending))
{
set_write_error(thd, is_transactional);
- if (check_write_error(thd) && cache_data &&
+ if (check_cache_error(thd, cache_data) &&
stmt_has_updated_non_trans_table(thd))
cache_data->set_incident();
delete pending;
cache_data->set_pending(NULL);
DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
- {DBUG_SET("-d,simulate_file_write_error");});
+ {
+ if (clear_dbug)
+ DBUG_SET("-d,simulate_file_write_error");
+ });
DBUG_RETURN(1);
}
+ DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
+ {
+ if (clear_dbug)
+ DBUG_SET("-d,simulate_file_write_error");
+ });
delete pending;
}
@@ -6950,18 +6980,12 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
BINLOG_USER_VAR_EVENT *user_var_event;
get_dynamic(&thd->user_var_events,(uchar*) &user_var_event, i);
- /* setting flags for user var log event */
- uchar flags= User_var_log_event::UNDEF_F;
- if (user_var_event->unsigned_flag)
- flags|= User_var_log_event::UNSIGNED_F;
-
User_var_log_event e(thd, user_var_event->user_var_event->name.str,
user_var_event->user_var_event->name.length,
user_var_event->value,
user_var_event->length,
- user_var_event->type,
- user_var_event->charset_number,
- flags,
+ user_var_event->th->user_var_log_event_data_type(
+ user_var_event->charset_number),
using_trans,
direct);
if (write_event(&e, cache_data, file))
@@ -7057,7 +7081,7 @@ err:
if (unlikely(error))
{
set_write_error(thd, is_trans_cache);
- if (check_write_error(thd) && cache_data &&
+ if (check_cache_error(thd, cache_data) &&
stmt_has_updated_non_trans_table(thd))
cache_data->set_incident();
}
@@ -8751,13 +8775,10 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered");
++num_commits;
+ set_current_thd(current->thd);
if (current->cache_mngr->using_xa && likely(!current->error) &&
!DBUG_IF("skip_commit_ordered"))
- {
- mysql_mutex_lock(&current->thd->LOCK_thd_data);
run_commit_ordered(current->thd, current->all);
- mysql_mutex_unlock(&current->thd->LOCK_thd_data);
- }
current->thd->wakeup_subsequent_commits(current->error);
/*
@@ -8774,6 +8795,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
}
current= next;
}
+ set_current_thd(leader->thd);
DEBUG_SYNC(leader->thd, "commit_after_group_run_commit_ordered");
mysql_mutex_unlock(&LOCK_commit_ordered);
DEBUG_SYNC(leader->thd, "commit_after_group_release_commit_ordered");
@@ -8794,6 +8816,20 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry,
DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_or_stmt");
+ /*
+ An error in the trx_cache will truncate the cache to the last good
+ statement, it won't leave a lingering error. Assert that this holds.
+ */
+ DBUG_ASSERT(!(entry->using_trx_cache && !mngr->trx_cache.empty() &&
+ mngr->get_binlog_cache_log(TRUE)->error));
+ /*
+ An error in the stmt_cache would be caught on the higher level and result
+ in an incident event being written over a (possibly corrupt) cache content.
+ Assert that this holds.
+ */
+ DBUG_ASSERT(!(entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
+ mngr->get_binlog_cache_log(FALSE)->error));
+
if (write_gtid_event(entry->thd, is_prepared_xa(entry->thd),
entry->using_trx_cache, commit_id,
has_xid, entry->ro_1pc))
@@ -11136,7 +11172,7 @@ Recovery_context::Recovery_context() :
prev_event_pos(0),
last_gtid_standalone(false), last_gtid_valid(false), last_gtid_no2pc(false),
last_gtid_engines(0),
- do_truncate(rpl_semi_sync_slave_enabled),
+ do_truncate(repl_semisync_slave.get_slave_enabled()),
truncate_validated(false), truncate_reset_done(false),
truncate_set_in_1st(false), id_binlog(MAX_binlog_id),
checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF), gtid_maybe_to_truncate(NULL)
@@ -11945,14 +11981,21 @@ set_binlog_snapshot_file(const char *src)
void
TC_LOG_BINLOG::set_status_variables(THD *thd)
{
- binlog_cache_mngr *cache_mngr;
+ bool have_snapshot= false;
if (thd && opt_bin_log)
- cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- else
- cache_mngr= 0;
+ {
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ auto cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ have_snapshot= cache_mngr && cache_mngr->last_commit_pos_file[0];
+ if (have_snapshot)
+ {
+ set_binlog_snapshot_file(cache_mngr->last_commit_pos_file);
+ binlog_snapshot_position= cache_mngr->last_commit_pos_offset;
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ }
- bool have_snapshot= (cache_mngr && cache_mngr->last_commit_pos_file[0] != 0);
mysql_mutex_lock(&LOCK_commit_ordered);
binlog_status_var_num_commits= this->num_commits;
binlog_status_var_num_group_commits= this->num_group_commits;
@@ -11967,12 +12010,6 @@ TC_LOG_BINLOG::set_status_variables(THD *thd)
binlog_status_group_commit_trigger_timeout= this->group_commit_trigger_timeout;
binlog_status_group_commit_trigger_lock_wait= this->group_commit_trigger_lock_wait;
mysql_mutex_unlock(&LOCK_prepare_ordered);
-
- if (have_snapshot)
- {
- set_binlog_snapshot_file(cache_mngr->last_commit_pos_file);
- binlog_snapshot_position= cache_mngr->last_commit_pos_offset;
- }
}
@@ -12083,6 +12120,15 @@ IO_CACHE *wsrep_get_cache(THD * thd, bool is_transactional)
return NULL;
}
+bool wsrep_is_binlog_cache_empty(THD *thd)
+{
+ binlog_cache_mngr *cache_mngr=
+ (binlog_cache_mngr *) thd_get_ha_data(thd, binlog_hton);
+ if (cache_mngr)
+ return cache_mngr->trx_cache.empty() && cache_mngr->stmt_cache.empty();
+ return true;
+}
+
void wsrep_thd_binlog_trx_reset(THD * thd)
{
DBUG_ENTER("wsrep_thd_binlog_trx_reset");
@@ -12143,12 +12189,9 @@ void wsrep_register_binlog_handler(THD *thd, bool trx)
/*
Set an implicit savepoint in order to be able to truncate a trx-cache.
*/
- if (cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
- {
- my_off_t pos= 0;
- binlog_trans_log_savepos(thd, &pos);
- cache_mngr->trx_cache.set_prev_position(pos);
- }
+ my_off_t pos= 0;
+ binlog_trans_log_savepos(thd, &pos);
+ cache_mngr->trx_cache.set_prev_position(pos);
/*
Set callbacks in order to be able to call commmit or rollback.
diff --git a/sql/log.h b/sql/log.h
index c20f0fe5..ae813f75 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -824,6 +824,7 @@ public:
int write_cache(THD *thd, IO_CACHE *cache);
void set_write_error(THD *thd, bool is_transactional);
bool check_write_error(THD *thd);
+ bool check_cache_error(THD *thd, binlog_cache_data *cache_data);
void start_union_events(THD *thd, query_id_t query_id_param);
void stop_union_events(THD *thd);
@@ -1253,6 +1254,7 @@ static inline TC_LOG *get_tc_log_implementation()
#ifdef WITH_WSREP
IO_CACHE* wsrep_get_cache(THD *, bool);
+bool wsrep_is_binlog_cache_empty(THD *);
void wsrep_thd_binlog_trx_reset(THD * thd);
void wsrep_thd_binlog_stmt_rollback(THD * thd);
#endif /* WITH_WSREP */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 5e255646..336b032f 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -915,7 +915,8 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
Log_event* Log_event::read_log_event(IO_CACHE* file,
const Format_description_log_event *fdle,
- my_bool crc_check)
+ my_bool crc_check,
+ my_bool print_errors)
{
DBUG_ENTER("Log_event::read_log_event(IO_CACHE*,Format_description_log_event*...)");
DBUG_ASSERT(fdle != 0);
@@ -954,8 +955,12 @@ Log_event* Log_event::read_log_event(IO_CACHE* file,
goto err;
}
+ /*
+ print_errors is false to prevent redundant error messages cluttering up the
+ log, as it will be printed below (if _our_ print_errors is true)
+ */
if ((res= read_log_event((uchar*) event.ptr(), event.length(),
- &error, fdle, crc_check)))
+ &error, fdle, crc_check, false)))
res->register_temp_buf((uchar*) event.release(), true);
err:
@@ -966,13 +971,7 @@ err:
if (force_opt)
DBUG_RETURN(new Unknown_log_event());
#endif
- if (event.length() >= OLD_HEADER_LEN)
- sql_print_error("Error in Log_event::read_log_event(): '%s',"
- " data_len: %lu, event_type: %u", error,
- (ulong) uint4korr(&event[EVENT_LEN_OFFSET]),
- (uint) (uchar)event[EVENT_TYPE_OFFSET]);
- else
- sql_print_error("Error in Log_event::read_log_event(): '%s'", error);
+
/*
The SQL slave thread will check if file->error<0 to know
if there was an I/O error. Even if there is no "low-level" I/O errors
@@ -982,6 +981,19 @@ err:
only corrupt the slave's databases. So stop.
*/
file->error= -1;
+
+#ifndef MYSQL_CLIENT
+ if (!print_errors)
+ DBUG_RETURN(res);
+#endif
+
+ if (event.length() >= OLD_HEADER_LEN)
+ sql_print_error("Error in Log_event::read_log_event(): '%s',"
+ " data_len: %lu, event_type: %u", error,
+ (ulong) uint4korr(&event[EVENT_LEN_OFFSET]),
+ (uint) (uchar)event[EVENT_TYPE_OFFSET]);
+ else
+ sql_print_error("Error in Log_event::read_log_event(): '%s'", error);
}
DBUG_RETURN(res);
}
@@ -995,7 +1007,8 @@ err:
Log_event* Log_event::read_log_event(const uchar *buf, uint event_len,
const char **error,
const Format_description_log_event *fdle,
- my_bool crc_check)
+ my_bool crc_check,
+ my_bool print_errors)
{
Log_event* ev;
enum enum_binlog_checksum_alg alg;
@@ -1063,7 +1076,8 @@ Log_event* Log_event::read_log_event(const uchar *buf, uint event_len,
DBUG_RETURN(NULL);
#else
*error= ER_THD_OR_DEFAULT(current_thd, ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE);
- sql_print_error("%s", *error);
+ if (print_errors)
+ sql_print_error("%s", *error);
DBUG_RETURN(NULL);
#endif
}
@@ -2928,6 +2942,41 @@ XA_prepare_log_event(const uchar *buf,
User_var_log_event methods
**************************************************************************/
+bool Log_event_data_type::unpack_optional_attributes(const char *pos,
+ const char *end)
+
+{
+ for ( ; pos < end; )
+ {
+ switch (*pos) {
+ case CHUNK_SIGNED:
+ m_is_unsigned= false;
+ pos++;
+ continue;
+ case CHUNK_UNSIGNED:
+ m_is_unsigned= true;
+ pos++;
+ continue;
+ case CHUNK_DATA_TYPE_NAME:
+ {
+ pos++;
+ if (pos >= end)
+ return true;
+ uint length= (uchar) *pos++;
+ if (pos + length > end)
+ return true;
+ m_data_type_name= {pos, length};
+ pos+= length;
+ continue;
+ }
+ default:
+ break; // Unknown chunk
+ }
+ }
+ return false;
+}
+
+
User_var_log_event::
User_var_log_event(const uchar *buf, uint event_len,
const Format_description_log_event* description_event)
@@ -2937,7 +2986,8 @@ User_var_log_event(const uchar *buf, uint event_len,
#endif
{
bool error= false;
- const uchar *buf_start= buf, *buf_end= buf + event_len;
+ const uchar *const buf_start= buf;
+ const char *buf_end= reinterpret_cast<const char*>(buf) + event_len;
/* The Post-Header is empty. The Variable Data part begins immediately. */
buf+= description_event->common_header_len +
@@ -2965,11 +3015,8 @@ User_var_log_event(const uchar *buf, uint event_len,
buf+= UV_NAME_LEN_SIZE + name_len;
is_null= (bool) *buf;
- flags= User_var_log_event::UNDEF_F; // defaults to UNDEF_F
if (is_null)
{
- type= STRING_RESULT;
- charset_number= my_charset_bin.number;
val_len= 0;
val= 0;
}
@@ -2984,8 +3031,8 @@ User_var_log_event(const uchar *buf, uint event_len,
goto err;
}
- type= (Item_result) buf[UV_VAL_IS_NULL];
- charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE);
+ m_type= (Item_result) buf[UV_VAL_IS_NULL];
+ m_charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE);
val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE);
@@ -2998,20 +3045,14 @@ User_var_log_event(const uchar *buf, uint event_len,
the flags value.
Old events will not have this extra byte, thence,
- we keep the flags set to UNDEF_F.
+ we keep m_is_unsigned==false.
*/
- size_t bytes_read= (val + val_len) - (char*) buf_start;
- if (bytes_read > event_len)
+ const char *pos= val + val_len;
+ if (pos > buf_end || unpack_optional_attributes(pos, buf_end))
{
error= true;
goto err;
}
- if ((data_written - bytes_read) > 0)
- {
- flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
- UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE +
- val_len);
- }
}
err:
@@ -3305,7 +3346,7 @@ Rows_log_event::Rows_log_event(const uchar *buf, uint event_len,
}
else
{
- m_table_id= (ulong) uint6korr(post_start);
+ m_table_id= (ulonglong) uint6korr(post_start);
post_start+= RW_FLAGS_OFFSET;
}
@@ -3660,11 +3701,12 @@ Table_map_log_event::Table_map_log_event(const uchar *buf, uint event_len,
else
{
DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN);
- m_table_id= (ulong) uint6korr(post_start);
+ m_table_id= (ulonglong) uint6korr(post_start);
post_start+= TM_FLAGS_OFFSET;
}
- DBUG_ASSERT(m_table_id != ~0ULL);
+ DBUG_ASSERT((m_table_id & MAX_TABLE_MAP_ID) != UINT32_MAX &&
+ (m_table_id & MAX_TABLE_MAP_ID) != 0);
m_flags= uint2korr(post_start);
diff --git a/sql/log_event.h b/sql/log_event.h
index 67e06d70..f6101eb7 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -57,6 +57,8 @@
#include "rpl_gtid.h"
+#include "log_event_data_type.h"
+
/* Forward declarations */
#ifndef MYSQL_CLIENT
class String;
@@ -156,6 +158,12 @@ class String;
#define NUM_LOAD_DELIM_STRS 5
+/*
+ The following is the max table_map_id. This is limited by that we
+ are using 6 bytes for it in replication
+*/
+#define MAX_TABLE_MAP_ID ((1ULL << (6*8)) -1)
+
/*****************************************************************************
MySQL Binary Log
@@ -1379,7 +1387,8 @@ public:
static Log_event* read_log_event(IO_CACHE* file,
const Format_description_log_event
*description_event,
- my_bool crc_check);
+ my_bool crc_check,
+ my_bool print_errors= 1);
/**
Reads an event from a binlog or relay log. Used by the dump thread
@@ -1523,7 +1532,8 @@ public:
static Log_event* read_log_event(const uchar *buf, uint event_len,
const char **error,
const Format_description_log_event
- *description_event, my_bool crc_check);
+ *description_event, my_bool crc_check,
+ my_bool print_errors= 1);
/**
Returns the human readable name of the given event type.
*/
@@ -3338,33 +3348,27 @@ private:
@section User_var_log_event_binary_format Binary Format
*/
-class User_var_log_event: public Log_event
+
+class User_var_log_event: public Log_event, public Log_event_data_type
{
public:
- enum {
- UNDEF_F= 0,
- UNSIGNED_F= 1
- };
const char *name;
size_t name_len;
const char *val;
size_t val_len;
- Item_result type;
- uint charset_number;
bool is_null;
- uchar flags;
#ifdef MYSQL_SERVER
bool deferred;
query_id_t query_id;
User_var_log_event(THD* thd_arg, const char *name_arg, size_t name_len_arg,
const char *val_arg, size_t val_len_arg,
- Item_result type_arg,
- uint charset_number_arg, uchar flags_arg,
+ const Log_event_data_type &data_type,
bool using_trans, bool direct)
:Log_event(thd_arg, 0, using_trans),
+ Log_event_data_type(data_type),
name(name_arg), name_len(name_len_arg), val(val_arg),
- val_len(val_len_arg), type(type_arg), charset_number(charset_number_arg),
- flags(flags_arg), deferred(false)
+ val_len(val_len_arg),
+ deferred(false)
{
is_null= !val;
if (direct)
@@ -4865,7 +4869,8 @@ public:
flag_set get_flags(flag_set flag) const { return m_flags & flag; }
#ifdef MYSQL_SERVER
- Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, bool is_transactional);
+ Table_map_log_event(THD *thd, TABLE *tbl, ulonglong tid,
+ bool is_transactional);
#endif
#ifdef HAVE_REPLICATION
Table_map_log_event(const uchar *buf, uint event_len,
@@ -5191,7 +5196,7 @@ protected:
this class, not create instances of this class.
*/
#ifdef MYSQL_SERVER
- Rows_log_event(THD*, TABLE*, ulong table_id,
+ Rows_log_event(THD*, TABLE*, ulonglong table_id,
MY_BITMAP const *cols, bool is_transactional,
Log_event_type event_type);
#endif
@@ -5425,7 +5430,7 @@ public:
};
#if defined(MYSQL_SERVER)
- Write_rows_log_event(THD*, TABLE*, ulong table_id,
+ Write_rows_log_event(THD*, TABLE*, ulonglong table_id,
bool is_transactional);
#endif
#ifdef HAVE_REPLICATION
@@ -5466,7 +5471,7 @@ class Write_rows_compressed_log_event : public Write_rows_log_event
{
public:
#if defined(MYSQL_SERVER)
- Write_rows_compressed_log_event(THD*, TABLE*, ulong table_id,
+ Write_rows_compressed_log_event(THD*, TABLE*, ulonglong table_id,
bool is_transactional);
virtual bool write();
#endif
@@ -5502,7 +5507,7 @@ public:
};
#ifdef MYSQL_SERVER
- Update_rows_log_event(THD*, TABLE*, ulong table_id,
+ Update_rows_log_event(THD*, TABLE*, ulonglong table_id,
bool is_transactional);
void init(MY_BITMAP const *cols);
@@ -5554,7 +5559,7 @@ class Update_rows_compressed_log_event : public Update_rows_log_event
{
public:
#if defined(MYSQL_SERVER)
- Update_rows_compressed_log_event(THD*, TABLE*, ulong table_id,
+ Update_rows_compressed_log_event(THD*, TABLE*, ulonglong table_id,
bool is_transactional);
virtual bool write();
#endif
@@ -5598,7 +5603,7 @@ public:
};
#ifdef MYSQL_SERVER
- Delete_rows_log_event(THD*, TABLE*, ulong, bool is_transactional);
+ Delete_rows_log_event(THD*, TABLE*, ulonglong, bool is_transactional);
#endif
#ifdef HAVE_REPLICATION
Delete_rows_log_event(const uchar *buf, uint event_len,
@@ -5639,7 +5644,8 @@ class Delete_rows_compressed_log_event : public Delete_rows_log_event
{
public:
#if defined(MYSQL_SERVER)
- Delete_rows_compressed_log_event(THD*, TABLE*, ulong, bool is_transactional);
+ Delete_rows_compressed_log_event(THD*, TABLE*, ulonglong,
+ bool is_transactional);
virtual bool write();
#endif
#ifdef HAVE_REPLICATION
diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc
index 4ae8bffc..ddd62b08 100644
--- a/sql/log_event_client.cc
+++ b/sql/log_event_client.cc
@@ -1503,8 +1503,9 @@ bool Rows_log_event::print_verbose(IO_CACHE *file,
if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
!(td= map->create_table_def()))
{
- return (my_b_printf(file, "### Row event for unknown table #%lu",
- (ulong) m_table_id));
+ char llbuff[22];
+ return (my_b_printf(file, "### Row event for unknown table #%s",
+ ullstr(m_table_id, llbuff)));
}
/* If the write rows event contained no values for the AI */
@@ -2488,7 +2489,7 @@ bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
}
else
{
- switch (type) {
+ switch (m_type) {
case REAL_RESULT:
double real_val;
char real_buf[FMT_G_BUFSIZE(14)];
@@ -2500,8 +2501,7 @@ bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
break;
case INT_RESULT:
char int_buf[22];
- longlong10_to_str(uint8korr(val), int_buf,
- ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10));
+ longlong10_to_str(uint8korr(val), int_buf, is_unsigned() ? 10 : -10);
if (my_b_printf(&cache, ":=%s%s\n", int_buf,
print_event_info->delimiter))
goto err;
@@ -2556,7 +2556,7 @@ bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
people want to mysqlbinlog|mysql into another server not supporting the
character set. But there's not much to do about this and it's unlikely.
*/
- if (!(cs= get_charset(charset_number, MYF(0))))
+ if (!(cs= get_charset(m_charset_number, MYF(0))))
{ /*
Generate an unusable command (=> syntax error) is probably the best
thing we can do here.
diff --git a/sql/log_event_data_type.h b/sql/log_event_data_type.h
new file mode 100644
index 00000000..e3b2039a
--- /dev/null
+++ b/sql/log_event_data_type.h
@@ -0,0 +1,74 @@
+/* Copyright (c) 2024, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#ifndef LOG_EVENT_DATA_TYPE_H
+#define LOG_EVENT_DATA_TYPE_H
+
+class Log_event_data_type
+{
+public:
+
+ enum {
+ CHUNK_SIGNED= 0,
+ CHUNK_UNSIGNED= 1,
+ CHUNK_DATA_TYPE_NAME= 2
+ };
+
+protected:
+ LEX_CSTRING m_data_type_name;
+ Item_result m_type;
+ uint m_charset_number;
+ bool m_is_unsigned;
+
+public:
+
+ Log_event_data_type()
+ :m_data_type_name({NULL,0}),
+ m_type(STRING_RESULT),
+ m_charset_number(my_charset_bin.number),
+ m_is_unsigned(false)
+ { }
+
+ Log_event_data_type(const LEX_CSTRING &data_type_name_arg,
+ Item_result type_arg,
+ uint charset_number_arg,
+ bool is_unsigned_arg)
+ :m_data_type_name(data_type_name_arg),
+ m_type(type_arg),
+ m_charset_number(charset_number_arg),
+ m_is_unsigned(is_unsigned_arg)
+ { }
+
+ const LEX_CSTRING & data_type_name() const
+ {
+ return m_data_type_name;
+ }
+ Item_result type() const
+ {
+ return m_type;
+ }
+ uint charset_number() const
+ {
+ return m_charset_number;
+ }
+ bool is_unsigned() const
+ {
+ return m_is_unsigned;
+ }
+
+ bool unpack_optional_attributes(const char *str, const char *end);
+};
+
+#endif // LOG_EVENT_DATA_TYPE_H
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 19901035..c02c22f4 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -47,12 +47,12 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi)
const Relay_log_info *rli= rgi->rli;
/*
- If m_table_id == ~0UL, then we have a dummy event that does not
+ If m_table_id == UINT32_MAX, then we have a dummy event that does not
contain any data. In that case, we just remove all tables in the
tables_to_lock list, close the thread tables, and return with
success.
*/
- if (ev->m_table_id == ~0UL)
+ if (ev->m_table_id == UINT32_MAX)
{
/*
This one is supposed to be set: just an extra check so that
@@ -1123,13 +1123,14 @@ int Update_rows_log_event_old::do_exec_row(TABLE *table)
**************************************************************************/
#ifndef MYSQL_CLIENT
-Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
+Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulonglong table_id,
MY_BITMAP const *cols,
bool is_transactional)
: Log_event(thd_arg, 0, is_transactional),
m_row_count(0),
m_table(tbl_arg),
- m_table_id(tid),
+ m_table_id(table_id),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0)
#ifdef HAVE_REPLICATION
@@ -1142,12 +1143,12 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
/*
We allow a special form of dummy event when the table, and cols
- are null and the table id is ~0UL. This is a temporary
+ are null and the table id is UINT32_MAX. This is a temporary
solution, to be able to terminate a started statement in the
binary log: the extraneous events will be removed in the future.
*/
- DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
- (!tbl_arg && !cols && tid == ~0UL));
+ DBUG_ASSERT((tbl_arg && tbl_arg->s && table_id != UINT32_MAX) ||
+ (!tbl_arg && !cols && table_id == UINT32_MAX));
if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)
set_flags(NO_FOREIGN_KEY_CHECKS_F);
@@ -1208,7 +1209,7 @@ Old_rows_log_event::Old_rows_log_event(const uchar *buf, uint event_len,
}
else
{
- m_table_id= (ulong) uint6korr(post_start);
+ m_table_id= (ulonglong) uint6korr(post_start);
post_start+= RW_FLAGS_OFFSET;
}
@@ -1248,7 +1249,7 @@ Old_rows_log_event::Old_rows_log_event(const uchar *buf, uint event_len,
const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf);
- DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %zu",
+ DBUG_PRINT("info",("m_table_id: %llu m_flags: %d m_width: %lu data_size: %zu",
m_table_id, m_flags, m_width, data_size));
DBUG_DUMP("rows_data", (uchar*) ptr_rows_data, data_size);
@@ -1362,12 +1363,12 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
Relay_log_info const *rli= rgi->rli;
/*
- If m_table_id == ~0UL, then we have a dummy event that does not
+ If m_table_id == UINT32_MAX, then we have a dummy event that does not
contain any data. In that case, we just remove all tables in the
tables_to_lock list, close the thread tables, and return with
success.
*/
- if (m_table_id == ~0UL)
+ if (m_table_id == UINT32_MAX)
{
/*
This one is supposed to be set: just an extra check so that
@@ -1784,10 +1785,10 @@ bool Old_rows_log_event::write_data_header()
// This method should not be reached.
assert(0);
- DBUG_ASSERT(m_table_id != ~0UL);
+ DBUG_ASSERT(m_table_id != UINT32_MAX);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
- int4store(buf + 0, m_table_id);
+ int4store(buf + 0, (ulong) m_table_id);
int2store(buf + 4, m_flags);
return write_data(buf, 6);
});
@@ -1834,7 +1835,7 @@ void Old_rows_log_event::pack_info(Protocol *protocol)
char const *const flagstr=
get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
size_t bytes= my_snprintf(buf, sizeof(buf),
- "table_id: %lu%s", m_table_id, flagstr);
+ "table_id: %llu%s", m_table_id, flagstr);
protocol->store(buf, bytes, &my_charset_bin);
}
#endif
@@ -1856,9 +1857,10 @@ bool Old_rows_log_event::print_helper(FILE *file,
if (!print_event_info->short_form)
{
+ char llbuff[22];
if (print_header(head, print_event_info, !do_print_encoded) ||
- my_b_printf(head, "\t%s: table id %lu%s\n",
- name, m_table_id,
+ my_b_printf(head, "\t%s: table id %s%s\n",
+ name, ullstr(m_table_id, llbuff),
do_print_encoded ? " flags: STMT_END_F" : "") ||
print_base64(body, print_event_info, do_print_encoded))
goto err;
@@ -2398,7 +2400,7 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
#if !defined(MYSQL_CLIENT)
Write_rows_log_event_old::Write_rows_log_event_old(THD *thd_arg,
TABLE *tbl_arg,
- ulong tid_arg,
+ ulonglong tid_arg,
MY_BITMAP const *cols,
bool is_transactional)
: Old_rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
@@ -2510,7 +2512,7 @@ bool Write_rows_log_event_old::print(FILE *file,
#ifndef MYSQL_CLIENT
Delete_rows_log_event_old::Delete_rows_log_event_old(THD *thd_arg,
TABLE *tbl_arg,
- ulong tid,
+ ulonglong tid,
MY_BITMAP const *cols,
bool is_transactional)
: Old_rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional),
@@ -2618,7 +2620,7 @@ bool Delete_rows_log_event_old::print(FILE *file,
#if !defined(MYSQL_CLIENT)
Update_rows_log_event_old::Update_rows_log_event_old(THD *thd_arg,
TABLE *tbl_arg,
- ulong tid,
+ ulonglong tid,
MY_BITMAP const *cols,
bool is_transactional)
: Old_rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional),
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index e5aaacec..1afe9aba 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -131,7 +131,7 @@ public:
MY_BITMAP const *get_cols() const { return &m_cols; }
size_t get_width() const { return m_width; }
- ulong get_table_id() const { return m_table_id; }
+ ulonglong get_table_id() const { return m_table_id; }
#ifndef MYSQL_CLIENT
virtual bool write_data_header();
@@ -158,7 +158,7 @@ protected:
this class, not create instances of this class.
*/
#ifndef MYSQL_CLIENT
- Old_rows_log_event(THD*, TABLE*, ulong table_id,
+ Old_rows_log_event(THD*, TABLE*, ulonglong table_id,
MY_BITMAP const *cols, bool is_transactional);
#endif
Old_rows_log_event(const uchar *row_data, uint event_len,
@@ -176,7 +176,7 @@ protected:
#ifndef MYSQL_CLIENT
TABLE *m_table; /* The table the rows belong to */
#endif
- ulong m_table_id; /* Table ID */
+ ulonglong m_table_id; /* Table ID */
MY_BITMAP m_cols; /* Bitmap denoting columns available */
ulong m_width; /* The width of the columns bitmap */
@@ -359,7 +359,7 @@ class Write_rows_log_event_old : public Old_rows_log_event
/********** BEGIN CUT & PASTE FROM Write_rows_log_event **********/
public:
#if !defined(MYSQL_CLIENT)
- Write_rows_log_event_old(THD*, TABLE*, ulong table_id,
+ Write_rows_log_event_old(THD*, TABLE*, ulonglong table_id,
MY_BITMAP const *cols, bool is_transactional);
#endif
#ifdef HAVE_REPLICATION
@@ -430,7 +430,7 @@ class Update_rows_log_event_old : public Old_rows_log_event
/********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/
public:
#ifndef MYSQL_CLIENT
- Update_rows_log_event_old(THD*, TABLE*, ulong table_id,
+ Update_rows_log_event_old(THD*, TABLE*, ulonglong table_id,
MY_BITMAP const *cols,
bool is_transactional);
#endif
@@ -507,7 +507,7 @@ class Delete_rows_log_event_old : public Old_rows_log_event
/********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/
public:
#ifndef MYSQL_CLIENT
- Delete_rows_log_event_old(THD*, TABLE*, ulong,
+ Delete_rows_log_event_old(THD*, TABLE*, ulonglong,
MY_BITMAP const *cols, bool is_transactional);
#endif
#ifdef HAVE_REPLICATION
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index 5cb15c1c..84a00b5d 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -918,6 +918,10 @@ int Log_event_writer::write_header(uchar *pos, size_t len)
int Log_event_writer::write_data(const uchar *pos, size_t len)
{
DBUG_ENTER("Log_event_writer::write_data");
+
+ if (!len)
+ DBUG_RETURN(0);
+
if (checksum_len)
crc= my_checksum(crc, pos, len);
@@ -4561,11 +4565,16 @@ bool XA_prepare_log_event::write()
#if defined(HAVE_REPLICATION)
static bool
user_var_append_name_part(THD *thd, String *buf,
- const char *name, size_t name_len)
+ const char *name, size_t name_len,
+ const LEX_CSTRING &data_type_name)
{
return buf->append('@') ||
append_identifier(thd, buf, name, name_len) ||
- buf->append('=');
+ buf->append('=') ||
+ (data_type_name.length &&
+ (buf->append(STRING_WITH_LEN("/*")) ||
+ buf->append(data_type_name.str, data_type_name.length) ||
+ buf->append(STRING_WITH_LEN("*/"))));
}
void User_var_log_event::pack_info(Protocol* protocol)
@@ -4575,14 +4584,15 @@ void User_var_log_event::pack_info(Protocol* protocol)
char buf_mem[FN_REFLEN+7];
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
buf.length(0);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len,
+ m_data_type_name) ||
buf.append(NULL_clex_str))
return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
}
else
{
- switch (type) {
+ switch (m_type) {
case REAL_RESULT:
{
double real_val;
@@ -4591,7 +4601,8 @@ void User_var_log_event::pack_info(Protocol* protocol)
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
float8get(real_val, val);
buf.length(0);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len,
+ m_data_type_name) ||
buf.append(buf2, my_gcvt(real_val, MY_GCVT_ARG_DOUBLE,
MY_GCVT_MAX_FIELD_WIDTH, buf2, NULL)))
return;
@@ -4604,10 +4615,11 @@ void User_var_log_event::pack_info(Protocol* protocol)
char buf_mem[FN_REFLEN + 22];
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
buf.length(0);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len,
+ m_data_type_name) ||
buf.append(buf2,
longlong10_to_str(uint8korr(val), buf2,
- ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10))-buf2))
+ (is_unsigned() ? 10 : -10))-buf2))
return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
@@ -4620,7 +4632,8 @@ void User_var_log_event::pack_info(Protocol* protocol)
String str(buf2, sizeof(buf2), &my_charset_bin);
buf.length(0);
my_decimal((const uchar *) (val + 2), val[0], val[1]).to_string(&str);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len,
+ m_data_type_name) ||
buf.append(str))
return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
@@ -4636,7 +4649,7 @@ void User_var_log_event::pack_info(Protocol* protocol)
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
CHARSET_INFO *cs;
buf.length(0);
- if (!(cs= get_charset(charset_number, MYF(0))))
+ if (!(cs= get_charset(m_charset_number, MYF(0))))
{
if (buf.append(STRING_WITH_LEN("???")))
return;
@@ -4645,7 +4658,8 @@ void User_var_log_event::pack_info(Protocol* protocol)
{
size_t old_len;
char *beg, *end;
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len,
+ m_data_type_name) ||
buf.append('_') ||
buf.append(cs->cs_name) ||
buf.append(' '))
@@ -4693,10 +4707,10 @@ bool User_var_log_event::write()
}
else
{
- buf1[1]= type;
- int4store(buf1 + 2, charset_number);
+ buf1[1]= m_type;
+ int4store(buf1 + 2, m_charset_number);
- switch (type) {
+ switch (m_type) {
case REAL_RESULT:
float8store(buf2, *(double*) val);
break;
@@ -4726,15 +4740,28 @@ bool User_var_log_event::write()
buf1_length= 10;
}
+ uchar data_type_name_chunk_signature= (uchar) CHUNK_DATA_TYPE_NAME;
+ uint data_type_name_chunk_signature_length= m_data_type_name.length ? 1 : 0;
+ uchar data_type_name_length_length= m_data_type_name.length ? 1 : 0;
+
/* Length of the whole event */
- event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len;
+ event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len +
+ data_type_name_chunk_signature_length +
+ data_type_name_length_length +
+ (uint) m_data_type_name.length;
+ uchar unsig= m_is_unsigned ? CHUNK_UNSIGNED : CHUNK_SIGNED;
+ uchar data_type_name_length= (uchar) m_data_type_name.length;
return write_header(event_length) ||
write_data(buf, sizeof(buf)) ||
write_data(name, name_len) ||
write_data(buf1, buf1_length) ||
write_data(pos, val_len) ||
- write_data(&flags, unsigned_len) ||
+ write_data(&unsig, unsigned_len) ||
+ write_data(&data_type_name_chunk_signature,
+ data_type_name_chunk_signature_length) ||
+ write_data(&data_type_name_length, data_type_name_length_length) ||
+ write_data(m_data_type_name.str, (uint) m_data_type_name.length) ||
write_footer();
}
@@ -4758,7 +4785,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
current_thd->query_id= query_id; /* recreating original time context */
}
- if (!(charset= get_charset(charset_number, MYF(MY_WME))))
+ if (!(charset= get_charset(m_charset_number, MYF(MY_WME))))
{
rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
@@ -4777,7 +4804,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
}
else
{
- switch (type) {
+ switch (m_type) {
case REAL_RESULT:
if (val_len != 8)
{
@@ -4841,13 +4868,10 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
if (e->fix_fields(thd, 0))
DBUG_RETURN(1);
- /*
- A variable can just be considered as a table with
- a single record and with a single column. Thus, like
- a column value, it could always have IMPLICIT derivation.
- */
- e->update_hash((void*) val, val_len, type, charset,
- (flags & User_var_log_event::UNSIGNED_F));
+ const Type_handler *th= Type_handler::handler_by_log_event_data_type(thd,
+ *this);
+ e->update_hash((void*) val, val_len, th, charset);
+
if (!is_deferred())
free_root(thd->mem_root, 0);
else
@@ -5575,13 +5599,14 @@ bool sql_ex_info::write_data(Log_event_writer *writer)
Rows_log_event member functions
**************************************************************************/
-Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
+Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulonglong table_id,
MY_BITMAP const *cols, bool is_transactional,
Log_event_type event_type)
: Log_event(thd_arg, 0, is_transactional),
m_row_count(0),
m_table(tbl_arg),
- m_table_id(tid),
+ m_table_id(table_id),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0),
m_type(event_type), m_extra_row_data(0)
@@ -5593,12 +5618,13 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
{
/*
We allow a special form of dummy event when the table, and cols
- are null and the table id is ~0UL. This is a temporary
+ are null and the table id is UINT32_MAX. This is a temporary
solution, to be able to terminate a started statement in the
binary log: the extraneous events will be removed in the future.
*/
- DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
- (!tbl_arg && !cols && tid == ~0UL));
+ DBUG_ASSERT((tbl_arg && tbl_arg->s &&
+ (table_id & MAX_TABLE_MAP_ID) != UINT32_MAX) ||
+ (!tbl_arg && !cols && (table_id & MAX_TABLE_MAP_ID) == UINT32_MAX));
if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)
set_flags(NO_FOREIGN_KEY_CHECKS_F);
@@ -5745,12 +5771,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
LEX *lex= thd->lex;
uint8 new_trg_event_map= get_trg_event_map();
/*
- If m_table_id == ~0ULL, then we have a dummy event that does not
+ If m_table_id == UINT32_MAX, then we have a dummy event that does not
contain any data. In that case, we just remove all tables in the
tables_to_lock list, close the thread tables, and return with
success.
*/
- if (m_table_id == ~0ULL)
+ if (m_table_id == UINT32_MAX)
{
/*
This one is supposed to be set: just an extra check so that
@@ -6412,10 +6438,10 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi)
bool Rows_log_event::write_data_header()
{
uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer
- DBUG_ASSERT(m_table_id != ~0ULL);
+ DBUG_ASSERT(m_table_id != UINT32_MAX);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
- int4store(buf + 0, m_table_id);
+ int4store(buf + 0, (ulong) m_table_id);
int2store(buf + 4, m_flags);
return (write_data(buf, 6));
});
@@ -6620,7 +6646,7 @@ int Table_map_log_event::save_field_metadata()
Mats says tbl->s lives longer than this event so it's ok to copy pointers
(tbl->s->db etc) and not pointer content.
*/
-Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
+Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulonglong tid,
bool is_transactional)
: Log_event(thd, 0, is_transactional),
m_table(tbl),
@@ -6643,7 +6669,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
uchar cbuf[MAX_INT_WIDTH];
uchar *cbuf_end;
DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
- DBUG_ASSERT(m_table_id != ~0ULL);
+ DBUG_ASSERT(m_table_id != UINT32_MAX);
/*
In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
table.cc / alloc_table_share():
@@ -6929,7 +6955,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
char buf[256];
my_snprintf(buf, sizeof(buf),
- "Found table map event mapping table id %u which "
+ "Found table map event mapping table id %llu which "
"was already mapped but with different settings.",
table_list->table_id);
@@ -6970,11 +6996,11 @@ int Table_map_log_event::do_update_pos(rpl_group_info *rgi)
bool Table_map_log_event::write_data_header()
{
- DBUG_ASSERT(m_table_id != ~0ULL);
+ DBUG_ASSERT(m_table_id != UINT32_MAX);
uchar buf[TABLE_MAP_HEADER_LEN];
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
- int4store(buf + 0, m_table_id);
+ int4store(buf + 0, (ulong) m_table_id);
int2store(buf + 4, m_flags);
return (write_data(buf, 6));
});
@@ -7410,7 +7436,7 @@ void Table_map_log_event::pack_info(Protocol *protocol)
{
char buf[256];
size_t bytes= my_snprintf(buf, sizeof(buf),
- "table_id: %llu (%s.%s)",
+ "table_id: %llu (%s.%s)",
m_table_id, m_dbnam, m_tblnam);
protocol->store(buf, bytes, &my_charset_bin);
}
@@ -7425,7 +7451,7 @@ void Table_map_log_event::pack_info(Protocol *protocol)
Constructor used to build an event for writing to the binary log.
*/
Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid_arg,
+ ulonglong tid_arg,
bool is_transactional)
:Rows_log_event(thd_arg, tbl_arg, tid_arg, tbl_arg->rpl_write_set,
is_transactional, WRITE_ROWS_EVENT_V1)
@@ -7435,7 +7461,7 @@ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Write_rows_compressed_log_event::Write_rows_compressed_log_event(
THD *thd_arg,
TABLE *tbl_arg,
- ulong tid_arg,
+ ulonglong tid_arg,
bool is_transactional)
: Write_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional)
{
@@ -7521,7 +7547,7 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
indexed and it cannot have a DEFAULT value).
*/
m_table->auto_increment_field_not_null= FALSE;
- m_table->mark_auto_increment_column();
+ m_table->mark_auto_increment_column(true);
}
return error;
@@ -8554,7 +8580,8 @@ end:
*/
Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid, bool is_transactional)
+ ulonglong tid,
+ bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional,
DELETE_ROWS_EVENT_V1)
{
@@ -8562,7 +8589,7 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Delete_rows_compressed_log_event::Delete_rows_compressed_log_event(
THD *thd_arg, TABLE *tbl_arg,
- ulong tid_arg,
+ ulonglong tid_arg,
bool is_transactional)
: Delete_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional)
{
@@ -8702,7 +8729,7 @@ uint8 Delete_rows_log_event::get_trg_event_map()
Constructor used to build an event for writing to the binary log.
*/
Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid,
+ ulonglong tid,
bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional,
UPDATE_ROWS_EVENT_V1)
@@ -8710,9 +8737,9 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
init(tbl_arg->rpl_write_set);
}
-Update_rows_compressed_log_event::Update_rows_compressed_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid,
- bool is_transactional)
+Update_rows_compressed_log_event::
+Update_rows_compressed_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulonglong tid, bool is_transactional)
: Update_rows_log_event(thd_arg, tbl_arg, tid, is_transactional)
{
m_type = UPDATE_ROWS_COMPRESSED_EVENT_V1;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 95286923..738fd73e 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -5221,6 +5221,14 @@ static int init_server_components()
if (!opt_abort && ddl_log_initialize())
unireg_abort(1);
+ /*
+ Plugins may not be completed because system table DDLs are only
+ run after the ddl recovery done. Therefore between the
+ plugin_init() call and the ha_signal_ddl_recovery_done() call
+ below only things related to preparation for recovery should be
+ done and nothing else, and definitely not anything assuming that
+ all plugins have been initialised.
+ */
if (plugin_init(&remaining_argc, remaining_argv,
(opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) |
(opt_abort ? PLUGIN_INIT_SKIP_INITIALIZATION : 0)))
@@ -5544,6 +5552,15 @@ static int init_server_components()
#else
locked_in_memory= 0;
#endif
+#ifdef PR_SET_THP_DISABLE
+ /*
+ Engine page buffers are now allocated.
+ Disable transparent huge pages for all
+ future allocations as these causes memory
+ leaks.
+ */
+ prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0);
+#endif
ft_init_stopwords();
@@ -5791,8 +5808,6 @@ int mysqld_main(int argc, char **argv)
SYSVAR_AUTOSIZE(my_thread_stack_size, new_thread_stack_size);
}
- (void) thr_setconcurrency(concurrency); // 10 by default
-
select_thread=pthread_self();
select_thread_in_use=1;
@@ -6867,8 +6882,8 @@ struct my_option my_long_options[]=
#endif
};
-static int show_queries(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_queries(THD *thd, SHOW_VAR *var, void *,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONGLONG;
var->value= &thd->query_id;
@@ -6876,16 +6891,16 @@ static int show_queries(THD *thd, SHOW_VAR *var, char *buff,
}
-static int show_net_compression(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_net_compression(THD *thd, SHOW_VAR *var, void *,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_MY_BOOL;
var->value= &thd->net.compress;
return 0;
}
-static int show_starttime(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_starttime(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -6894,8 +6909,8 @@ static int show_starttime(THD *thd, SHOW_VAR *var, char *buff,
}
#ifdef ENABLED_PROFILING
-static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_flushstatustime(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -6905,32 +6920,28 @@ static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff,
#endif
#ifdef HAVE_REPLICATION
-static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_rpl_status(THD *, SHOW_VAR *var, void *, system_status_var *,
+ enum_var_type)
{
var->type= SHOW_CHAR;
var->value= const_cast<char*>(rpl_status_type[(int)rpl_status]);
return 0;
}
-static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_slave_running(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
- Master_info *mi= NULL;
- bool UNINIT_VAR(tmp);
-
- var->type= SHOW_MY_BOOL;
- var->value= buff;
-
- if ((mi= get_master_info(&thd->variables.default_master_connection,
- Sql_condition::WARN_LEVEL_NOTE)))
+ if (Master_info *mi=
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_NOTE))
{
- tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING &&
- mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN);
+ *((my_bool*) buff)=
+ (mi->slave_running == MYSQL_SLAVE_RUN_READING &&
+ mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN);
mi->release();
+ var->type= SHOW_MY_BOOL;
+ var->value= buff;
}
- if (mi)
- *((my_bool *)buff)= tmp;
else
var->type= SHOW_UNDEF;
return 0;
@@ -6940,7 +6951,8 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff,
/* How many masters this slave is connected to */
-static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
+static int show_slaves_running(THD *, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONGLONG;
var->value= buff;
@@ -6951,19 +6963,17 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
}
-static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
- Master_info *mi;
-
- var->type= SHOW_LONGLONG;
- var->value= buff;
-
- if ((mi= get_master_info(&thd->variables.default_master_connection,
- Sql_condition::WARN_LEVEL_NOTE)))
+ if (Master_info *mi=
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_NOTE))
{
*((longlong *)buff)= mi->received_heartbeats;
mi->release();
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
}
else
var->type= SHOW_UNDEF;
@@ -6971,19 +6981,17 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff,
}
-static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_heartbeat_period(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
- Master_info *mi;
-
- var->type= SHOW_CHAR;
- var->value= buff;
-
- if ((mi= get_master_info(&thd->variables.default_master_connection,
- Sql_condition::WARN_LEVEL_NOTE)))
+ if (Master_info *mi=
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_NOTE))
{
- sprintf(buff, "%.3f", mi->heartbeat_period);
+ sprintf(static_cast<char*>(buff), "%.3f", mi->heartbeat_period);
mi->release();
+ var->type= SHOW_CHAR;
+ var->value= buff;
}
else
var->type= SHOW_UNDEF;
@@ -6993,8 +7001,8 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff,
#endif /* HAVE_REPLICATION */
-static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_open_tables(THD *, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -7002,8 +7010,8 @@ static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_prepared_stmt_count(THD *, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -7013,8 +7021,8 @@ static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_table_definitions(THD *, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -7033,8 +7041,8 @@ static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff,
inside an Event.
*/
-static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_ssl_get_version(THD *thd, SHOW_VAR *var, void *,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_CHAR;
if( thd->vio_ok() && thd->net.vio->ssl_arg )
@@ -7044,8 +7052,8 @@ static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -7056,8 +7064,8 @@ static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -7072,8 +7080,8 @@ static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_LONG;
var->value= buff;
@@ -7085,8 +7093,8 @@ static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_CHAR;
if( thd->vio_ok() && thd->net.vio->ssl_arg )
@@ -7096,9 +7104,10 @@ static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, void *buf,
+ system_status_var *, enum_var_type)
{
+ char *buff= static_cast<char*>(buf);
var->type= SHOW_CHAR;
var->value= buff;
if (thd->vio_ok() && thd->net.vio->ssl_arg)
@@ -7183,8 +7192,8 @@ end:
*/
static int
-show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_CHAR;
if(thd->vio_ok() && thd->net.vio->ssl_arg)
@@ -7193,7 +7202,7 @@ show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff,
X509 *cert= SSL_get_certificate(ssl);
const ASN1_TIME *not_before= X509_get0_notBefore(cert);
- var->value= my_asn1_time_to_string(not_before, buff,
+ var->value= my_asn1_time_to_string(not_before, static_cast<char*>(buff),
SHOW_VAR_FUNC_BUFF_SIZE);
if (!var->value)
return 1;
@@ -7217,8 +7226,8 @@ show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff,
*/
static int
-show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_CHAR;
if(thd->vio_ok() && thd->net.vio->ssl_arg)
@@ -7227,7 +7236,7 @@ show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff,
X509 *cert= SSL_get_certificate(ssl);
const ASN1_TIME *not_after= X509_get0_notAfter(cert);
- var->value= my_asn1_time_to_string(not_after, buff,
+ var->value= my_asn1_time_to_string(not_after, static_cast<char*>(buff),
SHOW_VAR_FUNC_BUFF_SIZE);
if (!var->value)
return 1;
@@ -7281,7 +7290,7 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, void *buff,
}
-static int show_memory_used(THD *thd, SHOW_VAR *var, char *buff,
+static int show_memory_used(THD *thd, SHOW_VAR *var, void *buff,
struct system_status_var *status_var,
enum enum_var_type scope)
{
@@ -7337,8 +7346,8 @@ static int debug_status_func(THD *thd, SHOW_VAR *var, void *buff,
#endif
#ifdef HAVE_POOL_OF_THREADS
-static int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_threadpool_idle_threads(THD *, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_INT;
var->value= buff;
@@ -7347,8 +7356,8 @@ static int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff,
}
-static int show_threadpool_threads(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_threadpool_threads(THD *, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_INT;
var->value= buff;
@@ -7500,7 +7509,7 @@ SHOW_VAR status_vars[]= {
SHOW_FUNC_ENTRY("Rpl_semi_sync_master_net_avg_wait_time", &SHOW_FNAME(avg_net_wait_time)),
{"Rpl_semi_sync_master_request_ack", (char*) &rpl_semi_sync_master_request_ack, SHOW_LONGLONG},
{"Rpl_semi_sync_master_get_ack", (char*)&rpl_semi_sync_master_get_ack, SHOW_LONGLONG},
- {"Rpl_semi_sync_slave_status", (char*) &rpl_semi_sync_slave_status, SHOW_BOOL},
+ SHOW_FUNC_ENTRY("Rpl_semi_sync_slave_status", &rpl_semi_sync_enabled),
{"Rpl_semi_sync_slave_send_ack", (char*) &rpl_semi_sync_slave_send_ack, SHOW_LONGLONG},
#endif /* HAVE_REPLICATION */
#ifdef HAVE_QUERY_CACHE
@@ -8082,6 +8091,9 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument,
test_flags= argument ? ((uint) atoi(argument) & ~TEST_BLOCKING) : 0;
opt_endinfo=1;
break;
+ case OPT_SECURE_AUTH:
+ WARN_DEPRECATED_NO_REPLACEMENT(NULL, "--secure-auth");
+ break;
case OPT_THREAD_CONCURRENCY:
WARN_DEPRECATED_NO_REPLACEMENT(NULL, "THREAD_CONCURRENCY");
break;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 2139b9b6..13b824d1 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -842,7 +842,7 @@ enum options_mysqld
OPT_WSREP_SYNC_WAIT,
#endif /* WITH_WSREP */
OPT_MYSQL_COMPATIBILITY,
- OPT_TLS_VERSION,
+ OPT_TLS_VERSION, OPT_SECURE_AUTH,
OPT_MYSQL_TO_BE_IMPLEMENTED,
OPT_SEQURE_FILE_PRIV,
OPT_which_is_always_the_last
@@ -884,6 +884,11 @@ enum enum_query_type
/// good for parsing
QT_PARSABLE= (1 << 8),
+ // If an expression is constant, print the expression, not the value
+ // it evaluates to. Should be used for error messages, so that they
+ // don't reveal values.
+ QT_NO_DATA_EXPANSION= (1 << 9),
+
/// This value means focus on readability, not on ability to parse back, etc.
QT_EXPLAIN= QT_TO_SYSTEM_CHARSET |
QT_ITEM_IDENT_SKIP_DB_NAMES |
@@ -904,12 +909,12 @@ enum enum_query_type
QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET|
QT_SHOW_SELECT_NUMBER,
- // If an expression is constant, print the expression, not the value
- // it evaluates to. Should be used for error messages, so that they
- // don't reveal values.
- QT_NO_DATA_EXPANSION= (1 << 9),
// Remove wrappers added for TVC when creating or showing view
- QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 12)
+ QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 12),
+
+ /// Print for FRM file. Focus on parse-back.
+ /// e.g. VIEW expressions and virtual column expressions
+ QT_FOR_FRM= (1 << 13)
};
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 70e71d9a..3dff8442 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -156,6 +156,7 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
net->where_b = net->remain_in_buf=0;
net->net_skip_rest_factor= 0;
net->last_errno=0;
+ net->pkt_nr_can_be_reset= 0;
net->thread_specific_malloc= MY_TEST(my_flags & MY_THREAD_SPECIFIC);
net->thd= 0;
#ifdef MYSQL_SERVER
@@ -1057,8 +1058,10 @@ retry:
{ /* Probably in MIT threads */
if (retry_count++ < net->retry_count)
continue;
- EXTRA_DEBUG_fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
- my_progname,vio_errno(net->vio));
+ EXTRA_DEBUG_fprintf(stderr, "%s: read looped with error %d on "
+ "file %lld, aborting thread\n",
+ my_progname, vio_errno(net->vio),
+ (longlong) vio_fd(net->vio));
}
#ifndef MYSQL_SERVER
if (length != 0 && vio_errno(net->vio) == SOCKET_EINTR)
@@ -1094,19 +1097,31 @@ retry:
#endif
if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)
{
-#ifndef MYSQL_SERVER
- if (net->buff[net->where_b + 3] == (uchar) (net->pkt_nr -1))
+ if (net->pkt_nr_can_be_reset)
{
/*
- If the server was killed then the server may have missed the
- last sent client packet and the packet numbering may be one off.
+ We are using a protocol like semi-sync where master and slave
+ sends packets in parallel.
+ Copy current one as it can be useful for debugging.
*/
- DBUG_PRINT("warning", ("Found possible out of order packets"));
- expect_error_packet= 1;
+ net->pkt_nr= net->buff[net->where_b + 3];
}
else
+ {
+#ifndef MYSQL_SERVER
+ if (net->buff[net->where_b + 3] == (uchar) (net->pkt_nr -1))
+ {
+ /*
+ If the server was killed then the server may have missed the
+ last sent client packet and the packet numbering may be one off.
+ */
+ DBUG_PRINT("warning", ("Found possible out of order packets"));
+ expect_error_packet= 1;
+ }
+ else
#endif
- goto packets_out_of_order;
+ goto packets_out_of_order;
+ }
}
net->compress_pkt_nr= ++net->pkt_nr;
#ifdef HAVE_COMPRESS
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 0ca8402f..42afc930 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -2692,7 +2692,7 @@ SQL_SELECT::test_quick_select(THD *thd,
bool ordered_output,
bool remove_false_parts_of_where,
bool only_single_index_range_scan,
- bool suppress_unusable_key_notes)
+ Item_func::Bitmap note_unusable_keys)
{
uint idx;
double scan_time;
@@ -2786,9 +2786,9 @@ SQL_SELECT::test_quick_select(THD *thd,
param.max_key_parts= 0;
param.remove_false_where_parts= remove_false_parts_of_where;
param.force_default_mrr= ordered_output;
- param.note_unusable_keys= (!suppress_unusable_key_notes &&
- thd->give_notes_for_unusable_keys());
-
+ param.note_unusable_keys= thd->give_notes_for_unusable_keys() ?
+ note_unusable_keys :
+ Item_func::BITMAP_NONE;
param.possible_keys.clear_all();
thd->no_errors=1; // Don't warn about NULL
@@ -3999,7 +3999,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
range_par->remove_jump_scans= FALSE;
range_par->real_keynr[0]= 0;
range_par->alloced_sel_args= 0;
- range_par->note_unusable_keys= 0;
+ range_par->note_unusable_keys= Item_func::BITMAP_NONE;
thd->no_errors=1; // Don't warn about NULL
thd->mem_root=&alloc;
@@ -8762,9 +8762,11 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
if (field->result_type() == STRING_RESULT &&
field->charset() != compare_collation())
{
- if (param->note_unusable_keys)
+ if (param->note_unusable_keys & BITMAP_LIKE)
field->raise_note_cannot_use_key_part(param->thd, keynr, key_part->part,
- func_name_cstring(), value,
+ func_name_cstring(),
+ compare_collation(),
+ value,
Data_type_compatibility::
INCOMPATIBLE_COLLATION);
DBUG_RETURN(0);
@@ -8780,9 +8782,11 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
field->type_handler() == &type_handler_enum ||
field->type_handler() == &type_handler_set)
{
- if (param->note_unusable_keys)
+ if (param->note_unusable_keys & BITMAP_LIKE)
field->raise_note_cannot_use_key_part(param->thd, keynr, key_part->part,
- func_name_cstring(), value,
+ func_name_cstring(),
+ compare_collation(),
+ value,
Data_type_compatibility::
INCOMPATIBLE_DATA_TYPE);
DBUG_RETURN(0);
@@ -8887,7 +8891,8 @@ Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param,
TODO: Perhaps we also need to raise a similar note when
a partition could not be used (when using_real_indexes==false).
*/
- if (param->using_real_indexes && param->note_unusable_keys)
+ if (param->using_real_indexes && param->note_unusable_keys &&
+ (param->note_unusable_keys & cond->bitmap_bit()))
{
DBUG_ASSERT(keynr < table->s->keys);
/*
@@ -8901,6 +8906,7 @@ Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param,
*/
raise_note_cannot_use_key_part(param->thd, keynr, key_part->part,
scalar_comparison_op_to_lex_cstring(op),
+ cond->compare_collation(),
value, compat);
}
return compat;
@@ -15173,13 +15179,6 @@ int QUICK_GROUP_MIN_MAX_SELECT::init()
{
if (group_prefix) /* Already initialized. */
return 0;
-
- /*
- We allocate one byte more to serve the case when the last field in
- the buffer is compared using uint3korr (e.g. a Field_newdate field)
- */
- if (!(last_prefix= (uchar*) alloc_root(&alloc, group_prefix_len+1)))
- return 1;
/*
We may use group_prefix to store keys with all select fields, so allocate
enough space for it.
@@ -15436,8 +15435,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
QUICK_GROUP_MIN_MAX_SELECT::reset()
DESCRIPTION
- Initialize the index chosen for access and find and store the prefix
- of the last group. The method is expensive since it performs disk access.
+ Initialize the index chosen for access.
RETURN
0 OK
@@ -15459,12 +15457,6 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void)
}
if (quick_prefix_select && quick_prefix_select->reset())
DBUG_RETURN(1);
- result= file->ha_index_last(record);
- if (result == HA_ERR_END_OF_FILE)
- DBUG_RETURN(0);
- /* Save the prefix of the last group. */
- key_copy(last_prefix, record, index_info, group_prefix_len);
-
DBUG_RETURN(0);
}
@@ -15510,34 +15502,20 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next()
#else
int result;
#endif
- int is_last_prefix= 0;
-
DBUG_ENTER("QUICK_GROUP_MIN_MAX_SELECT::get_next");
/*
- Loop until a group is found that satisfies all query conditions or the last
- group is reached.
+ Loop until a group is found that satisfies all query conditions or
+ there are no satisfying groups left
*/
do
{
result= next_prefix();
+ if (result != 0)
+ break;
/*
- Check if this is the last group prefix. Notice that at this point
- this->record contains the current prefix in record format.
+ At this point this->record contains the current prefix in record format.
*/
- if (!result)
- {
- is_last_prefix= key_cmp(index_info->key_part, last_prefix,
- group_prefix_len);
- DBUG_ASSERT(is_last_prefix <= 0);
- }
- else
- {
- if (result == HA_ERR_KEY_NOT_FOUND)
- continue;
- break;
- }
-
if (have_min)
{
min_res= next_min();
@@ -15566,8 +15544,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next()
HA_READ_KEY_EXACT);
result= have_min ? min_res : have_max ? max_res : result;
- } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
- is_last_prefix != 0);
+ } while (result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE);
if (result == HA_ERR_KEY_NOT_FOUND)
result= HA_ERR_END_OF_FILE;
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 4f766534..25bb9f0a 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -889,7 +889,10 @@ public:
*/
bool remove_false_where_parts;
- bool note_unusable_keys; // Give SQL notes for unusable keys
+ /*
+ Which functions should give SQL notes for unusable keys.
+ */
+ Item_func::Bitmap note_unusable_keys;
/*
used_key_no -> table_key_no translation table. Only makes sense if
@@ -1768,7 +1771,6 @@ private:
uchar *group_prefix; /* Key prefix consisting of the GROUP fields. */
const uint group_prefix_len; /* Length of the group prefix. */
uint group_key_parts; /* A number of keyparts in the group prefix */
- uchar *last_prefix; /* Prefix of the last group for detecting EOF. */
bool have_min; /* Specify whether we are computing */
bool have_max; /* a MIN, a MAX, or both. */
bool have_agg_distinct;/* aggregate_function(DISTINCT ...). */
@@ -1894,12 +1896,14 @@ class SQL_SELECT :public Sql_alloc {
true - for ERROR and IMPOSSIBLE_RANGE
false - Ok
*/
- bool check_quick(THD *thd, bool force_quick_range, ha_rows limit)
+ bool check_quick(THD *thd, bool force_quick_range, ha_rows limit,
+ Item_func::Bitmap note_unusable_keys)
{
key_map tmp;
tmp.set_all();
return test_quick_select(thd, tmp, 0, limit, force_quick_range,
- FALSE, FALSE, FALSE) != OK;
+ FALSE, FALSE, FALSE,
+ note_unusable_keys) != OK;
}
/*
@@ -1929,7 +1933,7 @@ class SQL_SELECT :public Sql_alloc {
bool ordered_output,
bool remove_false_parts_of_where,
bool only_single_index_range_scan,
- bool suppress_unusable_key_notes = 0);
+ Item_func::Bitmap note_unusable_keys);
};
typedef enum SQL_SELECT::quick_select_return_type quick_select_return;
diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc
index 4bc49394..972c7da6 100644
--- a/sql/opt_trace.cc
+++ b/sql/opt_trace.cc
@@ -103,7 +103,8 @@ inline bool sql_command_can_be_traced(enum enum_sql_command sql_command)
sql_command == SQLCOM_UPDATE ||
sql_command == SQLCOM_DELETE ||
sql_command == SQLCOM_DELETE_MULTI ||
- sql_command == SQLCOM_UPDATE_MULTI;
+ sql_command == SQLCOM_UPDATE_MULTI ||
+ sql_command == SQLCOM_INSERT_SELECT;
}
void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 6667129d..d2ef52e0 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -414,7 +414,6 @@ static bool write_eof_packet(THD *thd, NET *net,
bool Protocol::net_send_error_packet(THD *thd, uint sql_errno, const char *err,
const char* sqlstate)
-
{
NET *net= &thd->net;
uint length;
diff --git a/sql/protocol.h b/sql/protocol.h
index 4fdfde3e..09dbdfbd 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -229,9 +229,9 @@ public:
#ifdef EMBEDDED_LIBRARY
void remove_last_row() override;
#endif
- virtual bool store_field_metadata(const THD *thd, const Send_field &field,
- CHARSET_INFO *charset_for_protocol,
- uint pos);
+ bool store_field_metadata(const THD *thd, const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos);
bool store_item_metadata(THD *thd, Item *item, uint pos);
bool store_field_metadata_for_list_fields(const THD *thd, Field *field,
const TABLE_LIST *table_list,
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index 11629059..20188d6c 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -721,6 +721,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
if (WSREP_ON_ && wsrep_thd_is_local(thd))
{
thd->wsrep_ignore_table= false;
+ table->file->row_logging= 1; // replication requires binary logging
wsrep_start_trx_if_not_started(thd);
}
else
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 3c698f27..4fc204dd 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -43,7 +43,8 @@ Master_info::Master_info(LEX_CSTRING *connection_name_arg,
gtid_reconnect_event_skip_count(0), gtid_event_seen(false),
in_start_all_slaves(0), in_stop_all_slaves(0), in_flush_all_relay_logs(0),
users(0), killed(0),
- total_ddl_groups(0), total_non_trans_groups(0), total_trans_groups(0)
+ total_ddl_groups(0), total_non_trans_groups(0), total_trans_groups(0),
+ semi_sync_reply_enabled(0)
{
char *tmp;
host[0] = 0; user[0] = 0; password[0] = 0;
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 6058b7fb..159e099f 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -210,6 +210,16 @@ class Master_info : public Slave_reporting_capability
void lock_slave_threads();
void unlock_slave_threads();
+ ulonglong get_slave_skip_counter()
+ {
+ return rli.slave_skip_counter;
+ }
+
+ ulonglong get_max_relay_log_size()
+ {
+ return rli.max_relay_log_size;
+ }
+
/* the variables below are needed because we can change masters on the fly */
char master_log_name[FN_REFLEN+6]; /* Room for multi-*/
char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
@@ -366,6 +376,12 @@ class Master_info : public Slave_reporting_capability
it must be ignored similarly to the replicate-same-server-id rule.
*/
bool do_accept_own_server_id= false;
+ /*
+ Set to 1 when semi_sync is enabled. Set to 0 if there is any transmit
+ problems to the slave, in which case any furter semi-sync reply is
+ ignored
+ */
+ bool semi_sync_reply_enabled;
List <start_alter_info> start_alter_list;
MEM_ROOT mem_root;
/*
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 95a1234e..333a3960 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -895,8 +895,7 @@ do_retry:
thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
DBUG_EXECUTE_IF("inject_mdev8031", {
/* Simulate that we get deadlock killed at this exact point. */
- rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED;
- thd->set_killed(KILL_CONNECTION);
+ slave_background_kill_request(thd);
});
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("rpl_parallel_simulate_wait_at_retry", {
@@ -2877,23 +2876,12 @@ rpl_parallel::stop_during_until()
bool
-rpl_parallel::workers_idle()
+rpl_parallel::workers_idle(Relay_log_info *rli)
{
- 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);
+ mysql_mutex_assert_owner(&rli->data_lock);
+ return !rli->last_inuse_relaylog ||
+ rli->last_inuse_relaylog->queued_count ==
+ rli->last_inuse_relaylog->dequeued_count;
}
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index a9cfefcb..307d0e3b 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -460,9 +460,10 @@ struct rpl_parallel {
rpl_parallel_entry *find(uint32 domain_id, Relay_log_info *rli);
void wait_for_done(THD *thd, Relay_log_info *rli);
void stop_during_until();
- bool workers_idle();
int wait_for_workers_idle(THD *thd);
int do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size);
+
+ static bool workers_idle(Relay_log_info *rli);
};
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 0fd90704..30e1bb45 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -623,7 +623,7 @@ struct inuse_relaylog {
rpl_gtid *relay_log_state;
uint32 relay_log_state_count;
/* Number of events in this relay log queued for worker threads. */
- int64 queued_count;
+ Atomic_counter<int64> queued_count;
/* Number of events completed by worker threads. */
Atomic_counter<int64> dequeued_count;
/* Set when all events have been read from a relaylog. */
diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc
index 670a6d8d..9f30a820 100644
--- a/sql/semisync_master.cc
+++ b/sql/semisync_master.cc
@@ -91,7 +91,9 @@ Active_tranx::Active_tranx(mysql_mutex_t *lock,
for (int idx = 0; idx < m_num_entries; ++idx)
m_trx_htb[idx] = NULL;
+#ifdef EXTRA_DEBUG
sql_print_information("Semi-sync replication initialized for transactions.");
+#endif
}
Active_tranx::~Active_tranx()
@@ -352,8 +354,7 @@ Repl_semi_sync_master::Repl_semi_sync_master()
m_state(0),
m_wait_point(0)
{
- strcpy(m_reply_file_name, "");
- strcpy(m_wait_file_name, "");
+ m_reply_file_name[0]= m_wait_file_name[0]= 0;
}
int Repl_semi_sync_master::init_object()
@@ -379,20 +380,10 @@ int Repl_semi_sync_master::init_object()
{
result = enable_master();
if (!result)
- {
result= ack_receiver.start(); /* Start the ACK thread. */
- /*
- If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily
- switch off semisync to avoid hang if there's none active slave.
- */
- if (!rpl_semi_sync_master_wait_no_slave)
- switch_off();
- }
}
else
- {
disable_master();
- }
return result;
}
@@ -441,7 +432,7 @@ void Repl_semi_sync_master::disable_master()
*/
switch_off();
- assert(m_active_tranxs != NULL);
+ DBUG_ASSERT(m_active_tranxs != NULL);
delete m_active_tranxs;
m_active_tranxs = NULL;
@@ -450,7 +441,6 @@ void Repl_semi_sync_master::disable_master()
m_commit_file_name_inited = false;
set_master_enabled(false);
- sql_print_information("Semi-sync replication disabled on the master.");
}
unlock();
@@ -537,31 +527,34 @@ void Repl_semi_sync_master::add_slave()
void Repl_semi_sync_master::remove_slave()
{
lock();
- rpl_semi_sync_master_clients--;
-
- /* Only switch off if semi-sync is enabled and is on */
- if (get_master_enabled() && is_on())
+ if (!(--rpl_semi_sync_master_clients) && !rpl_semi_sync_master_wait_no_slave)
{
- /* If user has chosen not to wait if no semi-sync slave available
- and the last semi-sync slave exits, turn off semi-sync on master
- immediately.
- */
- if (!rpl_semi_sync_master_wait_no_slave &&
- rpl_semi_sync_master_clients == 0)
- switch_off();
+ /*
+ Signal transactions waiting in commit_trx() that they do not have to
+ wait anymore.
+ */
+ cond_broadcast();
}
unlock();
}
+
+/*
+ Check report package
+
+ @retval 0 ok
+ @retval 1 Error
+ @retval -1 Slave is going down (ok)
+*/
+
int Repl_semi_sync_master::report_reply_packet(uint32 server_id,
const uchar *packet,
ulong packet_len)
{
- int result= -1;
+ int result= 1; // Assume error
char log_file_name[FN_REFLEN+1];
my_off_t log_file_pos;
ulong log_file_len = 0;
-
DBUG_ENTER("Repl_semi_sync_master::report_reply_packet");
DBUG_EXECUTE_IF("semisync_corrupt_magic",
@@ -569,7 +562,14 @@ int Repl_semi_sync_master::report_reply_packet(uint32 server_id,
if (unlikely(packet[REPLY_MAGIC_NUM_OFFSET] !=
Repl_semi_sync_master::k_packet_magic_num))
{
- sql_print_error("Read semi-sync reply magic number error");
+ if (packet[0] == COM_QUIT && packet_len == 1)
+ {
+ /* Slave sent COM_QUIT as part of IO thread going down */
+ sql_print_information("slave IO thread has stopped");
+ DBUG_RETURN(-1);
+ }
+ else
+ sql_print_error("Read semi-sync reply magic number error");
goto l_end;
}
@@ -597,14 +597,13 @@ int Repl_semi_sync_master::report_reply_packet(uint32 server_id,
rpl_semi_sync_master_get_ack++;
report_reply_binlog(server_id, log_file_name, log_file_pos);
- result= 0;
+ DBUG_RETURN(0);
l_end:
- if (result == -1)
{
char buf[256];
- octet2hex(buf, (const char*) packet, std::min(static_cast<ulong>(sizeof(buf)-1),
- packet_len));
+ octet2hex(buf, (const char*) packet,
+ MY_MIN(sizeof(buf)-1, (size_t) packet_len));
sql_print_information("First bytes of the packet from semisync slave "
"server-id %d: %s", server_id, buf);
@@ -668,7 +667,7 @@ int Repl_semi_sync_master::report_reply_binlog(uint32 server_id,
m_reply_file_name_inited = true;
/* Remove all active transaction nodes before this point. */
- assert(m_active_tranxs != NULL);
+ DBUG_ASSERT(m_active_tranxs != NULL);
m_active_tranxs->clear_active_tranx_nodes(log_file_name, log_file_pos);
DBUG_PRINT("semisync", ("%s: Got reply at (%s, %lu)",
@@ -809,6 +808,8 @@ int Repl_semi_sync_master::dump_start(THD* thd,
(long) thd->variables.server_id, log_file,
(ulong) log_pos);
+ /* Mark that semi-sync net->pkt_nr is not reliable */
+ thd->net.pkt_nr_can_be_reset= 1;
return 0;
}
@@ -827,8 +828,15 @@ void Repl_semi_sync_master::dump_end(THD* thd)
int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name,
my_off_t trx_wait_binlog_pos)
{
+ bool success= 0;
DBUG_ENTER("Repl_semi_sync_master::commit_trx");
+ if (!rpl_semi_sync_master_clients && !rpl_semi_sync_master_wait_no_slave)
+ {
+ rpl_semi_sync_master_no_transactions++;
+ DBUG_RETURN(0);
+ }
+
if (get_master_enabled() && trx_wait_binlog_name)
{
struct timespec start_ts;
@@ -836,7 +844,7 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name,
int wait_result;
PSI_stage_info old_stage;
THD *thd= current_thd;
-
+ bool aborted= 0;
set_timespec(start_ts, 0);
DEBUG_SYNC(thd, "rpl_semisync_master_commit_trx_before_lock");
@@ -859,6 +867,13 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name,
while (is_on() && !thd_killed(thd))
{
+ /* We have to check these again as things may have changed */
+ if (!rpl_semi_sync_master_clients && !rpl_semi_sync_master_wait_no_slave)
+ {
+ aborted= 1;
+ break;
+ }
+
if (m_reply_file_name_inited)
{
int cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos,
@@ -873,6 +888,7 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name,
"Repl_semi_sync_master::commit_trx",
m_reply_file_name,
(ulong)m_reply_file_pos));
+ success= 1;
break;
}
}
@@ -973,13 +989,13 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name,
m_active_tranxs may be NULL if someone disabled semi sync during
cond_timewait()
*/
- assert(thd_killed(thd) || !m_active_tranxs ||
- !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name,
- trx_wait_binlog_pos));
+ DBUG_ASSERT(thd_killed(thd) || !m_active_tranxs || aborted ||
+ !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name,
+ trx_wait_binlog_pos));
l_end:
/* Update the status counter. */
- if (is_on())
+ if (success)
rpl_semi_sync_master_yes_transactions++;
else
rpl_semi_sync_master_no_transactions++;
@@ -1014,18 +1030,20 @@ void Repl_semi_sync_master::switch_off()
{
DBUG_ENTER("Repl_semi_sync_master::switch_off");
- m_state = false;
-
- /* Clear the active transaction list. */
- assert(m_active_tranxs != NULL);
- m_active_tranxs->clear_active_tranx_nodes(NULL, 0);
+ if (m_state)
+ {
+ m_state = false;
- rpl_semi_sync_master_off_times++;
- m_wait_file_name_inited = false;
- m_reply_file_name_inited = false;
- sql_print_information("Semi-sync replication switched OFF.");
- cond_broadcast(); /* wake up all waiting threads */
+ /* Clear the active transaction list. */
+ DBUG_ASSERT(m_active_tranxs != NULL);
+ m_active_tranxs->clear_active_tranx_nodes(NULL, 0);
+ rpl_semi_sync_master_off_times++;
+ m_wait_file_name_inited = false;
+ m_reply_file_name_inited = false;
+ sql_print_information("Semi-sync replication switched OFF.");
+ }
+ cond_broadcast(); /* wake up all waiting threads */
DBUG_VOID_RETURN;
}
@@ -1072,9 +1090,10 @@ int Repl_semi_sync_master::reserve_sync_header(String* packet)
{
DBUG_ENTER("Repl_semi_sync_master::reserve_sync_header");
- /* Set the magic number and the sync status. By default, no sync
- * is required.
- */
+ /*
+ Set the magic number and the sync status. By default, no sync
+ is required.
+ */
packet->append(reinterpret_cast<const char*>(k_sync_header),
sizeof(k_sync_header));
DBUG_RETURN(0);
@@ -1087,7 +1106,6 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet,
{
int cmp = 0;
bool sync = false;
-
DBUG_ENTER("Repl_semi_sync_master::update_sync_header");
/* If the semi-sync master is not enabled, or the slave is not a semi-sync
@@ -1103,16 +1121,11 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet,
/* This is the real check inside the mutex. */
if (!get_master_enabled())
- {
- assert(sync == false);
goto l_end;
- }
if (is_on())
{
/* semi-sync is ON */
- sync = false; /* No sync unless a transaction is involved. */
-
if (m_reply_file_name_inited)
{
cmp = Active_tranx::compare(log_file_name, log_file_pos,
@@ -1126,15 +1139,10 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet,
}
}
+ cmp = 1;
if (m_wait_file_name_inited)
- {
cmp = Active_tranx::compare(log_file_name, log_file_pos,
m_wait_file_name, m_wait_file_pos);
- }
- else
- {
- cmp = 1;
- }
/* If we are already waiting for some transaction replies which
* are later in binlog, do not wait for this one event.
@@ -1144,7 +1152,7 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet,
/*
* We only wait if the event is a transaction's ending event.
*/
- assert(m_active_tranxs != NULL);
+ DBUG_ASSERT(m_active_tranxs != NULL);
sync = m_active_tranxs->is_tranx_end_pos(log_file_name,
log_file_pos);
}
@@ -1172,13 +1180,12 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet,
l_end:
unlock();
- /* We do not need to clear sync flag because we set it to 0 when we
- * reserve the packet header.
- */
+ /*
+ We do not need to clear sync flag in packet because we set it to 0 when we
+ reserve the packet header.
+ */
if (sync)
- {
- (packet)[2] = k_packet_flag_sync;
- }
+ packet[2]= k_packet_flag_sync;
DBUG_RETURN(0);
}
@@ -1225,7 +1232,7 @@ int Repl_semi_sync_master::write_tranx_in_binlog(const char* log_file_name,
if (is_on())
{
- assert(m_active_tranxs != NULL);
+ DBUG_ASSERT(m_active_tranxs != NULL);
if(m_active_tranxs->insert_tranx_node(log_file_name, log_file_pos))
{
/*
@@ -1256,7 +1263,7 @@ int Repl_semi_sync_master::flush_net(THD *thd,
DBUG_ENTER("Repl_semi_sync_master::flush_net");
- assert((unsigned char)event_buf[1] == k_packet_magic_num);
+ DBUG_ASSERT((unsigned char)event_buf[1] == k_packet_magic_num);
if ((unsigned char)event_buf[2] != k_packet_flag_sync)
{
/* current event does not require reply */
@@ -1274,6 +1281,11 @@ int Repl_semi_sync_master::flush_net(THD *thd,
goto l_end;
}
+ /*
+ We have to do a net_clear() as with semi-sync the slave_reply's are
+ interleaved with data from the master and then the net->pkt_nr
+ cannot be kept in sync. Better to start pkt_nr from 0 again.
+ */
net_clear(net, 0);
net->pkt_nr++;
net->compress_pkt_nr++;
@@ -1300,11 +1312,7 @@ int Repl_semi_sync_master::after_reset_master()
lock();
- if (rpl_semi_sync_master_clients == 0 &&
- !rpl_semi_sync_master_wait_no_slave)
- m_state = 0;
- else
- m_state = get_master_enabled()? 1 : 0;
+ m_state = get_master_enabled() ? 1 : 0;
m_wait_file_name_inited = false;
m_reply_file_name_inited = false;
@@ -1338,18 +1346,6 @@ int Repl_semi_sync_master::before_reset_master()
DBUG_RETURN(result);
}
-void Repl_semi_sync_master::check_and_switch()
-{
- lock();
- if (get_master_enabled() && is_on())
- {
- if (!rpl_semi_sync_master_wait_no_slave
- && rpl_semi_sync_master_clients == 0)
- switch_off();
- }
- unlock();
-}
-
void Repl_semi_sync_master::set_export_stats()
{
lock();
@@ -1363,7 +1359,6 @@ void Repl_semi_sync_master::set_export_stats()
((rpl_semi_sync_master_net_wait_num) ?
(ulong)((double)rpl_semi_sync_master_net_wait_time /
((double)rpl_semi_sync_master_net_wait_num)) : 0);
-
unlock();
}
diff --git a/sql/semisync_master.h b/sql/semisync_master.h
index 5451ad51..99f46869 100644
--- a/sql/semisync_master.h
+++ b/sql/semisync_master.h
@@ -633,8 +633,6 @@ class Repl_semi_sync_master
/*called before reset master*/
int before_reset_master();
- void check_and_switch();
-
/*
Determines if the given thread is currently awaiting a semisync_ack. Note
that the thread's value is protected by this class's LOCK_binlog, so this
diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc
index 559f939c..a311599c 100644
--- a/sql/semisync_master_ack_receiver.cc
+++ b/sql/semisync_master_ack_receiver.cc
@@ -24,7 +24,8 @@ extern PSI_cond_key key_COND_ack_receiver;
#ifdef HAVE_PSI_THREAD_INTERFACE
extern PSI_thread_key key_thread_ack_receiver;
#endif
-extern Repl_semi_sync_master repl_semisync;
+
+my_socket global_ack_signal_fd= -1;
/* Callback function of ack receive thread */
pthread_handler_t ack_receive_handler(void *arg)
@@ -45,6 +46,7 @@ Ack_receiver::Ack_receiver()
m_status= ST_DOWN;
mysql_mutex_init(key_LOCK_ack_receiver, &m_mutex, NULL);
mysql_cond_init(key_COND_ack_receiver, &m_cond, NULL);
+ mysql_cond_init(key_COND_ack_receiver, &m_cond_reply, NULL);
m_pid= 0;
DBUG_VOID_RETURN;
@@ -57,6 +59,7 @@ void Ack_receiver::cleanup()
stop();
mysql_mutex_destroy(&m_mutex);
mysql_cond_destroy(&m_cond);
+ mysql_cond_destroy(&m_cond_reply);
DBUG_VOID_RETURN;
}
@@ -104,6 +107,7 @@ void Ack_receiver::stop()
if (m_status == ST_UP)
{
m_status= ST_STOPPING;
+ signal_listener(); // Signal listener thread to stop
mysql_cond_broadcast(&m_cond);
while (m_status == ST_STOPPING)
@@ -118,6 +122,21 @@ void Ack_receiver::stop()
DBUG_VOID_RETURN;
}
+#ifndef DBUG_OFF
+void static dbug_verify_no_duplicate_slaves(Slave_ilist *m_slaves, THD *thd)
+{
+ I_List_iterator<Slave> it(*m_slaves);
+ Slave *slave;
+ while ((slave= it++))
+ {
+ DBUG_ASSERT(slave->thd->variables.server_id != thd->variables.server_id);
+ }
+}
+#else
+#define dbug_verify_no_duplicate_slaves(A,B) do {} while(0)
+#endif
+
+
bool Ack_receiver::add_slave(THD *thd)
{
Slave *slave;
@@ -126,17 +145,23 @@ bool Ack_receiver::add_slave(THD *thd)
if (!(slave= new Slave))
DBUG_RETURN(true);
+ slave->active= 0;
slave->thd= thd;
slave->vio= *thd->net.vio;
slave->vio.mysql_socket.m_psi= NULL;
slave->vio.read_timeout= 1;
mysql_mutex_lock(&m_mutex);
+
+ dbug_verify_no_duplicate_slaves(&m_slaves, thd);
+
m_slaves.push_back(slave);
m_slaves_changed= true;
mysql_cond_broadcast(&m_cond);
mysql_mutex_unlock(&m_mutex);
+ signal_listener(); // Inform listener that there are new slaves
+
DBUG_RETURN(false);
}
@@ -144,6 +169,7 @@ void Ack_receiver::remove_slave(THD *thd)
{
I_List_iterator<Slave> it(m_slaves);
Slave *slave;
+ bool slaves_changed= 0;
DBUG_ENTER("Ack_receiver::remove_slave");
mysql_mutex_lock(&m_mutex);
@@ -153,10 +179,23 @@ void Ack_receiver::remove_slave(THD *thd)
if (slave->thd == thd)
{
delete slave;
- m_slaves_changed= true;
+ slaves_changed= true;
break;
}
}
+ if (slaves_changed)
+ {
+ m_slaves_changed= true;
+ mysql_cond_broadcast(&m_cond);
+ /*
+ Wait until Ack_receiver::run() acknowledges remove of slave
+ As this is only sent under the mutex and after listners has
+ been collected, we know that listener has ignored the found
+ slave.
+ */
+ if (m_status != ST_DOWN)
+ mysql_cond_wait(&m_cond_reply, &m_mutex);
+ }
mysql_mutex_unlock(&m_mutex);
DBUG_VOID_RETURN;
@@ -167,10 +206,15 @@ inline void Ack_receiver::set_stage_info(const PSI_stage_info &stage)
(void)MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__);
}
-inline void Ack_receiver::wait_for_slave_connection()
+void Ack_receiver::wait_for_slave_connection(THD *thd)
{
- set_stage_info(stage_waiting_for_semi_sync_slave);
- mysql_cond_wait(&m_cond, &m_mutex);
+ thd->enter_cond(&m_cond, &m_mutex, &stage_waiting_for_semi_sync_slave,
+ 0, __func__, __FILE__, __LINE__);
+
+ while (m_status == ST_UP && m_slaves.is_empty())
+ mysql_cond_wait(&m_cond, &m_mutex);
+
+ thd->exit_cond(0, __func__, __FILE__, __LINE__);
}
/* Auxilary function to initialize a NET object with given net buffer. */
@@ -188,17 +232,23 @@ void Ack_receiver::run()
THD *thd= new THD(next_thread_id());
NET net;
unsigned char net_buff[REPLY_MESSAGE_MAX_LENGTH];
+ DBUG_ENTER("Ack_receiver::run");
my_thread_init();
- DBUG_ENTER("Ack_receiver::run");
-
#ifdef HAVE_POLL
Poll_socket_listener listener(m_slaves);
#else
Select_socket_listener listener(m_slaves);
#endif //HAVE_POLL
+ if (listener.got_error())
+ {
+ sql_print_error("Got error %M starting ack receiver thread",
+ listener.got_error());
+ return;
+ }
+
sql_print_information("Starting ack receiver thread");
thd->system_thread= SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND;
thd->thread_stack= (char*) &thd;
@@ -207,64 +257,79 @@ void Ack_receiver::run()
thd->set_command(COM_DAEMON);
init_net(&net, net_buff, REPLY_MESSAGE_MAX_LENGTH);
- mysql_mutex_lock(&m_mutex);
+ /*
+ Mark that we have to setup the listener. Note that only this functions can
+ set m_slaves_changed to false
+ */
m_slaves_changed= true;
- mysql_mutex_unlock(&m_mutex);
while (1)
{
- int ret;
- uint slave_count __attribute__((unused))= 0;
+ int ret, slave_count= 0;
Slave *slave;
mysql_mutex_lock(&m_mutex);
- if (unlikely(m_status == ST_STOPPING))
+ if (unlikely(m_status != ST_UP))
goto end;
- set_stage_info(stage_waiting_for_semi_sync_ack_from_slave);
if (unlikely(m_slaves_changed))
{
if (unlikely(m_slaves.is_empty()))
{
- wait_for_slave_connection();
- mysql_mutex_unlock(&m_mutex);
+ m_slaves_changed= false;
+ mysql_cond_broadcast(&m_cond_reply); // Signal remove_slave
+ wait_for_slave_connection(thd);
+ /* Wait for slave unlocks m_mutex */
continue;
}
+ set_stage_info(stage_waiting_for_semi_sync_ack_from_slave);
if ((slave_count= listener.init_slave_sockets()) == 0)
+ {
+ mysql_mutex_unlock(&m_mutex);
+ m_slaves_changed= true;
+ continue; // Retry
+ }
+ if (slave_count < 0)
goto end;
m_slaves_changed= false;
+ mysql_cond_broadcast(&m_cond_reply); // Signal remove_slave
+ }
+
#ifdef HAVE_POLL
DBUG_PRINT("info", ("fd count %u", slave_count));
#else
DBUG_PRINT("info", ("fd count %u, max_fd %d", slave_count,
(int) listener.get_max_fd()));
#endif
- }
+ mysql_mutex_unlock(&m_mutex);
ret= listener.listen_on_sockets();
+
if (ret <= 0)
{
- mysql_mutex_unlock(&m_mutex);
ret= DBUG_IF("rpl_semisync_simulate_select_error") ? -1 : ret;
if (ret == -1 && errno != EINTR)
sql_print_information("Failed to wait on semi-sync sockets, "
"error: errno=%d", socket_errno);
- /* Sleep 1us, so other threads can catch the m_mutex easily. */
- my_sleep(1);
continue;
}
+ listener.clear_signal();
+ mysql_mutex_lock(&m_mutex);
set_stage_info(stage_reading_semi_sync_ack);
Slave_ilist_iterator it(m_slaves);
while ((slave= it++))
{
- if (listener.is_socket_active(slave))
+ if (slave->active &&
+ ((slave->vio.read_pos < slave->vio.read_end) ||
+ listener.is_socket_active(slave)))
{
ulong len;
+ /* Semi-sync packets will always be sent with pkt_nr == 1 */
net_clear(&net, 0);
net.vio= &slave->vio;
/*
@@ -275,29 +340,42 @@ void Ack_receiver::run()
len= my_net_read(&net);
if (likely(len != packet_error))
- repl_semisync_master.report_reply_packet(slave->server_id(),
- net.read_pos, len);
- else
{
- if (net.last_errno == ER_NET_READ_ERROR)
+ int res;
+ res= repl_semisync_master.report_reply_packet(slave->server_id(),
+ net.read_pos, len);
+ if (unlikely(res < 0))
{
- listener.clear_socket_info(slave);
+ /*
+ Slave has sent COM_QUIT or other failure.
+ Delete it from listener
+ */
+ it.remove();
+ m_slaves_changed= true;
}
+ }
+ else if (net.last_errno == ER_NET_READ_ERROR)
+ {
if (net.last_errno > 0 && global_system_variables.log_warnings > 2)
sql_print_warning("Semisync ack receiver got error %d \"%s\" "
"from slave server-id %d",
net.last_errno, ER_DEFAULT(net.last_errno),
slave->server_id());
+ it.remove();
+ m_slaves_changed= true;
}
}
}
mysql_mutex_unlock(&m_mutex);
}
+
end:
sql_print_information("Stopping ack receiver thread");
m_status= ST_DOWN;
- delete thd;
mysql_cond_broadcast(&m_cond);
+ mysql_cond_broadcast(&m_cond_reply);
mysql_mutex_unlock(&m_mutex);
+
+ delete thd;
DBUG_VOID_RETURN;
}
diff --git a/sql/semisync_master_ack_receiver.h b/sql/semisync_master_ack_receiver.h
index d869bd2e..eacb4b20 100644
--- a/sql/semisync_master_ack_receiver.h
+++ b/sql/semisync_master_ack_receiver.h
@@ -20,6 +20,7 @@
#include "my_pthread.h"
#include "sql_class.h"
#include "semisync.h"
+#include "socketpair.h"
#include <vector>
struct Slave :public ilink
@@ -29,6 +30,7 @@ struct Slave :public ilink
#ifdef HAVE_POLL
uint m_fds_index;
#endif
+ bool active;
my_socket sock_fd() const { return vio.mysql_socket.fd; }
uint server_id() const { return thd->variables.server_id; }
};
@@ -46,6 +48,7 @@ typedef I_List_iterator<Slave> Slave_ilist_iterator;
add_slave: maintain a new semisync slave's information
remove_slave: remove a semisync slave's information
*/
+
class Ack_receiver : public Repl_semi_sync_base
{
public:
@@ -96,15 +99,20 @@ public:
{
m_trace_level= trace_level;
}
+ bool running()
+ {
+ return m_status != ST_DOWN;
+ }
+
private:
enum status {ST_UP, ST_DOWN, ST_STOPPING};
- uint8 m_status;
+ enum status m_status;
/*
Protect m_status, m_slaves_changed and m_slaves. ack thread and other
session may access the variables at the same time.
*/
mysql_mutex_t m_mutex;
- mysql_cond_t m_cond;
+ mysql_cond_t m_cond, m_cond_reply;
/* If slave list is updated(add or remove). */
bool m_slaves_changed;
@@ -116,25 +124,103 @@ private:
Ack_receiver& operator=(const Ack_receiver &ack_receiver);
void set_stage_info(const PSI_stage_info &stage);
- void wait_for_slave_connection();
+ void wait_for_slave_connection(THD *thd);
};
+extern my_socket global_ack_signal_fd;
+
+class Ack_listener
+{
+public:
+ my_socket local_read_signal;
+ const Slave_ilist &m_slaves;
+ int error;
+
+ Ack_listener(const Slave_ilist &slaves)
+ :local_read_signal(-1), m_slaves(slaves), error(0)
+ {
+ my_socket pipes[2];
+#ifdef _WIN32
+ error= create_socketpair(pipes);
+#else
+ if (!pipe(pipes))
+ {
+ fcntl(pipes[0], F_SETFL, O_NONBLOCK);
+ fcntl(pipes[1], F_SETFL, O_NONBLOCK);
+ }
+ else
+ {
+ pipes[0]= pipes[1]= -1;
+ }
+#endif /* _WIN32 */
+ local_read_signal= pipes[0];
+ global_ack_signal_fd= pipes[1];
+ }
+
+ virtual ~Ack_listener()
+ {
+#ifdef _WIN32
+ my_socket pipes[2];
+ pipes[0]= local_read_signal;
+ pipes[1]= global_ack_signal_fd;
+ close_socketpair(pipes);
+#else
+ if (global_ack_signal_fd >= 0)
+ close(global_ack_signal_fd);
+ if (local_read_signal >= 0)
+ close(local_read_signal);
+#endif /* _WIN32 */
+ global_ack_signal_fd= local_read_signal= -1;
+ }
+
+ int got_error() { return error; }
+
+ virtual bool has_signal_data()= 0;
+
+ /* Clear data sent by signal_listener() to abort read */
+ void clear_signal()
+ {
+ if (has_signal_data())
+ {
+ char buff[100];
+ /* Clear the signal message */
+#ifndef _WIN32
+ read(local_read_signal, buff, sizeof(buff));
+#else
+ recv(local_read_signal, buff, sizeof(buff), 0);
+#endif /* _WIN32 */
+ }
+ }
+};
+
+static inline void signal_listener()
+{
+#ifndef _WIN32
+ my_write(global_ack_signal_fd, (uchar*) "a", 1, MYF(0));
+#else
+ send(global_ack_signal_fd, "a", 1, 0);
+#endif /* _WIN32 */
+}
+
#ifdef HAVE_POLL
#include <sys/poll.h>
-#include <vector>
-class Poll_socket_listener
+class Poll_socket_listener final : public Ack_listener
{
+private:
+ std::vector<pollfd> m_fds;
+
public:
Poll_socket_listener(const Slave_ilist &slaves)
- :m_slaves(slaves)
- {
- }
+ :Ack_listener(slaves)
+ {}
+
+ virtual ~Poll_socket_listener() = default;
bool listen_on_sockets()
{
- return poll(m_fds.data(), m_fds.size(), 1000 /*1 Second timeout*/);
+ return poll(m_fds.data(), m_fds.size(), -1);
}
bool is_socket_active(const Slave *slave)
@@ -148,15 +234,29 @@ public:
m_fds[slave->m_fds_index].events= 0;
}
- uint init_slave_sockets()
+ bool has_signal_data() override
+ {
+ /* The signal fd is always first */
+ return (m_fds[0].revents & POLLIN);
+ }
+
+ int init_slave_sockets()
{
Slave_ilist_iterator it(const_cast<Slave_ilist&>(m_slaves));
Slave *slave;
uint fds_index= 0;
+ pollfd poll_fd;
m_fds.clear();
+ /* First put in the signal socket */
+ poll_fd.fd= local_read_signal;
+ poll_fd.events= POLLIN;
+ m_fds.push_back(poll_fd);
+ fds_index++;
+
while ((slave= it++))
{
+ slave->active= 1;
pollfd poll_fd;
poll_fd.fd= slave->sock_fd();
poll_fd.events= POLLIN;
@@ -165,29 +265,30 @@ public:
}
return fds_index;
}
-
-private:
- const Slave_ilist &m_slaves;
- std::vector<pollfd> m_fds;
};
#else //NO POLL
-class Select_socket_listener
+class Select_socket_listener final : public Ack_listener
{
+private:
+ my_socket m_max_fd;
+ fd_set m_init_fds;
+ fd_set m_fds;
+
public:
Select_socket_listener(const Slave_ilist &slaves)
- :m_slaves(slaves), m_max_fd(INVALID_SOCKET)
- {
- }
+ :Ack_listener(slaves), m_max_fd(INVALID_SOCKET)
+ {}
+
+ virtual ~Select_socket_listener() = default;
bool listen_on_sockets()
{
/* Reinitialize the fds with active fds before calling select */
m_fds= m_init_fds;
- struct timeval tv= {1,0};
/* select requires max fd + 1 for the first argument */
- return select((int) m_max_fd+1, &m_fds, NULL, NULL, &tv);
+ return select((int) m_max_fd+1, &m_fds, NULL, NULL, NULL);
}
bool is_socket_active(const Slave *slave)
@@ -195,43 +296,61 @@ public:
return FD_ISSET(slave->sock_fd(), &m_fds);
}
+ bool has_signal_data() override
+ {
+ return FD_ISSET(local_read_signal, &m_fds);
+ }
+
void clear_socket_info(const Slave *slave)
{
FD_CLR(slave->sock_fd(), &m_init_fds);
}
- uint init_slave_sockets()
+ int init_slave_sockets()
{
Slave_ilist_iterator it(const_cast<Slave_ilist&>(m_slaves));
Slave *slave;
uint fds_index= 0;
FD_ZERO(&m_init_fds);
+ m_max_fd= -1;
+
+ /* First put in the signal socket */
+ FD_SET(local_read_signal, &m_init_fds);
+ fds_index++;
+ set_if_bigger(m_max_fd, local_read_signal);
+#ifndef _WIN32
+ if (local_read_signal > FD_SETSIZE)
+ {
+ int socket_id= local_read_signal;
+ sql_print_error("Semisync slave socket fd is %u. "
+ "select() cannot handle if the socket fd is "
+ "greater than %u (FD_SETSIZE).", socket_id, FD_SETSIZE);
+ return -1;
+ }
+#endif
+
while ((slave= it++))
{
my_socket socket_id= slave->sock_fd();
- m_max_fd= (socket_id > m_max_fd ? socket_id : m_max_fd);
+ set_if_bigger(m_max_fd, socket_id);
#ifndef _WIN32
if (socket_id > FD_SETSIZE)
{
sql_print_error("Semisync slave socket fd is %u. "
"select() cannot handle if the socket fd is "
"greater than %u (FD_SETSIZE).", socket_id, FD_SETSIZE);
- return 0;
+ it.remove();
+ continue;
}
#endif //_WIN32
FD_SET(socket_id, &m_init_fds);
fds_index++;
+ slave->active= 1;
}
return fds_index;
}
my_socket get_max_fd() { return m_max_fd; }
-
-private:
- const Slave_ilist &m_slaves;
- my_socket m_max_fd;
- fd_set m_init_fds;
- fd_set m_fds;
};
#endif //HAVE_POLL
diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc
index 788aab78..4314b116 100644
--- a/sql/semisync_slave.cc
+++ b/sql/semisync_slave.cc
@@ -20,20 +20,9 @@
Repl_semi_sync_slave repl_semisync_slave;
-my_bool rpl_semi_sync_slave_enabled= 0;
-
+my_bool global_rpl_semi_sync_slave_enabled= 0;
char rpl_semi_sync_slave_delay_master;
-my_bool rpl_semi_sync_slave_status= 0;
ulong rpl_semi_sync_slave_trace_level;
-
-/*
- indicate whether or not the slave should send a reply to the master.
-
- This is set to true in repl_semi_slave_read_event if the current
- event read is the last event of a transaction. And the value is
- checked in repl_semi_slave_queue_event.
-*/
-bool semi_sync_need_reply= false;
unsigned int rpl_semi_sync_slave_kill_conn_timeout;
unsigned long long rpl_semi_sync_slave_send_ack = 0;
@@ -44,14 +33,26 @@ int Repl_semi_sync_slave::init_object()
m_init_done = true;
/* References to the parameter works after set_options(). */
- set_slave_enabled(rpl_semi_sync_slave_enabled);
+ set_slave_enabled(global_rpl_semi_sync_slave_enabled);
set_trace_level(rpl_semi_sync_slave_trace_level);
set_delay_master(rpl_semi_sync_slave_delay_master);
set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout);
-
return result;
}
+static bool local_semi_sync_enabled;
+
+int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *status_var,
+ enum_var_type scope)
+{
+ local_semi_sync_enabled= repl_semisync_slave.get_slave_enabled();
+ var->type= SHOW_BOOL;
+ var->value= (char*) &local_semi_sync_enabled;
+ return 0;
+}
+
+
int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
unsigned long total_len,
int *semi_flags,
@@ -61,12 +62,12 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
int read_res = 0;
DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header");
- if (rpl_semi_sync_slave_status)
+ if (get_slave_enabled())
{
if (!DBUG_IF("semislave_corrupt_log")
&& header[0] == k_packet_magic_num)
{
- semi_sync_need_reply = (header[1] & k_packet_flag_sync);
+ bool semi_sync_need_reply = (header[1] & k_packet_flag_sync);
*payload_len = total_len - 2;
*payload = header + 2;
@@ -85,7 +86,9 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
"len: %lu", total_len);
read_res = -1;
}
- } else {
+ }
+ else
+ {
*payload= header;
*payload_len= total_len;
}
@@ -93,9 +96,23 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
DBUG_RETURN(read_res);
}
-int Repl_semi_sync_slave::slave_start(Master_info *mi)
+/*
+ Set default semisync variables and print some replication info to the log
+
+ Note that the main setup is done in request_transmit()
+*/
+
+void Repl_semi_sync_slave::slave_start(Master_info *mi)
{
- bool semi_sync= get_slave_enabled();
+
+ /*
+ Set semi_sync_enabled at slave start. This is not changed until next
+ slave start or reconnect.
+ */
+ bool semi_sync= global_rpl_semi_sync_slave_enabled;
+
+ set_slave_enabled(semi_sync);
+ mi->semi_sync_reply_enabled= 0;
sql_print_information("Slave I/O thread: Start %s replication to\
master '%s@%s:%d' in log '%s' at position %lu",
@@ -104,30 +121,29 @@ int Repl_semi_sync_slave::slave_start(Master_info *mi)
const_cast<char *>(mi->master_log_name),
(unsigned long)(mi->master_log_pos));
- if (semi_sync && !rpl_semi_sync_slave_status)
- rpl_semi_sync_slave_status= 1;
-
/*clear the counter*/
rpl_semi_sync_slave_send_ack= 0;
- return 0;
}
-int Repl_semi_sync_slave::slave_stop(Master_info *mi)
+void Repl_semi_sync_slave::slave_stop(Master_info *mi)
{
if (get_slave_enabled())
kill_connection(mi->mysql);
- if (rpl_semi_sync_slave_status)
- rpl_semi_sync_slave_status= 0;
-
- return 0;
+ set_slave_enabled(0);
}
-int Repl_semi_sync_slave::reset_slave(Master_info *mi)
+void Repl_semi_sync_slave::slave_reconnect(Master_info *mi)
{
- return 0;
+ /*
+ Start semi-sync either if it globally enabled or if was enabled
+ before the reconnect.
+ */
+ if (global_rpl_semi_sync_slave_enabled || get_slave_enabled())
+ slave_start(mi);
}
+
void Repl_semi_sync_slave::kill_connection(MYSQL *mysql)
{
if (!mysql)
@@ -194,33 +210,43 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi)
!(res= mysql_store_result(mysql)))
{
sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql));
+ set_slave_enabled(0);
return 1;
}
row= mysql_fetch_row(res);
- if (DBUG_IF("master_not_support_semisync") || !row)
+ if (DBUG_IF("master_not_support_semisync") || (!row || ! row[1]))
{
/* Master does not support semi-sync */
- sql_print_warning("Master server does not support semi-sync, "
- "fallback to asynchronous replication");
- rpl_semi_sync_slave_status= 0;
+ if (!row)
+ sql_print_warning("Master server does not support semi-sync, "
+ "fallback to asynchronous replication");
+ set_slave_enabled(0);
mysql_free_result(res);
return 0;
}
+ if (strcmp(row[1], "ON"))
+ sql_print_information("Slave has semi-sync enabled but master server does "
+ "not. Semi-sync will be activated when master "
+ "enables it");
mysql_free_result(res);
/*
Tell master dump thread that we want to do semi-sync
- replication
+ replication. This is done by setting a thread local variable in
+ the master connection.
*/
query= "SET @rpl_semi_sync_slave= 1";
if (mysql_real_query(mysql, query, (ulong)strlen(query)))
{
- sql_print_error("Set 'rpl_semi_sync_slave=1' on master failed");
+ sql_print_error("%s on master failed", query);
+ set_slave_enabled(0);
return 1;
}
+ mi->semi_sync_reply_enabled= 1;
+ /* Inform net_server that pkt_nr can come out of order */
+ mi->mysql->net.pkt_nr_can_be_reset= 1;
mysql_free_result(mysql_store_result(mysql));
- rpl_semi_sync_slave_status= 1;
return 0;
}
@@ -230,46 +256,40 @@ int Repl_semi_sync_slave::slave_reply(Master_info *mi)
MYSQL* mysql= mi->mysql;
const char *binlog_filename= const_cast<char *>(mi->master_log_name);
my_off_t binlog_filepos= mi->master_log_pos;
-
NET *net= &mysql->net;
uchar reply_buffer[REPLY_MAGIC_NUM_LEN
+ REPLY_BINLOG_POS_LEN
+ REPLY_BINLOG_NAME_LEN];
int reply_res = 0;
size_t name_len = strlen(binlog_filename);
-
DBUG_ENTER("Repl_semi_sync_slave::slave_reply");
+ DBUG_ASSERT(get_slave_enabled() && mi->semi_sync_reply_enabled);
- if (rpl_semi_sync_slave_status && semi_sync_need_reply)
+ /* Prepare the buffer of the reply. */
+ reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num;
+ int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos);
+ memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET,
+ binlog_filename,
+ name_len + 1 /* including trailing '\0' */);
+
+ DBUG_PRINT("semisync", ("%s: reply (%s, %lu)",
+ "Repl_semi_sync_slave::slave_reply",
+ binlog_filename, (ulong)binlog_filepos));
+
+ /*
+ We have to do a net_clear() as with semi-sync the slave_reply's are
+ interleaved with data from the master and then the net->pkt_nr
+ cannot be kept in sync. Better to start pkt_nr from 0 again.
+ */
+ net_clear(net, 0);
+ /* Send the reply. */
+ reply_res = my_net_write(net, reply_buffer,
+ name_len + REPLY_BINLOG_NAME_OFFSET);
+ if (!reply_res)
{
- /* Prepare the buffer of the reply. */
- reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num;
- int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos);
- memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET,
- binlog_filename,
- name_len + 1 /* including trailing '\0' */);
-
- DBUG_PRINT("semisync", ("%s: reply (%s, %lu)",
- "Repl_semi_sync_slave::slave_reply",
- binlog_filename, (ulong)binlog_filepos));
-
- net_clear(net, 0);
- /* Send the reply. */
- reply_res = my_net_write(net, reply_buffer,
- name_len + REPLY_BINLOG_NAME_OFFSET);
+ reply_res= DBUG_IF("semislave_failed_net_flush") || net_flush(net);
if (!reply_res)
- {
- reply_res = (DBUG_IF("semislave_failed_net_flush") || net_flush(net));
- if (reply_res)
- sql_print_error("Semi-sync slave net_flush() reply failed");
rpl_semi_sync_slave_send_ack++;
- }
- else
- {
- sql_print_error("Semi-sync slave send reply failed: %s (%d)",
- net->last_error, net->last_errno);
- }
}
-
DBUG_RETURN(reply_res);
}
diff --git a/sql/semisync_slave.h b/sql/semisync_slave.h
index a8229245..6811584c 100644
--- a/sql/semisync_slave.h
+++ b/sql/semisync_slave.h
@@ -33,7 +33,7 @@ class Master_info;
class Repl_semi_sync_slave
:public Repl_semi_sync_base {
public:
- Repl_semi_sync_slave() :m_slave_enabled(false) {}
+ Repl_semi_sync_slave() :m_slave_enabled(false) {}
~Repl_semi_sync_slave() = default;
void set_trace_level(unsigned long trace_level) {
@@ -45,7 +45,7 @@ public:
*/
int init_object();
- bool get_slave_enabled() {
+ inline bool get_slave_enabled() {
return m_slave_enabled;
}
@@ -53,7 +53,7 @@ public:
m_slave_enabled = enabled;
}
- bool is_delay_master(){
+ inline bool is_delay_master(){
return m_delay_master;
}
@@ -88,24 +88,23 @@ public:
* binlog position.
*/
int slave_reply(Master_info* mi);
- int slave_start(Master_info *mi);
- int slave_stop(Master_info *mi);
- int request_transmit(Master_info*);
+ void slave_start(Master_info *mi);
+ void slave_stop(Master_info *mi);
+ void slave_reconnect(Master_info *mi);
+ int request_transmit(Master_info *mi);
void kill_connection(MYSQL *mysql);
- int reset_slave(Master_info *mi);
private:
/* True when init_object has been called */
bool m_init_done;
- bool m_slave_enabled; /* semi-sycn is enabled on the slave */
+ bool m_slave_enabled; /* semi-sync is enabled on the slave */
bool m_delay_master;
unsigned int m_kill_conn_timeout;
};
/* System and status variables for the slave component */
-extern my_bool rpl_semi_sync_slave_enabled;
-extern my_bool rpl_semi_sync_slave_status;
+extern my_bool global_rpl_semi_sync_slave_enabled;
extern ulong rpl_semi_sync_slave_trace_level;
extern Repl_semi_sync_slave repl_semisync_slave;
@@ -113,4 +112,7 @@ extern char rpl_semi_sync_slave_delay_master;
extern unsigned int rpl_semi_sync_slave_kill_conn_timeout;
extern unsigned long long rpl_semi_sync_slave_send_ack;
+extern int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *status_var,
+ enum_var_type scope);
#endif /* SEMISYNC_SLAVE_H */
diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc
index e1a4a25b..17240fd4 100644
--- a/sql/service_wsrep.cc
+++ b/sql/service_wsrep.cc
@@ -395,17 +395,9 @@ extern "C" void wsrep_thd_set_PA_unsafe(THD *thd)
}
}
-extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd,
- const char* db,
- const char* table,
- enum Wsrep_service_key_type key_type)
+extern "C" uint32 wsrep_get_domain_id()
{
- wsrep_key_arr_t key_arr = {0, 0};
- int ret = wsrep_prepare_keys_for_isolation(thd, db, table, NULL, &key_arr);
- ret = ret || wsrep_thd_append_key(thd, key_arr.keys,
- (int)key_arr.keys_len, key_type);
- wsrep_keys_free(&key_arr);
- return ret;
+ return wsrep_gtid_domain_id;
}
extern "C" my_bool wsrep_thd_is_local_transaction(const THD *thd)
@@ -413,4 +405,3 @@ extern "C" my_bool wsrep_thd_is_local_transaction(const THD *thd)
return (wsrep_thd_is_local(thd) &&
thd->wsrep_cs().transaction().active());
}
-
diff --git a/sql/slave.cc b/sql/slave.cc
index e781c461..27721e1b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -46,7 +46,7 @@
#include <signal.h>
#include <mysql.h>
#include <myisam.h>
-
+#include "debug_sync.h" // debug_sync_set_action
#include "sql_base.h" // close_thread_tables
#include "tztime.h" // struct Time_zone
#include "log_event.h" // Rotate_log_event,
@@ -63,7 +63,6 @@ Master_info_index *master_info_index;
#ifdef HAVE_REPLICATION
#include "rpl_tblmap.h"
-#include "debug_sync.h"
#include "rpl_parallel.h"
#include "sql_show.h"
#include "semisync_slave.h"
@@ -3200,6 +3199,14 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
mysql_mutex_lock(&mi->err_lock);
/* err_lock is to protect mi->rli.last_error() */
mysql_mutex_lock(&mi->rli.err_lock);
+
+ DBUG_EXECUTE_IF("hold_sss_with_err_lock", {
+ DBUG_ASSERT(!debug_sync_set_action(
+ thd, STRING_WITH_LEN("now SIGNAL sss_got_err_lock "
+ "WAIT_FOR sss_continue")));
+ DBUG_SET("-d,hold_sss_with_err_lock");
+ });
+
protocol->store_string_or_null(mi->host, &my_charset_bin);
protocol->store_string_or_null(mi->user, &my_charset_bin);
protocol->store((uint32) mi->port);
@@ -3279,7 +3286,8 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
while the slave is processing ignored events, such as those skipped
due to slave_skip_counter.
*/
- if (mi->using_parallel() && idle && !mi->rli.parallel.workers_idle())
+ if (mi->using_parallel() && idle &&
+ !rpl_parallel::workers_idle(&mi->rli))
idle= false;
}
if (idle)
@@ -4446,6 +4454,15 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
{
Gtid_log_event *gev= static_cast<Gtid_log_event *>(ev);
+#ifdef ENABLED_DEBUG_SYNC
+ DBUG_EXECUTE_IF(
+ "pause_sql_thread_on_relay_fde_after_trans",
+ {
+ DBUG_SET("-d,pause_sql_thread_on_relay_fde_after_trans");
+ DBUG_SET("+d,pause_sql_thread_on_next_relay_fde");
+ });
+#endif
+
/*
For GTID, allocate a new sub_id for the given domain_id.
The sub_id must be allocated in increasing order of binlog order.
@@ -4598,12 +4615,14 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
#endif /* WITH_WSREP */
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF(
- "pause_sql_thread_on_fde",
- if (ev && typ == FORMAT_DESCRIPTION_EVENT) {
+ "pause_sql_thread_on_next_relay_fde",
+ if (ev && typ == FORMAT_DESCRIPTION_EVENT &&
+ ((Format_description_log_event *) ev)->is_relay_log_event()) {
DBUG_ASSERT(!debug_sync_set_action(
thd,
STRING_WITH_LEN(
"now SIGNAL paused_on_fde WAIT_FOR sql_thread_continue")));
+ DBUG_SET("-d,pause_sql_thread_on_next_relay_fde");
});
#endif
@@ -4720,6 +4739,7 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
sql_print_information("%s", messages[SLAVE_RECON_MSG_KILLED_AFTER]);
return 1;
}
+ repl_semisync_slave.slave_reconnect(mi);
return 0;
}
@@ -4808,14 +4828,7 @@ pthread_handler_t handle_slave_io(void *arg)
}
thd->variables.wsrep_on= 0;
- if (DBUG_IF("failed_slave_start")
- || repl_semisync_slave.slave_start(mi))
- {
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Failed to run 'thread_start' hook");
- goto err;
- }
+ repl_semisync_slave.slave_start(mi);
if (!(mi->mysql = mysql = mysql_init(NULL)))
{
@@ -4909,6 +4922,7 @@ connected:
if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
reconnect_messages[SLAVE_RECON_ACT_REG]))
goto err;
+
goto connected;
}
@@ -4932,7 +4946,13 @@ connected:
goto err;
goto connected;
}
- DBUG_EXECUTE_IF("fail_com_register_slave", goto err;);
+ DBUG_EXECUTE_IF("fail_com_register_slave",
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, NULL,
+ ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE",
+ "Debug Induced Error");
+ goto err;
+ });
}
DBUG_PRINT("info",("Starting reading binary log from master"));
@@ -4966,6 +4986,15 @@ connected:
we're in fact receiving nothing.
*/
THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event);
+
+#ifdef ENABLED_DEBUG_SYNC
+ DBUG_EXECUTE_IF("pause_before_io_read_event",
+ {
+ DBUG_ASSERT(!debug_sync_set_action( thd, STRING_WITH_LEN(
+ "now signal io_thread_at_read_event wait_for io_thread_continue_read_event")));
+ DBUG_SET("-d,pause_before_io_read_event");
+ };);
+#endif
event_len= read_event(mysql, mi, &suppress_warnings, &network_read_len);
if (check_io_slave_killed(mi, NullS))
goto err;
@@ -5065,17 +5094,36 @@ Stopping slave I/O thread due to out-of-memory error from master");
goto err;
}
- if (rpl_semi_sync_slave_status && (mi->semi_ack & SEMI_SYNC_NEED_ACK))
+ if (repl_semisync_slave.get_slave_enabled() &&
+ mi->semi_sync_reply_enabled &&
+ (mi->semi_ack & SEMI_SYNC_NEED_ACK))
{
- /*
- We deliberately ignore the error in slave_reply, such error should
- not cause the slave IO thread to stop, and the error messages are
- already reported.
- */
- DBUG_EXECUTE_IF("simulate_delay_semisync_slave_reply", my_sleep(800000););
- (void)repl_semisync_slave.slave_reply(mi);
+ DBUG_EXECUTE_IF("simulate_delay_semisync_slave_reply",
+ my_sleep(800000););
+ if (repl_semisync_slave.slave_reply(mi))
+ {
+ /*
+ Master is not responding (gone away?) or it has turned semi sync
+ off. Turning off semi-sync responses as there is no point in sending
+ data to the master if the master not receiving the messages.
+ This also stops the logs from getting filled with
+ "Semi-sync slave net_flush() reply failed" messages.
+ On reconnect semi sync will be turned on again, if the
+ master has semi-sync enabled.
+
+ We check mi->abort_slave to see if the io thread was
+ killed and in this case we do not need an error message as
+ we know what is going on.
+ */
+ if (!mi->abort_slave)
+ sql_print_error("Master server does not read semi-sync messages "
+ "last_error: %s (%d). "
+ "Fallback to asynchronous replication",
+ mi->mysql->net.last_error,
+ mi->mysql->net.last_errno);
+ mi->semi_sync_reply_enabled= 0;
+ }
}
-
if (mi->using_gtid == Master_info::USE_GTID_NO &&
/*
If rpl_semi_sync_slave_delay_master is enabled, we will flush
@@ -5513,19 +5561,25 @@ pthread_handler_t handle_slave_sql(void *arg)
}
else
rli->gtid_skip_flag = GTID_SKIP_NOT;
+ mysql_mutex_lock(&rli->data_lock);
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
- 1 /*need data lock*/, &errmsg,
+ 0 /*need data lock*/, &errmsg,
1 /*look for a description_event*/))
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
"Error initializing relay log position: %s", errmsg);
+ mysql_mutex_unlock(&rli->data_lock);
goto err_before_start;
}
rli->reset_inuse_relaylog();
if (rli->alloc_inuse_relaylog(rli->group_relay_log_name))
+ {
+ mysql_mutex_unlock(&rli->data_lock);
goto err_before_start;
+ }
+ mysql_mutex_unlock(&rli->data_lock);
strcpy(rli->future_event_master_log_name, rli->group_master_log_name);
THD_CHECK_SENTRY(thd);
@@ -6948,7 +7002,7 @@ dbug_gtid_accept:
*/
mi->do_accept_own_server_id=
(s_id == global_system_variables.server_id &&
- rpl_semi_sync_slave_enabled && opt_gtid_strict_mode &&
+ repl_semisync_slave.get_slave_enabled() && opt_gtid_strict_mode &&
mi->using_gtid != Master_info::USE_GTID_NO &&
!mysql_bin_log.check_strict_gtid_sequence(event_gtid.domain_id,
event_gtid.server_id,
diff --git a/sql/socketpair.c b/sql/socketpair.c
new file mode 100644
index 00000000..ef89fa04
--- /dev/null
+++ b/sql/socketpair.c
@@ -0,0 +1,156 @@
+/* socketpair.c
+Copyright 2007, 2010 by Nathan C. Myers <ncm@cantrip.org>
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ The name of the author must not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Changes:
+ * 2023-12-25 Addopted for MariaDB usage
+ * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements
+ * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54
+ * github.com/GerHobbelt/selectable-socketpair
+ * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64
+ * and UNIX/other platforms
+ * 2013-07-18: Change to BSD 3-clause license
+ * 2010-03-31:
+ * set addr to 127.0.0.1 because win32 getsockname does not always set it.
+ * 2010-02-25:
+ * set SO_REUSEADDR option to avoid leaking some windows resource.
+ * Windows System Error 10049, "Event ID 4226 TCP/IP has reached
+ * the security limit imposed on the number of concurrent TCP connect
+ * attempts." Bleah.
+ * 2007-04-25:
+ * preserve value of WSAGetLastError() on all error returns.
+ * 2007-04-22: (Thanks to Matthew Gregan <kinetik@flim.org>)
+ * s/EINVAL/WSAEINVAL/ fix trivial compile failure
+ * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout
+ * of a child process.
+ * add argument make_overlapped
+ */
+
+#include <my_global.h>
+#ifdef _WIN32
+#include <ws2tcpip.h> /* socklen_t, et al (MSVC20xx) */
+#include <windows.h>
+#include <io.h>
+#include "socketpair.h"
+
+#define safe_errno (errno != 0) ? errno : -1
+
+/**
+ create_socketpair()
+
+ @param socks[2] Will be filled by 2 SOCKET entries (similar to pipe())
+ socks[0] for reading
+ socks[1] for writing
+
+ @return: 0 ok
+ # System error code. -1 if unknown
+ */
+
+int create_socketpair(SOCKET socks[2])
+{
+ union
+ {
+ struct sockaddr_in inaddr;
+ struct sockaddr addr;
+ } a;
+ SOCKET listener= -1;
+ int reuse = 1;
+ int last_error;
+ socklen_t addrlen = sizeof(a.inaddr);
+
+ socks[0]= socks[1]= -1;
+
+ if ((listener= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
+ return safe_errno;
+
+ memset(&a, 0, sizeof(a));
+ a.inaddr.sin_family = AF_INET;
+ a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ a.inaddr.sin_port = 0;
+
+ for (;;) /* Avoid using goto */
+ {
+ if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
+ (char*) &reuse, (socklen_t) sizeof(reuse)) == -1)
+ break;
+ if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
+ break;
+
+ memset(&a, 0, sizeof(a));
+ if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
+ break;
+ // win32 getsockname may only set the port number, p=0.0005.
+ // ( http://msdn.microsoft.com/library/ms738543.aspx ):
+ a.inaddr.sin_addr.s_addr= htonl(INADDR_LOOPBACK);
+ a.inaddr.sin_family= AF_INET;
+
+ if (listen(listener, 1) == SOCKET_ERROR)
+ break;
+
+ socks[1]= socket(AF_INET, SOCK_STREAM, 0);
+ if (socks[1] == -1)
+ break;
+ if (connect(socks[1], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
+ break;
+
+ socks[0]= accept(listener, NULL, NULL);
+ if (socks[0] == -1)
+ break;
+
+ closesocket(listener);
+
+ {
+ /* Make both sockets non blocking */
+ ulong arg= 1;
+ ioctlsocket(socks[0], FIONBIO,(void*) &arg);
+ ioctlsocket(socks[1], FIONBIO,(void*) &arg);
+ }
+ return 0;
+ }
+ /* Error handling */
+ last_error= WSAGetLastError();
+ if (listener != -1)
+ closesocket(listener);
+ close_socketpair(socks);
+ WSASetLastError(last_error);
+
+ return last_error;
+}
+
+/*
+ Free socketpair
+*/
+
+void close_socketpair(SOCKET socks[2])
+{
+ if (socks[0] != -1)
+ closesocket(socks[0]);
+ if (socks[1] != -1)
+ closesocket(socks[1]);
+ socks[0]= socks[1]= -1;
+}
+
+#endif /*_WIN32 */
diff --git a/sql/socketpair.h b/sql/socketpair.h
new file mode 100644
index 00000000..d9f89c84
--- /dev/null
+++ b/sql/socketpair.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2023, MariaDB Plc
+
+ 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 */
+
+#ifdef _WIN32
+C_MODE_START
+ int create_socketpair(SOCKET socks[2]);
+ void close_socketpair(SOCKET socks[2]);
+C_MODE_END
+#endif /* _WIN32 */
diff --git a/sql/sp.cc b/sql/sp.cc
index f51851d1..32353435 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -2731,7 +2731,13 @@ sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
for (uint i=0 ; i < src->records ; i++)
{
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
- (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
+ DBUG_ASSERT(thd->active_stmt_arena_to_use()->
+ is_stmt_prepare_or_first_stmt_execute() ||
+ thd->active_stmt_arena_to_use()->
+ is_conventional() ||
+ thd->active_stmt_arena_to_use()->state ==
+ Query_arena::STMT_SP_QUERY_ARGUMENTS);
+ (void)sp_add_used_routine(prelocking_ctx, thd->active_stmt_arena_to_use(),
&rt->mdl_request.key, rt->m_handler,
belong_to_view);
}
@@ -2757,7 +2763,7 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *belong_to_view)
{
for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next)
- (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
+ (void)sp_add_used_routine(prelocking_ctx, thd->active_stmt_arena_to_use(),
&rt->mdl_request.key, rt->m_handler,
belong_to_view);
}
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index 36ad3710..1da807e9 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -195,7 +195,7 @@ sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name)
sp_cache *c= *cp;
if (! c)
return NULL;
- return c->lookup(buf, name->make_qname(buf, sizeof(buf)));
+ return c->lookup(buf, name->make_qname(buf, sizeof(buf), true));
}
@@ -302,7 +302,7 @@ sp_cache::~sp_cache()
void
sp_cache::init()
{
- my_hash_init(key_memory_sp_cache, &m_hashtable, system_charset_info, 0, 0, 0,
+ my_hash_init(key_memory_sp_cache, &m_hashtable, &my_charset_bin, 0, 0, 0,
hash_get_key_for_sp_head, hash_free_sp_head, 0);
}
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 5fd6ab52..58235055 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1575,7 +1575,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
{
// Don't count a call ended with an error as normal run
executed_counter= 0;
- main_mem_root.read_only= 0;
+ main_mem_root.flags &= ~ROOT_FLAG_READ_ONLY;
reset_instrs_executed_counter();
}
#endif
@@ -1696,10 +1696,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
#ifdef PROTECT_STATEMENT_MEMROOT
if (!err_status)
{
- if (!main_mem_root.read_only &&
+ if (!(main_mem_root.flags & ROOT_FLAG_READ_ONLY) &&
has_all_instrs_executed())
{
- main_mem_root.read_only= 1;
+ main_mem_root.flags |= ROOT_FLAG_READ_ONLY;
}
++executed_counter;
DBUG_PRINT("info", ("execute counter: %lu", executed_counter));
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 029b12ad..c6080e5b 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -957,6 +957,7 @@ class User_table_tabular: public User_table
int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
{
+ mysql_mutex_assert_owner(&acl_cache->lock);
u->alloc_auth(root, 1);
if (have_password())
{
@@ -2316,6 +2317,9 @@ static bool validate_password(THD *thd, const LEX_CSTRING &user,
static int set_user_salt(ACL_USER::AUTH *auth, plugin_ref plugin)
{
st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
if (info->interface_version >= 0x0202 && info->preprocess_hash &&
auth->auth_string.length)
{
@@ -2351,6 +2355,8 @@ static int set_user_auth(THD *thd, const LEX_CSTRING &user,
plugin_ref plugin= get_auth_plugin(thd, auth->plugin, &unlock_plugin);
int res= 1;
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
if (!plugin)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
@@ -2427,10 +2433,13 @@ static bool set_user_salt_if_needed(ACL_USER *user_copy, int curr_auth,
if (auth_copy->salt.str)
return 0; // already done
+ mysql_mutex_lock(&acl_cache->lock);
if (set_user_salt(auth_copy, plugin))
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
return 1;
+ }
- mysql_mutex_lock(&acl_cache->lock);
ACL_USER *user= find_user_exact(user_copy->host.hostname, user_copy->user.str);
// make sure the user wasn't altered or dropped meanwhile
if (user)
@@ -3403,10 +3412,18 @@ end:
check_role_is_granted_callback,
NULL) == -1))
{
- /* Role is not granted but current user can see the role */
- my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
- MYF(0), thd->security_ctx->priv_user,
- thd->security_ctx->priv_host, rolename);
+ /* This happens for SET ROLE case and when `--skip-name-resolve` option
+ is used. In that situation host can be NULL and current user is always
+ target user, so printing `priv_user@priv_host` is not incorrect.
+ */
+ if (!host)
+ my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
+ MYF(0), thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host, rolename);
+ else
+ /* Role is not granted but current user can see the role */
+ my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
+ MYF(0), user, host, rolename);
}
else
{
@@ -3477,6 +3494,7 @@ ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo,
const Account_options &options,
const privilege_t privileges)
{
+ mysql_mutex_assert_owner(&acl_cache->lock);
user= safe_lexcstrdup_root(&acl_memroot, combo.user);
update_hostname(&host, safe_strdup_root(&acl_memroot, combo.host.str));
hostname_length= combo.host.length;
@@ -3493,6 +3511,8 @@ static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
const privilege_t privileges)
{
ACL_USER_PARAM::AUTH *work_copy= NULL;
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
if (nauth)
{
if (!(work_copy= (ACL_USER_PARAM::AUTH*)
@@ -5201,6 +5221,7 @@ update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
return 0;
}
+ mysql_mutex_assert_owner(&acl_cache->lock);
/* 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,
@@ -5265,6 +5286,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
DBUG_ENTER("replace_proxies_priv_table");
+ mysql_mutex_assert_owner(&acl_cache->lock);
if (!table)
{
my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
@@ -8373,11 +8395,6 @@ bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
INSERT_ACL : SELECT_ACL);
}
- if (tl->with || !tl->db.str ||
- (tl->select_lex &&
- (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
- continue;
-
const ACL_internal_table_access *access=
get_cached_table_access(&t_ref->grant.m_internal,
t_ref->get_db_name(),
@@ -12159,8 +12176,8 @@ static my_bool count_column_grants(void *grant_table,
This must be performed under the mutex in order to make sure the
iteration does not fail.
*/
-static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_column_grants(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum enum_var_type scope)
{
var->type= SHOW_ULONG;
var->value= buff;
@@ -12176,8 +12193,8 @@ static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+static int show_database_grants(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum enum_var_type scope)
{
var->type= SHOW_UINT;
var->value= buff;
@@ -13568,8 +13585,37 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
DBUG_RETURN(0);
}
+
+/**
+ Determine if the client is MySQL Connector/NET.
+
+ Checks whether the given connection attributes blob corresponds to
+ MySQL Connector/NET by examining the "_client_name" attribute, which is
+ expected to be the first attribute in the blob.
+
+ @param connection_attrs - The connection attributes blob.
+ @param length - The length of the blob.
+
+ @return true if the client is MySQL Connector/NET, false otherwise.
+*/
+static inline bool is_connector_net_client(const char *connection_attrs,
+ size_t length)
+{
+ constexpr LEX_CSTRING prefix=
+ {STRING_WITH_LEN("\x0c_client_name\x13mysql-connector-net")};
+
+ if (length < prefix.length)
+ return false;
+
+ /* Optimization to avoid following memcmp in common cases.*/
+ if (connection_attrs[prefix.length - 1] != prefix.str[prefix.length - 1])
+ return false;
+
+ return !memcmp(connection_attrs, prefix.str, prefix.length);
+}
+
static bool
-read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
+read_client_connect_attrs(char **ptr, char *end, THD* thd)
{
ulonglong length;
char *ptr_save= *ptr;
@@ -13592,10 +13638,14 @@ read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
if (length > 65535)
return true;
- if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
+ if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, thd->charset()) &&
current_thd->variables.log_warnings)
sql_print_warning("Connection attributes of length %llu were truncated",
length);
+
+ /* Connector/Net crashes, when "show collations" returns NULL IDs*/
+ if (is_connector_net_client(*ptr, length))
+ thd->variables.old_behavior |= OLD_MODE_NO_NULL_COLLATION_IDS;
return false;
}
@@ -13729,7 +13779,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
}
if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
- read_client_connect_attrs(&next_field, end, thd->charset()))
+ read_client_connect_attrs(&next_field, end, thd))
{
my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
MYF(0));
@@ -13979,7 +14029,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
- mpvio->auth_info.thd->charset()))
+ mpvio->auth_info.thd))
return packet_error;
/*
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index fcbd8a55..a3b9bbd4 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -818,7 +818,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (lock_type == TL_WRITE && table->mdl_request.type > MDL_SHARED_WRITE)
{
if (table->table->s->tmp_table)
- thd->close_unused_temporary_table_instances(tables);
+ thd->close_unused_temporary_table_instances(table);
else
{
/* Store information about table for ddl log */
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index 3b9c8402..ff7b0be7 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -641,19 +641,19 @@ bool Sql_cmd_alter_table::execute(THD *thd)
}
wsrep::key_array keys;
- wsrep_append_fk_parent_table(thd, first_table, &keys);
-
- WSREP_TO_ISOLATION_BEGIN_ALTER(lex->name.str ? select_lex->db.str
- : first_table->db.str,
- lex->name.str ? lex->name.str
- : first_table->table_name.str,
- first_table, &alter_info, &keys,
- used_engine ? &create_info : nullptr)
+ if (!wsrep_append_fk_parent_table(thd, first_table, &keys))
{
- WSREP_WARN("ALTER TABLE isolation failure");
- DBUG_RETURN(TRUE);
+ WSREP_TO_ISOLATION_BEGIN_ALTER(lex->name.str ? select_lex->db.str
+ : first_table->db.str,
+ lex->name.str ? lex->name.str
+ : first_table->table_name.str,
+ first_table, &alter_info, &keys,
+ used_engine ? &create_info : nullptr)
+ {
+ WSREP_WARN("ALTER TABLE isolation failure");
+ DBUG_RETURN(TRUE);
+ }
}
-
DEBUG_SYNC(thd, "wsrep_alter_table_after_toi");
}
#endif
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index 4c853689..93b0dbb3 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -953,7 +953,8 @@ void field_longlong::get_opt_type(String *answer,
UINT_MAX24 : INT_MAX24))
snprintf(buff, sizeof(buff), "MEDIUMINT(%d)", (int) max_length);
else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ?
- UINT_MAX32 : INT_MAX32))
+ (longlong) UINT_MAX32 :
+ (longlong) INT_MAX32))
snprintf(buff, sizeof(buff), "INT(%d)", (int) max_length);
else
snprintf(buff, sizeof(buff), "BIGINT(%d)", (int) max_length);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index a0656e48..7b9ffc2e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -74,7 +74,9 @@ No_such_table_error_handler::handle_condition(THD *,
*cond_hdl= NULL;
if (!first_error)
first_error= sql_errno;
- if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE)
+ if (sql_errno == ER_NO_SUCH_TABLE
+ || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE
+ || sql_errno == ER_UNKNOWN_SEQUENCES)
{
m_handled_errors++;
return TRUE;
@@ -2288,6 +2290,7 @@ retry_share:
if (thd->has_read_only_protection())
{
MYSQL_UNBIND_TABLE(table->file);
+ table->vcol_cleanup_expr(thd);
tc_release_table(table);
DBUG_RETURN(TRUE);
}
@@ -2307,6 +2310,7 @@ retry_share:
if (result)
{
MYSQL_UNBIND_TABLE(table->file);
+ table->vcol_cleanup_expr(thd);
tc_release_table(table);
DBUG_RETURN(TRUE);
}
@@ -7202,6 +7206,7 @@ set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref)
if (!(context= new (thd->mem_root) Name_resolution_context))
return TRUE;
context->init();
+ context->select_lex= table_ref->select_lex;
context->first_name_resolution_table=
context->last_name_resolution_table= table_ref;
item->context= context;
@@ -8020,7 +8025,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
while ((item= it++))
{
if (make_pre_fix)
- pre_fix->push_back(item, thd->stmt_arena->mem_root);
+ pre_fix->push_back(item, thd->active_stmt_arena_to_use()->mem_root);
if (item->fix_fields_if_needed_for_scalar(thd, it.ref()))
{
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index e71c7015..118b361e 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -137,7 +137,7 @@ int binlog_defragment(THD *thd)
entry[k]=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name[k].str,
name[k].length);
- if (!entry[k] || entry[k]->type != STRING_RESULT)
+ if (!entry[k] || entry[k]->type_handler()->result_type() != STRING_RESULT)
{
my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), name[k].str);
return -1;
@@ -162,7 +162,8 @@ int binlog_defragment(THD *thd)
gathered_length += entry[k]->length;
}
for (uint k=0; k < 2; k++)
- update_hash(entry[k], true, NULL, 0, STRING_RESULT, &my_charset_bin, 0);
+ update_hash(entry[k], true, NULL, 0,
+ &type_handler_long_blob, &my_charset_bin);
DBUG_ASSERT(gathered_length == thd->lex->comment.length);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9da1ec54..17d89188 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -727,7 +727,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
wsrep_wfc()
#endif /*WITH_WSREP */
{
- ulong tmp;
bzero(&variables, sizeof(variables));
/*
@@ -879,14 +878,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
tablespace_op=FALSE;
- /*
- Initialize the random generator. We call my_rnd() without a lock as
- it's not really critical if two threads modifies the structure at the
- same time. We ensure that we have an unique number foreach thread
- by adding the address of the stack.
- */
- tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff);
- my_rnd_init(&rand, tmp + (ulong)((size_t) &rand), tmp + (ulong) ::global_query_id);
substitute_null_with_insert_id = FALSE;
lock_info.mysql_thd= (void *)this;
@@ -1247,7 +1238,9 @@ void THD::init()
user_time.val= start_time= start_time_sec_part= 0;
- server_status= SERVER_STATUS_AUTOCOMMIT;
+ server_status= 0;
+ if (variables.option_bits & OPTION_AUTOCOMMIT)
+ server_status|= SERVER_STATUS_AUTOCOMMIT;
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
if (variables.sql_mode & MODE_ANSI_QUOTES)
@@ -1312,6 +1305,17 @@ void THD::init()
/* Set to handle counting of aborted connections */
userstat_running= opt_userstat_running;
last_global_update_time= current_connect_time= time(NULL);
+
+ /*
+ Initialize the random generator. We call my_rnd() without a lock as
+ it's not really critical if two threads modify the structure at the
+ same time. We ensure that we have a unique number for each thread
+ by adding the address of this THD.
+ */
+ ulong tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff);
+ my_rnd_init(&rand, tmp + (ulong)(intptr) this,
+ (ulong)(my_timer_cycles() + global_query_id));
+
#ifndef EMBEDDED_LIBRARY
session_tracker.enable(this);
#endif //EMBEDDED_LIBRARY
@@ -1604,6 +1608,10 @@ void THD::free_connection()
vio_delete(net.vio);
net.vio= nullptr;
net_end(&net);
+ delete(rgi_fake);
+ rgi_fake= NULL;
+ delete(rli_fake);
+ rli_fake= NULL;
#endif
if (!cleanup_done)
cleanup();
@@ -1642,6 +1650,7 @@ void THD::reset_for_reuse()
abort_on_warning= 0;
free_connection_done= 0;
m_command= COM_CONNECT;
+ proc_info= "login"; // Same as in THD::THD()
transaction->on= 1;
#if defined(ENABLED_PROFILING)
profiling.reset();
@@ -1654,6 +1663,7 @@ void THD::reset_for_reuse()
wsrep_cs().reset_error();
wsrep_aborter= 0;
wsrep_abort_by_kill= NOT_KILLED;
+ my_free(wsrep_abort_by_kill_err);
wsrep_abort_by_kill_err= 0;
#ifndef DBUG_OFF
wsrep_killed_state= 0;
@@ -1696,6 +1706,8 @@ THD::~THD()
#ifdef WITH_WSREP
mysql_cond_destroy(&COND_wsrep_thd);
+ my_free(wsrep_abort_by_kill_err);
+ wsrep_abort_by_kill_err= 0;
#endif
mdl_context.destroy();
@@ -1708,17 +1720,6 @@ THD::~THD()
dbug_sentry= THD_SENTRY_GONE;
#endif
#ifndef EMBEDDED_LIBRARY
- if (rgi_fake)
- {
- delete rgi_fake;
- rgi_fake= NULL;
- }
- if (rli_fake)
- {
- delete rli_fake;
- rli_fake= NULL;
- }
-
if (rgi_slave)
rgi_slave->cleanup_after_session();
my_free(semisync_info);
@@ -1726,6 +1727,7 @@ THD::~THD()
main_lex.free_set_stmt_mem_root();
free_root(&main_mem_root, MYF(0));
my_free(m_token_array);
+ my_free(killed_err);
main_da.free_memory();
if (tdc_hash_pins)
lf_hash_put_pins(tdc_hash_pins);
@@ -1857,14 +1859,6 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
*/
}
-#define SECONDS_TO_WAIT_FOR_KILL 2
-#if !defined(_WIN32) && defined(HAVE_SELECT)
-/* my_sleep() can wait for sub second times */
-#define WAIT_FOR_KILL_TRY_TIMES 20
-#else
-#define WAIT_FOR_KILL_TRY_TIMES 2
-#endif
-
/**
Awake a thread.
@@ -2142,7 +2136,11 @@ void THD::reset_killed()
mysql_mutex_assert_not_owner(&LOCK_thd_kill);
mysql_mutex_lock(&LOCK_thd_kill);
killed= NOT_KILLED;
- killed_err= 0;
+ if (unlikely(killed_err))
+ {
+ my_free(killed_err);
+ killed_err= 0;
+ }
mysql_mutex_unlock(&LOCK_thd_kill);
}
#ifdef WITH_WSREP
@@ -2153,6 +2151,7 @@ void THD::reset_killed()
mysql_mutex_assert_not_owner(&LOCK_thd_kill);
mysql_mutex_lock(&LOCK_thd_kill);
wsrep_abort_by_kill= NOT_KILLED;
+ my_free(wsrep_abort_by_kill_err);
wsrep_abort_by_kill_err= 0;
mysql_mutex_unlock(&LOCK_thd_kill);
}
@@ -5933,8 +5932,6 @@ void THD::set_examined_row_count(ha_rows count)
void THD::inc_sent_row_count(ha_rows count)
{
m_sent_row_count+= count;
- DBUG_EXECUTE_IF("debug_huge_number_of_examined_rows",
- m_examined_row_count= (ULONGLONG_MAX - 1000000););
MYSQL_SET_STATEMENT_ROWS_SENT(m_statement_psi, m_sent_row_count);
}
@@ -6998,7 +6995,8 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
{
DBUG_ENTER("binlog_prepare_pending_rows_event");
/* Pre-conditions */
- DBUG_ASSERT(table->s->table_map_id != ~0UL);
+ DBUG_ASSERT((table->s->table_map_id & MAX_TABLE_MAP_ID) != UINT32_MAX &&
+ (table->s->table_map_id & MAX_TABLE_MAP_ID) != 0);
/* Fetch the type code for the RowsEventT template parameter */
int const general_type_code= RowsEventT::TYPE_CODE;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index beb33d83..7543df39 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -201,6 +201,7 @@ enum enum_binlog_row_image {
#define OLD_MODE_UTF8_IS_UTF8MB3 (1 << 3)
#define OLD_MODE_IGNORE_INDEX_ONLY_FOR_JOIN (1 << 4)
#define OLD_MODE_COMPAT_5_1_CHECKSUM (1 << 5)
+#define OLD_MODE_NO_NULL_COLLATION_IDS (1 << 6)
extern char internal_table_name[2];
extern char empty_c_string[1];
@@ -281,9 +282,8 @@ typedef struct st_user_var_events
user_var_entry *user_var_event;
char *value;
size_t length;
- Item_result type;
+ const Type_handler *th;
uint charset_number;
- bool unsigned_flag;
} BINLOG_USER_VAR_EVENT;
/*
@@ -657,6 +657,15 @@ enum killed_type
KILL_TYPE_QUERY
};
+#define SECONDS_TO_WAIT_FOR_KILL 2
+#define SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL 10
+#if !defined(_WIN32) && defined(HAVE_SELECT)
+/* my_sleep() can wait for sub second times */
+#define WAIT_FOR_KILL_TRY_TIMES 20
+#else
+#define WAIT_FOR_KILL_TRY_TIMES 2
+#endif
+
#include "sql_lex.h" /* Must be here */
class Delayed_insert;
@@ -694,7 +703,6 @@ typedef struct system_variables
ulonglong max_statement_time;
ulonglong optimizer_switch;
ulonglong optimizer_trace;
- ulong optimizer_trace_max_mem_size;
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
@@ -758,6 +766,8 @@ typedef struct system_variables
ulong optimizer_use_condition_selectivity;
ulong optimizer_max_sel_arg_weight;
ulong optimizer_max_sel_args;
+ ulong optimizer_trace_max_mem_size;
+ ulong optimizer_adjust_secondary_key_costs;
ulong use_stat_tables;
double sample_percentage;
ulong histogram_size;
@@ -3204,6 +3214,17 @@ public:
*/
Query_arena *stmt_arena;
+ /**
+ Get either call or statement arena. In case some function is called from
+ within a query the call arena has to be used for a memory allocation,
+ else use the statement arena.
+ */
+ Query_arena *active_stmt_arena_to_use()
+ {
+ return (state == Query_arena::STMT_SP_QUERY_ARGUMENTS) ? this :
+ stmt_arena;
+ }
+
void *bulk_param;
/*
@@ -3435,7 +3456,11 @@ public:
{ return m_sent_row_count; }
ha_rows get_examined_row_count() const
- { return m_examined_row_count; }
+ {
+ DBUG_EXECUTE_IF("debug_huge_number_of_examined_rows",
+ return (ULONGLONG_MAX - 1000000););
+ return m_examined_row_count;
+ }
ulonglong get_affected_rows() const
{ return affected_rows; }
@@ -4608,7 +4633,8 @@ public:
The worst things that can happen is that we get
a suboptimal error message.
*/
- killed_err= (err_info*) alloc_root(&main_mem_root, sizeof(*killed_err));
+ if (!killed_err)
+ killed_err= (err_info*) my_malloc(PSI_INSTRUMENT_ME, sizeof(*killed_err), MYF(MY_WME));
if (likely(killed_err))
{
killed_err->no= killed_errno_arg;
@@ -5039,13 +5065,24 @@ public:
public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
- void set_command(enum enum_server_command command)
+ inline void set_command(enum enum_server_command command)
{
+ DBUG_ASSERT(command != COM_SLEEP);
m_command= command;
#ifdef HAVE_PSI_THREAD_INTERFACE
PSI_STATEMENT_CALL(set_thread_command)(m_command);
#endif
}
+ /* As sleep needs a bit of special handling, we have a special case for it */
+ inline void mark_connection_idle()
+ {
+ proc_info= 0;
+ m_command= COM_SLEEP;
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_STATEMENT_CALL(set_thread_command)(m_command);
+#endif
+ }
+
inline enum enum_server_command get_command() const
{ return m_command; }
@@ -7118,7 +7155,7 @@ public:
// this is needed for user_vars hash
-class user_var_entry
+class user_var_entry: public Type_handler_hybrid_field_type
{
CHARSET_INFO *m_charset;
public:
@@ -7127,8 +7164,6 @@ class user_var_entry
char *value;
size_t length;
query_id_t update_query_id, used_query_id;
- Item_result type;
- bool unsigned_flag;
double val_real(bool *null_value);
longlong val_int(bool *null_value) const;
@@ -7809,6 +7844,66 @@ public:
};
+class Identifier_chain2
+{
+ LEX_CSTRING m_name[2];
+public:
+ Identifier_chain2()
+ :m_name{Lex_cstring(), Lex_cstring()}
+ { }
+ Identifier_chain2(const LEX_CSTRING &a, const LEX_CSTRING &b)
+ :m_name{a, b}
+ { }
+
+ const LEX_CSTRING& operator [] (size_t i) const
+ {
+ return m_name[i];
+ }
+
+ static Identifier_chain2 split(const LEX_CSTRING &txt)
+ {
+ DBUG_ASSERT(txt.str[txt.length] == '\0'); // Expect 0-terminated input
+ const char *dot= strchr(txt.str, '.');
+ if (!dot)
+ return Identifier_chain2(Lex_cstring(), txt);
+ size_t length0= dot - txt.str;
+ Lex_cstring name0(txt.str, length0);
+ Lex_cstring name1(txt.str + length0 + 1, txt.length - length0 - 1);
+ return Identifier_chain2(name0, name1);
+ }
+
+ // Export as a qualified name string: 'db.name'
+ size_t make_qname(char *dst, size_t dstlen, bool casedn_part1) const
+ {
+ size_t res= my_snprintf(dst, dstlen, "%.*s.%.*s",
+ (int) m_name[0].length, m_name[0].str,
+ (int) m_name[1].length, m_name[1].str);
+ if (casedn_part1 && dstlen > m_name[0].length)
+ my_casedn_str(system_charset_info, dst + m_name[0].length + 1);
+ return res;
+ }
+
+ // Export as a qualified name string, allocate on mem_root.
+ LEX_CSTRING make_qname(MEM_ROOT *mem_root, bool casedn_part1) const
+ {
+ LEX_STRING dst;
+ /* format: [pkg + dot] + name + '\0' */
+ size_t dst_size= m_name[0].length + 1 /*dot*/ + m_name[1].length + 1/*\0*/;
+ if (unlikely(!(dst.str= (char*) alloc_root(mem_root, dst_size))))
+ return {NULL, 0};
+ if (!m_name[0].length)
+ {
+ DBUG_ASSERT(!casedn_part1); // Should not be called this way
+ dst.length= my_snprintf(dst.str, dst_size, "%.*s",
+ (int) m_name[1].length, m_name[1].str);
+ return {dst.str, dst.length};
+ }
+ dst.length= make_qname(dst.str, dst_size, casedn_part1);
+ return {dst.str, dst.length};
+ }
+};
+
+
/**
This class resembles the SQL Standard schema qualified object name:
<schema qualified name> ::= [ <schema name> <period> ] <qualified identifier>
@@ -7849,41 +7944,16 @@ public:
void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db,
const LEX_CSTRING &name);
- static Database_qualified_name split(const LEX_CSTRING &txt)
- {
- DBUG_ASSERT(txt.str[txt.length] == '\0'); // Expect 0-terminated input
- const char *dot= strchr(txt.str, '.');
- if (!dot)
- return Database_qualified_name(NULL, 0, txt.str, txt.length);
- size_t dblen= dot - txt.str;
- Lex_cstring db(txt.str, dblen);
- Lex_cstring name(txt.str + dblen + 1, txt.length - dblen - 1);
- return Database_qualified_name(db, name);
- }
-
// Export db and name as a qualified name string: 'db.name'
- size_t make_qname(char *dst, size_t dstlen) const
+ size_t make_qname(char *dst, size_t dstlen, bool casedn_name) const
{
- return my_snprintf(dst, dstlen, "%.*s.%.*s",
- (int) m_db.length, m_db.str,
- (int) m_name.length, m_name.str);
+ return Identifier_chain2(m_db, m_name).make_qname(dst, dstlen, casedn_name);
}
// Export db and name as a qualified name string, allocate on mem_root.
- bool make_qname(MEM_ROOT *mem_root, LEX_CSTRING *dst) const
+ LEX_CSTRING make_qname(MEM_ROOT *mem_root, bool casedn_name) const
{
- const uint dot= !!m_db.length;
- char *tmp;
- /* format: [database + dot] + name + '\0' */
- dst->length= m_db.length + dot + m_name.length;
- if (unlikely(!(dst->str= tmp= (char*) alloc_root(mem_root,
- dst->length + 1))))
- return true;
- snprintf(tmp, dst->length + 1, "%.*s%.*s%.*s",
- (int) m_db.length, (m_db.length ? m_db.str : ""),
- dot, ".",
- (int) m_name.length, m_name.str);
DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str));
- return false;
+ return Identifier_chain2(m_db, m_name).make_qname(mem_root, casedn_name);
}
bool make_package_routine_name(MEM_ROOT *mem_root,
@@ -7894,9 +7964,8 @@ public:
size_t length= package.length + 1 + routine.length + 1;
if (unlikely(!(tmp= (char *) alloc_root(mem_root, length))))
return true;
- m_name.length= my_snprintf(tmp, length, "%.*s.%.*s",
- (int) package.length, package.str,
- (int) routine.length, routine.str);
+ m_name.length= Identifier_chain2(package, routine).make_qname(tmp, length,
+ false);
m_name.str= tmp;
return false;
}
@@ -7925,7 +7994,7 @@ public:
{ }
LEX_CSTRING lex_cstring() const override
{
- size_t length= m_name->make_qname(err_buffer, sizeof(err_buffer));
+ size_t length= m_name->make_qname(err_buffer, sizeof(err_buffer), false);
return {err_buffer, length};
}
};
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 6b195ac9..8878c722 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1240,8 +1240,7 @@ void prepare_new_connection_state(THD* thd)
embedded server library.
TODO: refactor this to avoid code duplication there
*/
- thd->proc_info= 0;
- thd->set_command(COM_SLEEP);
+ thd->mark_connection_idle();
thd->init_for_queries();
if (opt_init_connect.length &&
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index ad385128..5b3db9bc 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -106,6 +106,7 @@ bool LEX::check_dependencies_in_with_clauses()
@param tables Points to the beginning of the sub-chain
@param tables_last Points to the address with the sub-chain barrier
+ @param excl_spec Ignore the definition with this spec
@details
The method resolves tables references to CTE from the chain of
@@ -147,7 +148,8 @@ bool LEX::check_dependencies_in_with_clauses()
*/
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
- TABLE_LIST **tables_last)
+ TABLE_LIST **tables_last,
+ st_select_lex_unit *excl_spec)
{
With_element *with_elem= 0;
@@ -156,7 +158,8 @@ bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
if (tbl->derived)
continue;
if (!tbl->db.str && !tbl->with)
- tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
+ tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
+ excl_spec);
if (!tbl->with) // no CTE matches table reference tbl
{
if (only_cte_resolution)
@@ -244,7 +247,7 @@ LEX::check_cte_dependencies_and_resolve_references()
return true;
if (!with_cte_resolution)
return false;
- if (resolve_references_to_cte(query_tables, query_tables_last))
+ if (resolve_references_to_cte(query_tables, query_tables_last, NULL))
return true;
return false;
}
@@ -388,6 +391,7 @@ bool With_element::check_dependencies_in_spec()
@param table The reference to the table that is looked for
@param barrier The barrier with element for the search
+ @param excl_spec Ignore the definition with this spec
@details
The function looks through the elements of this with clause trying to find
@@ -401,12 +405,15 @@ bool With_element::check_dependencies_in_spec()
*/
With_element *With_clause::find_table_def(TABLE_LIST *table,
- With_element *barrier)
+ With_element *barrier,
+ st_select_lex_unit *excl_spec)
{
for (With_element *with_elem= with_list.first;
with_elem != barrier;
with_elem= with_elem->next)
{
+ if (excl_spec && with_elem->spec == excl_spec)
+ continue;
if (my_strcasecmp(system_charset_info, with_elem->get_name_str(),
table->table_name.str) == 0 &&
!table->is_fqtn)
@@ -466,7 +473,7 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
top_unit->with_element &&
top_unit->with_element->get_owner() == with_clause)
barrier= top_unit->with_element;
- found= with_clause->find_table_def(tbl, barrier);
+ found= with_clause->find_table_def(tbl, barrier, NULL);
if (found)
break;
}
@@ -521,10 +528,11 @@ void With_element::check_dependencies_in_select(st_select_lex *sl,
{
With_clause *with_clause= sl->master_unit()->with_clause;
if (with_clause)
- tbl->with= with_clause->find_table_def(tbl, NULL);
+ tbl->with= with_clause->find_table_def(tbl, NULL, NULL);
if (!tbl->with)
tbl->with= owner->find_table_def(tbl,
- owner->with_recursive ? NULL : this);
+ owner->with_recursive ? NULL : this,
+ NULL);
}
if (!tbl->with)
tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
@@ -643,6 +651,8 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit,
{
check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map);
}
+ if ((sl= unit->fake_select_lex))
+ check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map);
}
@@ -1099,7 +1109,8 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex,
*/
lex->only_cte_resolution= old_lex->only_cte_resolution;
if (lex->resolve_references_to_cte(lex->query_tables,
- lex->query_tables_last))
+ lex->query_tables_last,
+ spec))
{
res= NULL;
goto err;
@@ -1199,7 +1210,7 @@ With_element::process_columns_of_derived_unit(THD *thd,
/* Rename the columns of the first select in the unit */
while ((item= it++, name= nm++))
{
- item->set_name(thd, *name);
+ lex_string_set(&item->name, name->str);
item->base_flags|= item_base_t::IS_EXPLICIT_NAME;
}
@@ -1279,14 +1290,14 @@ bool With_element::prepare_unreferenced(THD *thd)
sl= sl->next_select())
sl->context.outer_context= 0;
+ uint8 save_context_analysys_only= thd->lex->context_analysis_only;
thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
if (!spec->prepared &&
(spec->prepare(spec->derived, 0, 0) ||
process_columns_of_derived_unit(thd, spec) ||
check_duplicate_names(thd, first_sl->item_list, 1)))
rc= true;
-
- thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
+ thd->lex->context_analysis_only= save_context_analysys_only;
return rc;
}
@@ -1302,6 +1313,7 @@ bool With_element::is_anchor(st_select_lex *sel)
Search for the definition of the given table referred in this select node
@param table reference to the table whose definition is searched for
+ @param excl_spec ignore the definition with this spec
@details
The method looks for the definition of the table whose reference is occurred
@@ -1314,7 +1326,8 @@ bool With_element::is_anchor(st_select_lex *sel)
NULL - otherwise
*/
-With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
+With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table,
+ st_select_lex_unit *excl_spec)
{
With_element *found= NULL;
With_clause *containing_with_clause= NULL;
@@ -1331,7 +1344,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
With_clause *attached_with_clause= sl->get_with_clause();
if (attached_with_clause &&
attached_with_clause != containing_with_clause &&
- (found= attached_with_clause->find_table_def(table, NULL)))
+ (found= attached_with_clause->find_table_def(table, NULL, excl_spec)))
break;
master_unit= sl->master_unit();
outer_sl= master_unit->outer_select();
@@ -1341,7 +1354,8 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
containing_with_clause= with_elem->get_owner();
With_element *barrier= containing_with_clause->with_recursive ?
NULL : with_elem;
- if ((found= containing_with_clause->find_table_def(table, barrier)))
+ if ((found= containing_with_clause->find_table_def(table, barrier,
+ excl_spec)))
break;
if (outer_sl && !outer_sl->get_with_element())
break;
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
index 6a1f67d3..1da7c6c3 100644
--- a/sql/sql_cte.h
+++ b/sql/sql_cte.h
@@ -325,7 +325,8 @@ public:
friend
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
- TABLE_LIST **tables_last);
+ TABLE_LIST **tables_last,
+ st_select_lex_unit *excl_spec);
};
const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8;
@@ -425,7 +426,8 @@ public:
void move_anchors_ahead();
- With_element *find_table_def(TABLE_LIST *table, With_element *barrier);
+ With_element *find_table_def(TABLE_LIST *table, With_element *barrier,
+ st_select_lex_unit *excl_spec);
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 90194659..202dde11 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -534,7 +534,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
if (unlikely(error))
DBUG_RETURN(TRUE);
- if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
+ if ((select && select->check_quick(thd, safe_update, limit,
+ Item_func::BITMAP_ALL)) || !limit)
{
query_plan.set_impossible_where();
if (thd->lex->describe || thd->lex->analyze_stmt)
@@ -1268,6 +1269,13 @@ multi_delete::initialize_tables(JOIN *join)
{
TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update();
tables_to_delete_from|= tbl->table->map;
+
+ /*
+ Ensure that filesort re-reads the row from the engine before
+ delete is called.
+ */
+ join->map2table[tbl->table->tablenr]->keep_current_rowid= true;
+
if (delete_while_scanning &&
unique_table(thd, tbl, join->tables_list, 0))
{
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 4e42bcd3..afcbb7f8 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1243,6 +1243,9 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
goto err;
JOIN *join= unit->first_select()->join;
join->first_record= false;
+ if (join->zero_result_cause)
+ goto err;
+
for (uint i= join->top_join_tab_count;
i < join->top_join_tab_count + join->aggr_tables;
i++)
@@ -1351,6 +1354,10 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->get_unit()));
st_select_lex_unit *unit= derived->get_unit();
+ // reset item names to that saved after wildcard expansion in JOIN::prepare
+ for(st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select())
+ sl->restore_item_list_names();
+
derived->merged_for_insert= FALSE;
unit->unclean();
unit->types.empty();
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index f9932f11..34e77e37 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -667,7 +667,8 @@ SQL_SELECT *prepare_simple_select(THD *thd, Item *cond,
SQL_SELECT *res= make_select(table, 0, 0, cond, 0, 0, error);
if (unlikely(*error) ||
- (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR))) ||
+ (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR,
+ Item_func::BITMAP_ALL))) ||
(likely(res) && res->quick && unlikely(res->quick->reset())))
{
delete res;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 26431968..97e1c102 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -83,6 +83,7 @@
#include "rpl_rli.h"
#ifdef WITH_WSREP
+#include "wsrep_mysqld.h" /* wsrep_append_table_keys() */
#include "wsrep_trans_observer.h" /* wsrep_start_transction() */
#endif /* WITH_WSREP */
@@ -95,7 +96,8 @@ static void end_delayed_insert(THD *thd);
pthread_handler_t handle_delayed_insert(void *arg);
static void unlink_blobs(TABLE *table);
#endif
-static bool check_view_insertability(THD *thd, TABLE_LIST *view);
+static bool check_view_insertability(THD *thd, TABLE_LIST *view,
+ List<Item> &fields);
static int binlog_show_create_table_(THD *thd, TABLE *table,
Table_specification_st *create_info);
@@ -310,7 +312,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (check_key_in_view(thd, table_list) ||
(table_list->view &&
- check_view_insertability(thd, table_list)))
+ check_view_insertability(thd, table_list, fields)))
{
my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT");
DBUG_RETURN(-1);
@@ -591,7 +593,8 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
Open tables used for sub-selects or in stored functions, will also
cache these functions.
*/
- if (open_and_lock_tables(thd, table_list->next_global, TRUE,
+ if (table_list->next_global &&
+ open_and_lock_tables(thd, table_list->next_global, TRUE,
MYSQL_OPEN_IGNORE_ENGINE_STATS))
{
end_delayed_insert(thd);
@@ -1424,6 +1427,7 @@ abort:
check_view_insertability()
thd - thread handler
view - reference on VIEW
+ fields - fields used in insert
IMPLEMENTATION
A view is insertable if the folloings are true:
@@ -1439,7 +1443,8 @@ abort:
TRUE - can't be used for insert
*/
-static bool check_view_insertability(THD * thd, TABLE_LIST *view)
+static bool check_view_insertability(THD *thd, TABLE_LIST *view,
+ List<Item> &fields)
{
uint num= view->view->first_select_lex()->item_list.elements;
TABLE *table= view->table;
@@ -1450,6 +1455,8 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
uint32 *used_fields_buff= (uint32*)thd->alloc(used_fields_buff_size);
MY_BITMAP used_fields;
enum_column_usage saved_column_usage= thd->column_usage;
+ List_iterator_fast<Item> it(fields);
+ Item *ex;
DBUG_ENTER("check_key_in_view");
if (!used_fields_buff)
@@ -1478,6 +1485,17 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
/* simple SELECT list entry (field without expression) */
if (!(field= trans->item->field_for_view_update()))
{
+ // Do not check fields which we are not inserting into
+ while((ex= it++))
+ {
+ // The field used in the INSERT
+ if (ex->real_item()->field_for_view_update() ==
+ trans->item->field_for_view_update())
+ break;
+ }
+ it.rewind();
+ if (!ex)
+ continue;
thd->column_usage= saved_column_usage;
DBUG_RETURN(TRUE);
}
@@ -1492,11 +1510,12 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
}
thd->column_usage= saved_column_usage;
/* unique test */
- for (trans= trans_start; trans != trans_end; trans++)
+ while((ex= it++))
{
/* Thanks to test above, we know that all columns are of type Item_field */
- Item_field *field= (Item_field *)trans->item;
- /* check fields belong to table in which we are inserting */
+ DBUG_ASSERT(ex->real_item()->field_for_view_update()->type() ==
+ Item::FIELD_ITEM);
+ Item_field *field= (Item_field *)ex->real_item()->field_for_view_update();
if (field->field->table == table &&
bitmap_fast_test_and_set(&used_fields, field->field->field_index))
DBUG_RETURN(TRUE);
@@ -1780,7 +1799,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
/* Check if there is more uniq keys after field */
-static int last_uniq_key(TABLE *table,uint keynr)
+static int last_uniq_key(TABLE *table, const KEY *key, uint keynr)
{
/*
When an underlying storage engine informs that the unique key
@@ -1800,7 +1819,7 @@ static int last_uniq_key(TABLE *table,uint keynr)
return 0;
while (++keynr < table->s->keys)
- if (table->key_info[keynr].flags & HA_NOSAME)
+ if (key[keynr].flags & HA_NOSAME)
return 0;
return 1;
}
@@ -2115,8 +2134,27 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
tables which have ON UPDATE but have no ON DELETE triggers,
we just should not expose this fact to users by invoking
ON UPDATE triggers.
+
+ Note, TABLE_SHARE and TABLE see long uniques differently:
+ - TABLE_SHARE sees as HA_KEY_ALG_LONG_HASH and HA_NOSAME
+ - TABLE sees as usual non-unique indexes
*/
- if (last_uniq_key(table,key_nr) &&
+ bool is_long_unique= table->s->key_info &&
+ table->s->key_info[key_nr].algorithm ==
+ HA_KEY_ALG_LONG_HASH;
+ if ((is_long_unique ?
+ /*
+ We have a long unique. Test that there are no in-engine
+ uniques and the current long unique is the last long unique.
+ */
+ !(table->key_info[0].flags & HA_NOSAME) &&
+ last_uniq_key(table, table->s->key_info, key_nr) :
+ /*
+ We have a normal key - not a long unique.
+ Test is the current normal key is unique and
+ it is the last normal unique.
+ */
+ last_uniq_key(table, table->key_info, key_nr)) &&
!table->file->referenced_by_foreign_key() &&
(!table->triggers || !table->triggers->has_delete_triggers()))
{
@@ -2721,7 +2759,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
}
THD_STAGE_INFO(client_thd, stage_got_handler_lock);
if (client_thd->killed)
- goto error;
+ goto error2;
if (thd.killed)
{
/*
@@ -2746,7 +2784,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
my_message(thd.get_stmt_da()->sql_errno(),
thd.get_stmt_da()->message(), MYF(0));
}
- goto error;
+ goto error2;
}
}
share= table->s;
@@ -2775,11 +2813,14 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
&record, (uint) share->reclength,
&bitmap, (uint) share->column_bitmap_size*4,
NullS))
- goto error;
+ goto error2;
/* Copy the TABLE object. */
copy= new (copy_tmp) TABLE;
*copy= *table;
+ copy->vcol_refix_list.empty();
+ init_sql_alloc(key_memory_TABLE, &copy->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
/* We don't need to change the file handler here */
/* Assign the pointers for the field pointers array and the record. */
@@ -2863,11 +2904,15 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
bzero((char*) bitmap, share->column_bitmap_size * bitmaps_used);
copy->read_set= &copy->def_read_set;
copy->write_set= &copy->def_write_set;
+ move_root(client_thd->mem_root, &copy->mem_root);
+ free_root(&copy->mem_root, 0);
DBUG_RETURN(copy);
/* Got fatal error */
error:
+ free_root(&copy->mem_root, 0);
+error2:
tables_in_use--;
mysql_cond_signal(&cond); // Inform thread about abort
DBUG_RETURN(0);
@@ -5115,17 +5160,13 @@ bool select_create::send_eof()
thd->wsrep_trx_id(), thd->thread_id, thd->query_id);
/*
- append table level exclusive key for CTAS
+ For CTAS, append table level exclusive key for created table
+ and table level shared key for selected table.
*/
- wsrep_key_arr_t key_arr= {0, 0};
- wsrep_prepare_keys_for_isolation(thd,
- table_list->db.str,
- table_list->table_name.str,
- table_list,
- &key_arr);
- int rcode= wsrep_thd_append_key(thd, key_arr.keys, key_arr.keys_len,
- WSREP_SERVICE_KEY_EXCLUSIVE);
- wsrep_keys_free(&key_arr);
+ int rcode= wsrep_append_table_keys(thd, table_list, table_list,
+ WSREP_SERVICE_KEY_EXCLUSIVE);
+ rcode= rcode || wsrep_append_table_keys(thd, nullptr, select_tables,
+ WSREP_SERVICE_KEY_SHARED);
if (rcode)
{
DBUG_PRINT("wsrep", ("row key failed: %d", rcode));
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d9706020..e6db1456 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1459,7 +1459,7 @@ bool is_lex_native_function(const LEX_CSTRING *name)
bool is_native_function(THD *thd, const LEX_CSTRING *name)
{
- if (native_functions_hash.find(thd, *name))
+ if (mariadb_schema.find_native_function_builder(thd, *name))
return true;
if (is_lex_native_function(name))
@@ -2138,7 +2138,18 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
if (lex->parsing_options.lookup_keywords_after_qualifier)
next_state= MY_LEX_IDENT_OR_KEYWORD;
else
- next_state= MY_LEX_IDENT_START; // Next is ident (not keyword)
+ {
+ /*
+ Next is:
+ - A qualified func with a special syntax:
+ mariadb_schema.REPLACE('a','b','c')
+ mariadb_schema.SUSTRING('a',1,2)
+ mariadb_schema.TRIM('a')
+ - Or an identifier otherwise. No keyword lookup is done,
+ all keywords are treated as identifiers.
+ */
+ next_state= MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC;
+ }
if (!ident_map[(uchar) yyPeek()]) // Probably ` or "
next_state= MY_LEX_START;
return((int) c);
@@ -2582,7 +2593,12 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
We should now be able to handle:
[(global | local | session) .]variable_name
*/
- return scan_ident_sysvar(thd, &yylval->ident_cli);
+ return scan_ident_common(thd, &yylval->ident_cli,
+ GENERAL_KEYWORD_OR_FUNC_LPAREN);
+
+ case MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC:
+ return scan_ident_common(thd, &yylval->ident_cli,
+ QUALIFIED_SPECIAL_FUNC_LPAREN);
}
}
}
@@ -2604,7 +2620,64 @@ bool Lex_input_stream::get_7bit_or_8bit_ident(THD *thd, uchar *last_char)
}
-int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
+/*
+ Resolve special SQL functions that have a qualified syntax in sql_yacc.yy.
+ These functions are not listed in the native function registry
+ because of a special syntax, or a reserved keyword:
+
+ mariadb_schema.SUBSTRING('a' FROM 1 FOR 2) -- Special syntax
+ mariadb_schema.TRIM(BOTH ' ' FROM 'a') -- Special syntax
+ mariadb_schema.REPLACE('a','b','c') -- Verb keyword
+*/
+
+int Lex_input_stream::find_keyword_qualified_special_func(Lex_ident_cli_st *str,
+ uint length) const
+{
+ /*
+ There are many other special functions, see the following grammar rules:
+ function_call_keyword
+ function_call_nonkeyword
+ Here we resolve only those that have a qualified syntax to handle
+ different behavior in different @@sql_mode settings.
+
+ Other special functions do not work in qualified context:
+ SELECT mariadb_schema.year(now()); -- Function year is not defined
+ SELECT mariadb_schema.now(); -- Function now is not defined
+
+ We don't resolve TRIM_ORACLE here, because it does not have
+ a qualified syntax yet. Search for "trim_operands" in sql_yacc.yy
+ to find more comments.
+ */
+ static LEX_CSTRING funcs[]=
+ {
+ {STRING_WITH_LEN("SUBSTRING")},
+ {STRING_WITH_LEN("SUBSTR")},
+ {STRING_WITH_LEN("TRIM")},
+ {STRING_WITH_LEN("REPLACE")}
+ };
+
+ int tokval= find_keyword(str, length, true);
+ if (!tokval)
+ return 0;
+ for (size_t i= 0; i < array_elements(funcs); i++)
+ {
+ CHARSET_INFO *cs= system_charset_info;
+ /*
+ Check length equality to avoid non-ASCII variants
+ compared as equal to ASCII variants.
+ */
+ if (length == funcs[i].length &&
+ !cs->coll->strnncollsp(cs,
+ (const uchar *) m_tok_start, length,
+ (const uchar *) funcs[i].str, funcs[i].length))
+ return tokval;
+ }
+ return 0;
+}
+
+
+int Lex_input_stream::scan_ident_common(THD *thd, Lex_ident_cli_st *str,
+ Ident_mode mode)
{
uchar last_char;
uint length;
@@ -2618,10 +2691,41 @@ int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
next_state= MY_LEX_IDENT_SEP;
if (!(length= yyLength()))
return ABORT_SYM; // Names must be nonempty.
- if ((tokval= find_keyword(str, length, 0)))
- {
- yyUnget(); // Put back 'c'
- return tokval; // Was keyword
+
+ switch (mode) {
+ case GENERAL_KEYWORD_OR_FUNC_LPAREN:
+ /*
+ We can come here inside a system variable after "@@",
+ e.g. @@global.character_set_client.
+ We resolve all general purpose keywords here.
+
+ We can come here when LEX::parsing_options.lookup_keywords_after_qualifier
+ is true, i.e. within the "field_spec" Bison rule.
+ We need to resolve functions that have special rules inside sql_yacc.yy,
+ such as SUBSTR, REPLACE, TRIM, to make this work:
+ c2 varchar(4) GENERATED ALWAYS AS (mariadb_schema.substr(c1,1,4))
+ */
+ if ((tokval= find_keyword(str, length, last_char == '(')))
+ {
+ yyUnget(); // Put back 'c'
+ return tokval; // Was keyword
+ }
+ break;
+ case QUALIFIED_SPECIAL_FUNC_LPAREN:
+ /*
+ We come here after '.' in various contexts:
+ SELECT @@global.character_set_client;
+ SELECT t1.a FROM t1;
+ SELECT test.f1() FROM t1;
+ SELECT mariadb_schema.trim('a');
+ */
+ if (last_char == '(' &&
+ (tokval= find_keyword_qualified_special_func(str, length)))
+ {
+ yyUnget(); // Put back 'c'
+ return tokval; // Was keyword
+ }
+ break;
}
yyUnget(); // ptr points now after last token char
@@ -2960,6 +3064,7 @@ void st_select_lex::init_query()
tvc= 0;
versioned_tables= 0;
pushdown_select= 0;
+ orig_names_of_item_list_elems= 0;
}
void st_select_lex::init_select()
@@ -3011,6 +3116,7 @@ void st_select_lex::init_select()
versioned_tables= 0;
is_tvc_wrapper= false;
nest_flags= 0;
+ orig_names_of_item_list_elems= 0;
}
/*
@@ -3526,8 +3632,9 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
uint n_elems= get_cardinality_of_ref_ptrs_slice(order_group_num) * 5;
if (!ref_pointer_array.is_null())
return false;
- Item **array= static_cast<Item**>(thd->stmt_arena->alloc(sizeof(Item*) *
- n_elems));
+
+ Item **array= static_cast<Item**>(
+ thd->active_stmt_arena_to_use()->alloc(sizeof(Item*) * n_elems));
if (likely(array != NULL))
ref_pointer_array= Ref_ptr_array(array, n_elems);
return array == NULL;
@@ -4671,18 +4778,27 @@ 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)
{
+ Query_arena *active_arena= thd->active_stmt_arena_to_use();
+
DBUG_ENTER("st_select_lex::fix_prepare_information");
- if (!thd->stmt_arena->is_conventional() &&
+
+ if (!active_arena->is_conventional() &&
!(changed_elements & TOUCHED_SEL_COND))
{
Query_arena_stmt on_stmt_arena(thd);
changed_elements|= TOUCHED_SEL_COND;
+ /*
+ TODO: return after MDEV-33218 fix
+ DBUG_ASSERT(
+ active_arena->is_stmt_prepare_or_first_stmt_execute() ||
+ active_arena->state == Query_arena::STMT_SP_QUERY_ARGUMENTS);
+ */
if (group_list.first)
{
if (!group_list_ptrs)
{
- void *mem= thd->stmt_arena->alloc(sizeof(Group_list_ptrs));
- group_list_ptrs= new (mem) Group_list_ptrs(thd->stmt_arena->mem_root);
+ void *mem= active_arena->alloc(sizeof(Group_list_ptrs));
+ group_list_ptrs= new (mem) Group_list_ptrs(active_arena->mem_root);
}
group_list_ptrs->reserve(group_list.elements);
for (ORDER *order= group_list.first; order; order= order->next)
@@ -7342,7 +7458,8 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
name->m_name);
else
sp->init_sp_name(name);
- sp->make_qname(sp->get_main_mem_root(), &sp->m_qname);
+ if (!(sp->m_qname= sp->make_qname(sp->get_main_mem_root(), true)).str)
+ return NULL;
}
sphead= sp;
}
@@ -9229,7 +9346,7 @@ bool LEX::call_statement_start(THD *thd,
const Lex_ident_sys_st *proc)
{
Database_qualified_name q_db_pkg(db, pkg);
- Database_qualified_name q_pkg_proc(pkg, proc);
+ Identifier_chain2 q_pkg_proc(*pkg, *proc);
sp_name *spname;
sql_command= SQLCOM_CALL;
@@ -9247,7 +9364,7 @@ bool LEX::call_statement_start(THD *thd,
// Concat `pkg` and `name` to `pkg.name`
LEX_CSTRING pkg_dot_proc;
- if (q_pkg_proc.make_qname(thd->mem_root, &pkg_dot_proc) ||
+ if (!(pkg_dot_proc= q_pkg_proc.make_qname(thd->mem_root, false)).str ||
check_ident_length(&pkg_dot_proc) ||
!(spname= new (thd->mem_root) sp_name(db, &pkg_dot_proc, true)))
return true;
@@ -9313,7 +9430,8 @@ sp_package *LEX::create_package_start(THD *thd,
return NULL;
pkg->reset_thd_mem_root(thd);
pkg->init(this);
- pkg->make_qname(pkg->get_main_mem_root(), &pkg->m_qname);
+ if (!(pkg->m_qname= pkg->make_qname(pkg->get_main_mem_root(), true)).str)
+ return NULL;
sphead= pkg;
return pkg;
}
@@ -9376,6 +9494,136 @@ Item *LEX::make_item_func_sysdate(THD *thd, uint fsp)
}
+const Schema *
+LEX::find_func_schema_by_name_or_error(const Lex_ident_sys &schema,
+ const Lex_ident_sys &func)
+{
+ Schema *res= Schema::find_by_name(schema);
+ if (res)
+ return res;
+ Database_qualified_name qname(schema, func);
+ my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), ErrConvDQName(&qname).ptr());
+ return NULL;
+}
+
+
+Item *LEX::make_item_func_substr(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ const Lex_substring_spec_st &spec)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema= find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_substr(thd, spec) : NULL;
+}
+
+
+Item *LEX::make_item_func_substr(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ List<Item> *item_list)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ Schema *schema;
+ if (item_list &&
+ (item_list->elements == 2 || item_list->elements == 3) &&
+ (schema= Schema::find_by_name(schema_name)))
+ {
+ Item_args args(thd, *item_list);
+ Lex_substring_spec_st spec=
+ Lex_substring_spec_st::init(args.arguments()[0],
+ args.arguments()[1],
+ item_list->elements == 3 ?
+ args.arguments()[2] : NULL);
+ return schema->make_item_func_substr(thd, spec);
+ }
+ return make_item_func_call_generic(thd, schema_name, func_name, item_list);
+}
+
+
+Item *LEX::make_item_func_replace(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ Item *org,
+ Item *find,
+ Item *replace)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema= find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_replace(thd, org, find, replace) :
+ NULL;
+}
+
+
+Item *LEX::make_item_func_replace(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ List<Item> *item_list)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema;
+ if (item_list && item_list->elements == 3 &&
+ (schema= Schema::find_by_name(schema_name)))
+ {
+ Item_args args(thd, *item_list);
+ return schema->make_item_func_replace(thd, args.arguments()[0],
+ args.arguments()[1],
+ args.arguments()[2]);
+ }
+ return make_item_func_call_generic(thd, schema_name, func_name, item_list);
+}
+
+
+Item *LEX::make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ const Lex_trim_st &spec)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema= find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_trim(thd, spec) : NULL;
+}
+
+
+Item *LEX::make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ List<Item> *item_list)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema;
+ if (item_list && item_list->elements == 1 &&
+ (schema= Schema::find_by_name(schema_name)))
+ {
+ Item_args args(thd, *item_list);
+ Lex_trim spec(TRIM_BOTH, args.arguments()[0]);
+ return schema->make_item_func_trim(thd, spec);
+ }
+ return make_item_func_call_generic(thd, schema_name, func_name, item_list);
+}
+
+
bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table,
const LEX_CSTRING field_name)
{
@@ -9454,16 +9702,10 @@ Item *Lex_trim_st::make_item_func_trim_oracle(THD *thd) const
}
-Item *Lex_trim_st::make_item_func_trim(THD *thd) const
-{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- make_item_func_trim_oracle(thd) :
- make_item_func_trim_std(thd);
-}
-
-
-Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
- Lex_ident_cli_st *cname, List<Item> *args)
+Item *LEX::make_item_func_call_generic(THD *thd,
+ const Lex_ident_cli_st *cdb,
+ const Lex_ident_cli_st *cname,
+ List<Item> *args)
{
Lex_ident_sys db(thd, cdb), name(thd, cname);
if (db.is_null() || name.is_null())
@@ -9490,6 +9732,19 @@ Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
if (check_routine_name(&name))
return NULL;
+ return make_item_func_call_generic(thd, db, name, args);
+}
+
+
+Item *LEX::make_item_func_call_generic(THD *thd,
+ const Lex_ident_sys &db,
+ const Lex_ident_sys &name,
+ List<Item> *args)
+{
+ const Schema *schema= Schema::find_by_name(db);
+ if (schema)
+ return schema->make_item_func_call_native(thd, name, args);
+
Create_qfunc *builder= find_qualified_function_builder(thd);
DBUG_ASSERT(builder);
return builder->create_with_db(thd, &db, &name, true, args);
@@ -9510,7 +9765,7 @@ Item *LEX::make_item_func_call_generic(THD *thd,
static Lex_cstring dot(".", 1);
Lex_ident_sys db(thd, cdb), pkg(thd, cpkg), func(thd, cfunc);
Database_qualified_name q_db_pkg(db, pkg);
- Database_qualified_name q_pkg_func(pkg, func);
+ Identifier_chain2 q_pkg_func(pkg, func);
sp_name *qname;
if (db.is_null() || pkg.is_null() || func.is_null())
@@ -9527,7 +9782,7 @@ Item *LEX::make_item_func_call_generic(THD *thd,
// Concat `pkg` and `name` to `pkg.name`
LEX_CSTRING pkg_dot_func;
- if (q_pkg_func.make_qname(thd->mem_root, &pkg_dot_func) ||
+ if (!(pkg_dot_func= q_pkg_func.make_qname(thd->mem_root, false)).str ||
check_ident_length(&pkg_dot_func) ||
!(qname= new (thd->mem_root) sp_name(&db, &pkg_dot_func, true)))
return NULL;
@@ -9936,8 +10191,17 @@ bool Lex_order_limit_lock::set_to(SELECT_LEX *sel)
"CUBE/ROLLUP", "ORDER BY");
return TRUE;
}
+ for (ORDER *order= order_list->first; order; order= order->next)
+ (*order->item)->walk(&Item::change_context_processor, FALSE,
+ &sel->context);
sel->order_list= *(order_list);
}
+ if (limit.select_limit)
+ limit.select_limit->walk(&Item::change_context_processor, FALSE,
+ &sel->context);
+ if (limit.offset_limit)
+ limit.offset_limit->walk(&Item::change_context_processor, FALSE,
+ &sel->context);
sel->is_set_query_expr_tail= true;
return FALSE;
}
@@ -11191,6 +11455,72 @@ exit:
}
+/**
+ @brief
+ Save the original names of items from the item list.
+
+ @retval
+ true - if an error occurs
+ false - otherwise
+*/
+
+bool st_select_lex::save_item_list_names(THD *thd)
+{
+ if (orig_names_of_item_list_elems)
+ return false;
+
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ if (unlikely(!(orig_names_of_item_list_elems= new(thd->mem_root)
+ List<Lex_ident_sys>)))
+ return true;
+
+ List_iterator_fast<Item> li(item_list);
+ Item *item;
+
+ while ((item= li++))
+ {
+ Lex_ident_sys *name= new (thd->mem_root) Lex_ident_sys(thd, &item->name);
+ if (unlikely(!name ||
+ orig_names_of_item_list_elems->push_back(name, thd->mem_root)))
+ {
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ orig_names_of_item_list_elems= 0;
+ return true;
+ }
+ }
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ return false;
+}
+
+
+/**
+ @brief
+ Restore the name of each item in the item_list of this st_select_lex
+ from orig_names_of_item_list_elems.
+*/
+
+void st_select_lex::restore_item_list_names()
+{
+ if (!orig_names_of_item_list_elems)
+ return;
+
+ DBUG_ASSERT(item_list.elements == orig_names_of_item_list_elems->elements);
+
+ List_iterator_fast<Lex_ident_sys> it(*orig_names_of_item_list_elems);
+ Lex_ident_sys *new_name;
+ List_iterator_fast<Item> li(item_list);
+ Item *item;
+
+ while ((item= li++) && (new_name= it++))
+ lex_string_set( &item->name, new_name->str);
+}
+
bool LEX::stmt_install_plugin(const DDL_options_st &opt,
const Lex_ident_sys_st &name,
const LEX_CSTRING &soname)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index f548fbe5..3151f237 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -146,6 +146,11 @@ public:
struct Lex_ident_sys_st: public LEX_CSTRING
{
public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+ static void operator delete(void *ptr,size_t size) { TRASH_FREE(ptr, size); }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
+
bool copy_ident_cli(THD *thd, const Lex_ident_cli_st *str);
bool copy_keyword(THD *thd, const Lex_ident_cli_st *str);
bool copy_sys(THD *thd, const LEX_CSTRING *str);
@@ -179,6 +184,10 @@ public:
LEX_CSTRING tmp= {name, length};
set_valid_utf8(&tmp);
}
+ Lex_ident_sys(THD *thd, const LEX_CSTRING *str)
+ {
+ set_valid_utf8(str);
+ }
Lex_ident_sys & operator=(const Lex_ident_sys_st &name)
{
Lex_ident_sys_st::operator=(name);
@@ -1221,6 +1230,11 @@ public:
List<Field_pair> grouping_tmp_fields;
List<udf_func> udf_list; /* udf function calls stack */
List<Index_hint> *index_hints; /* list of USE/FORCE/IGNORE INDEX */
+ /*
+ This list is used to restore the names of items
+ from item_list after each execution of the statement.
+ */
+ List<Lex_ident_sys> *orig_names_of_item_list_elems;
List<List_item> save_many_values;
List<Item> *save_insert_list;
@@ -1438,6 +1452,9 @@ public:
bool straight_fl);
TABLE_LIST *convert_right_join();
List<Item>* get_item_list();
+ bool save_item_list_names(THD *thd);
+ void restore_item_list_names();
+
ulong get_table_join_options();
void set_lock_for_tables(thr_lock_type lock_type, bool for_update,
bool skip_locks);
@@ -1577,7 +1594,8 @@ public:
master_unit()->cloned_from->with_element :
master_unit()->with_element;
}
- With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
+ With_element *find_table_def_in_with_clauses(TABLE_LIST *table,
+ st_select_lex_unit * excl_spec);
bool check_unrestricted_recursive(bool only_standard_compliant);
bool check_subqueries_with_recursive_references();
void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list);
@@ -2461,6 +2479,15 @@ public:
void reduce_digest_token(uint token_left, uint token_right);
private:
+
+ enum Ident_mode
+ {
+ GENERAL_KEYWORD_OR_FUNC_LPAREN,
+ QUALIFIED_SPECIAL_FUNC_LPAREN
+ };
+
+ int scan_ident_common(THD *thd, Lex_ident_cli_st *str, Ident_mode mode);
+
/**
Set the echo mode.
@@ -2781,8 +2808,8 @@ private:
bool consume_comment(int remaining_recursions_permitted);
int lex_one_token(union YYSTYPE *yylval, THD *thd);
int find_keyword(Lex_ident_cli_st *str, uint len, bool function) const;
+ int find_keyword_qualified_special_func(Lex_ident_cli_st *str, uint len) const;
LEX_CSTRING get_token(uint skip, uint length);
- int scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str);
int scan_ident_start(THD *thd, Lex_ident_cli_st *str);
int scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
CHARSET_INFO **cs, my_lex_states *);
@@ -4128,8 +4155,42 @@ public:
Item *create_item_query_expression(THD *thd, st_select_lex_unit *unit);
Item *make_item_func_sysdate(THD *thd, uint fsp);
- Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db,
- Lex_ident_cli_st *name, List<Item> *args);
+
+ static const Schema *
+ find_func_schema_by_name_or_error(const Lex_ident_sys &schema_name,
+ const Lex_ident_sys &func_name);
+ Item *make_item_func_replace(THD *thd,
+ const Lex_ident_cli_st &schema_name,
+ const Lex_ident_cli_st &func_name,
+ Item *org, Item *find, Item *replace);
+ Item *make_item_func_replace(THD *thd,
+ const Lex_ident_cli_st &schema_name,
+ const Lex_ident_cli_st &func_name,
+ List<Item> *args);
+ Item *make_item_func_substr(THD *thd,
+ const Lex_ident_cli_st &schema_name,
+ const Lex_ident_cli_st &func_name,
+ const Lex_substring_spec_st &spec);
+ Item *make_item_func_substr(THD *thd,
+ const Lex_ident_cli_st &schema_name,
+ const Lex_ident_cli_st &func_name,
+ List<Item> *args);
+ Item *make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name,
+ const Lex_ident_cli_st &func_name,
+ const Lex_trim_st &spec);
+ Item *make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name,
+ const Lex_ident_cli_st &func_name,
+ List<Item> *args);
+ Item *make_item_func_call_generic(THD *thd,
+ const Lex_ident_cli_st *db,
+ const Lex_ident_cli_st *name,
+ List<Item> *args);
+ Item *make_item_func_call_generic(THD *thd,
+ const Lex_ident_sys &db,
+ const Lex_ident_sys &name,
+ List<Item> *args);
Item *make_item_func_call_generic(THD *thd,
Lex_ident_cli_st *db,
Lex_ident_cli_st *pkg,
@@ -4830,7 +4891,8 @@ public:
bool check_dependencies_in_with_clauses();
bool check_cte_dependencies_and_resolve_references();
bool resolve_references_to_cte(TABLE_LIST *tables,
- TABLE_LIST **tables_last);
+ TABLE_LIST **tables_last,
+ st_select_lex_unit *excl_spec);
/**
Turn on the SELECT_DESCRIBE flag for every SELECT_LEX involved into
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 5a57c86e..faec566c 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -799,7 +799,9 @@ public:
class base_ilist_iterator
{
base_ilist *list;
- struct ilink **el,*current;
+ struct ilink **el;
+protected:
+ struct ilink *current;
public:
base_ilist_iterator(base_ilist &list_par) :list(&list_par),
el(&list_par.first),current(0) {}
@@ -811,6 +813,13 @@ public:
el= &current->next;
return current;
}
+ /* Unlink element returned by last next() call */
+ inline void unlink(void)
+ {
+ struct ilink **tmp= current->prev;
+ current->unlink();
+ el= tmp;
+ }
};
@@ -840,6 +849,13 @@ template <class T> class I_List_iterator :public base_ilist_iterator
public:
I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
+ /* Remove element returned by last next() call */
+ inline void remove(void)
+ {
+ unlink();
+ delete (T*) current;
+ current= 0; // Safety
+ }
};
/**
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 8aa1452c..be3063e5 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -255,6 +255,10 @@ public:
*/
void skip_data_till_eof()
{
+#ifndef EMBEDDED_LIBRARY
+ if (mysql_bin_log.is_open())
+ cache.read_function= cache.real_read_function;
+#endif
while (GET != my_b_EOF)
;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1817f811..f9da2c40 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2124,7 +2124,6 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD *
{
ulong pos;
ushort flags;
- uint32 slave_server_id;
status_var_increment(thd->status_var.com_other);
@@ -2135,10 +2134,26 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD *
/* TODO: The following has to be changed to an 8 byte integer */
pos = uint4korr(packet);
flags = uint2korr(packet + 4);
- thd->variables.server_id=0; /* avoid suicide */
- if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
- kill_zombie_dump_threads(slave_server_id);
- thd->variables.server_id = slave_server_id;
+ if ((thd->variables.server_id= uint4korr(packet+6)))
+ {
+ bool got_error;
+
+ got_error= kill_zombie_dump_threads(thd,
+ thd->variables.server_id);
+ if (got_error || thd->killed)
+ {
+ if (!thd->killed)
+ my_printf_error(ER_MASTER_FATAL_ERROR_READING_BINLOG,
+ "Could not start dump thread for slave: %u as "
+ "it has already a running dump thread",
+ MYF(0), (uint) thd->variables.server_id);
+ else if (! thd->get_stmt_da()->is_set())
+ thd->send_kill_message();
+ error= TRUE;
+ thd->unregister_slave(); // todo: can be extraneous
+ break;
+ }
+ }
const char *name= packet + 10;
size_t nlen= strlen(name);
@@ -2146,6 +2161,8 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD *
general_log_print(thd, command, "Log: '%s' Pos: %lu", name, pos);
if (nlen < FN_REFLEN)
mysql_binlog_send(thd, thd->strmake(name, nlen), (my_off_t)pos, flags);
+ if (thd->killed && ! thd->get_stmt_da()->is_set())
+ thd->send_kill_message();
thd->unregister_slave(); // todo: can be extraneous
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
error = TRUE;
@@ -2435,7 +2452,7 @@ resume:
/* Performance Schema Interface instrumentation, end */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->set_examined_row_count(0); // For processlist
- thd->set_command(COM_SLEEP);
+ thd->mark_connection_idle();
thd->m_statement_psi= NULL;
thd->m_digest= NULL;
@@ -2449,6 +2466,8 @@ resume:
*/
thd->lex->m_sql_cmd= NULL;
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ DBUG_EXECUTE_IF("print_allocated_thread_memory",
+ SAFEMALLOC_REPORT_MEMORY(sf_malloc_dbug_id()););
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
@@ -5173,9 +5192,55 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
my_ok(thd);
break;
case SQLCOM_BACKUP_LOCK:
- if (check_global_access(thd, RELOAD_ACL))
- goto error;
- /* first table is set for lock. For unlock the list is empty */
+ if (check_global_access(thd, RELOAD_ACL, true))
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ In case there is no global privilege, check DB privilege for LOCK TABLES.
+ */
+ if (first_table) // BACKUP LOCK
+ {
+ if (check_single_table_access(thd, LOCK_TABLES_ACL, first_table, true))
+ {
+ char command[30];
+ get_privilege_desc(command, sizeof(command), RELOAD_ACL|LOCK_TABLES_ACL);
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ goto error;
+ }
+ }
+ else // BACKUP UNLOCK
+ {
+ /*
+ We test mdl_backup_lock here because, if a user could obtain a lock
+ it would be silly to error and say `you can't BACKUP UNLOCK`
+ (because its obvious you did a `BACKUP LOCK`).
+ As `BACKUP UNLOCK` doesn't have a database reference,
+ there's no way we can check if the `BACKUP LOCK` privilege is missing.
+ Testing `thd->db` would involve faking a `TABLE_LIST` structure,
+ which because of the depth of inspection
+ in `check_single_table_access` makes the faking likely to cause crashes,
+ or unintended effects. The outcome of this is,
+ if a user does an `BACKUP UNLOCK` without a `BACKUP LOCKED` table,
+ there may be a` ER_SPECIFIC_ACCESS_DENIED` error even though
+ user has the privilege.
+ Its a bit different to what happens if the user has RELOAD_ACL,
+ where the error is silently ignored.
+ */
+ if (!thd->mdl_backup_lock)
+ {
+
+ char command[30];
+ get_privilege_desc(command, sizeof(command), RELOAD_ACL|LOCK_TABLES_ACL);
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ goto error;
+ }
+ }
+#endif
+ }
+ /*
+ There is reload privilege, first table is set for lock.
+ For unlock the list is empty
+ */
if (first_table)
res= backup_lock(thd, first_table);
else
@@ -7931,6 +7996,7 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
thd->wsrep_retry_query = NULL;
thd->wsrep_retry_query_len = 0;
thd->wsrep_retry_command = COM_CONNECT;
+ thd->proc_info= 0;
}
return false;
}
@@ -9285,11 +9351,13 @@ kill_one_thread(THD *thd, my_thread_id id, killed_state kill_signal, killed_type
struct kill_threads_callback_arg
{
- kill_threads_callback_arg(THD *thd_arg, LEX_USER *user_arg):
- thd(thd_arg), user(user_arg) {}
+ kill_threads_callback_arg(THD *thd_arg, LEX_USER *user_arg,
+ killed_state kill_signal_arg):
+ thd(thd_arg), user(user_arg), kill_signal(kill_signal_arg), counter(0) {}
THD *thd;
LEX_USER *user;
- List<THD> threads_to_kill;
+ killed_state kill_signal;
+ uint counter;
};
@@ -9312,11 +9380,12 @@ static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg)
{
return MY_TEST(arg->thd->security_ctx->master_access & PROCESS_ACL);
}
- if (!arg->threads_to_kill.push_back(thd, arg->thd->mem_root))
- {
- mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete
- mysql_mutex_lock(&thd->LOCK_thd_data);
- }
+ arg->counter++;
+ mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->awake_no_mutex(arg->kill_signal);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
}
return 0;
@@ -9326,42 +9395,17 @@ static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg)
static uint kill_threads_for_user(THD *thd, LEX_USER *user,
killed_state kill_signal, ha_rows *rows)
{
- kill_threads_callback_arg arg(thd, user);
+ kill_threads_callback_arg arg(thd, user, kill_signal);
DBUG_ENTER("kill_threads_for_user");
-
- *rows= 0;
-
- if (unlikely(thd->is_fatal_error)) // If we run out of memory
- DBUG_RETURN(ER_OUT_OF_RESOURCES);
-
DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str,
(uint) kill_signal));
+ *rows= 0;
+
if (server_threads.iterate(kill_threads_callback, &arg))
DBUG_RETURN(ER_KILL_DENIED_ERROR);
- if (!arg.threads_to_kill.is_empty())
- {
- List_iterator_fast<THD> it2(arg.threads_to_kill);
- THD *next_ptr;
- THD *ptr= it2++;
- do
- {
- ptr->awake_no_mutex(kill_signal);
- /*
- Careful here: The list nodes are allocated on the memroots of the
- THDs to be awakened.
- But those THDs may be terminated and deleted as soon as we release
- LOCK_thd_kill, which will make the list nodes invalid.
- Since the operation "it++" dereferences the "next" pointer of the
- previous list node, we need to do this while holding LOCK_thd_kill.
- */
- next_ptr= it2++;
- mysql_mutex_unlock(&ptr->LOCK_thd_kill);
- mysql_mutex_unlock(&ptr->LOCK_thd_data);
- (*rows)++;
- } while ((ptr= next_ptr));
- }
+ *rows= arg.counter;
DBUG_RETURN(0);
}
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 35767307..c968637d 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1688,7 +1688,6 @@ int plugin_init(int *argc, char **argv, int flags)
}
}
- free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE));
tmp.state= PLUGIN_IS_UNINITIALIZED;
if (register_builtin(plugin, &tmp, &plugin_ptr))
goto err_unlock;
@@ -1967,7 +1966,7 @@ static void plugin_load(MEM_ROOT *tmp_root)
the mutex here to satisfy the assert
*/
mysql_mutex_lock(&LOCK_plugin);
- plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG));
+ plugin_add(tmp_root, true, &name, &dl, MYF(ME_ERROR_LOG));
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
mysql_mutex_unlock(&LOCK_plugin);
}
diff --git a/sql/sql_plugin_services.inl b/sql/sql_plugin_services.inl
index f2b2d08d..5ac06e76 100644
--- a/sql/sql_plugin_services.inl
+++ b/sql/sql_plugin_services.inl
@@ -183,7 +183,8 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_report_bf_lock_wait,
wsrep_thd_kill_LOCK,
wsrep_thd_kill_UNLOCK,
- wsrep_thd_set_PA_unsafe
+ wsrep_thd_set_PA_unsafe,
+ wsrep_get_domain_id
};
static struct thd_specifics_service_st thd_specifics_handler=
@@ -228,11 +229,6 @@ static struct json_service_st json_handler=
json_unescape_json
};
-static struct thd_mdl_service_st thd_mdl_handler=
-{
- thd_mdl_context
-};
-
struct sql_service_st sql_service_handler=
{
mysql_init,
@@ -251,7 +247,13 @@ struct sql_service_st sql_service_handler=
mysql_fetch_lengths,
mysql_set_character_set,
mysql_num_fields,
- mysql_select_db
+ mysql_select_db,
+ mysql_ssl_set
+};
+
+static struct thd_mdl_service_st thd_mdl_handler=
+{
+ thd_mdl_context
};
#define DEFINE_warning_function(name, ret) { \
@@ -348,8 +350,8 @@ static struct st_service_ref list_of_services[]=
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "wsrep_service", VERSION_wsrep, &wsrep_handler },
{ "json_service", VERSION_json, &json_handler },
- { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler },
{ "sql_service", VERSION_sql_service, &sql_service_handler },
+ { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler },
{ "provider_service_bzip2", VERSION_provider_bzip2, &provider_handler_bzip2 },
{ "provider_service_lz4", VERSION_provider_lz4, &provider_handler_lz4 },
{ "provider_service_lzma", VERSION_provider_lzma, &provider_handler_lzma },
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index bf3c7cbb..df200a69 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -132,6 +132,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8;
#include "wsrep_mysqld.h"
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
+#include "sql_audit.h" // mysql_audit_release
#include "xa.h" // xa_recover_get_fields
#include "sql_audit.h" // mysql_audit_release
@@ -181,7 +182,7 @@ public:
/*
The following data member is wholly for debugging purpose.
It can be used for possible crash analysis to determine how many times
- the stored routine was executed before the mem_root marked read_only
+ the stored routine was executed before the mem_root marked ROOT_FLAG_READ_ONLY
was requested for a memory chunk. Additionally, a value of this data
member is output to the log with DBUG_PRINT.
*/
@@ -265,7 +266,6 @@ private:
class Ed_connection;
-
/******************************************************************************
Implementation
******************************************************************************/
@@ -4068,6 +4068,7 @@ static bool execute_server_code(THD *thd,
const char *sql_text, size_t sql_len)
{
PSI_statement_locker *parent_locker;
+ Reprepare_observer *reprepare_observer;
bool error;
query_id_t save_query_id= thd->query_id;
query_id_t next_id= next_query_id();
@@ -4092,28 +4093,33 @@ static bool execute_server_code(THD *thd,
parent_locker= thd->m_statement_psi;
thd->m_statement_psi= NULL;
+ reprepare_observer= thd->m_reprepare_observer;
+ thd->m_reprepare_observer= NULL;
error= mysql_execute_command(thd);
thd->m_statement_psi= parent_locker;
+ thd->m_reprepare_observer= reprepare_observer;
/* report error issued during command execution */
if (likely(error == 0) && thd->spcont == NULL)
- general_log_write(thd, COM_QUERY,
- thd->query(), thd->query_length());
+ general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
end:
thd->lex->restore_set_statement_var();
thd->query_id= save_query_id;
delete_explain_query(thd->lex);
+
lex_end(thd->lex);
return error;
}
+
bool Execute_sql_statement::execute_server_code(THD *thd)
{
return ::execute_server_code(thd, m_sql_text.str, m_sql_text.length);
}
+
/***************************************************************************
Prepared_statement
****************************************************************************/
@@ -4685,7 +4691,7 @@ reexecute:
#ifdef PROTECT_STATEMENT_MEMROOT
// There was reprepare so the counter of runs should be reset
executed_counter= 0;
- mem_root->read_only= 0;
+ mem_root->flags &= ~ROOT_FLAG_READ_ONLY;
#endif
goto reexecute;
}
@@ -4694,7 +4700,7 @@ reexecute:
#ifdef PROTECT_STATEMENT_MEMROOT
if (!error)
{
- mem_root->read_only= 1;
+ mem_root->flags |= ROOT_FLAG_READ_ONLY;
++executed_counter;
DBUG_PRINT("info", ("execute counter: %lu", executed_counter));
@@ -4703,7 +4709,7 @@ reexecute:
{
// Error on call shouldn't be counted as a normal run
executed_counter= 0;
- mem_root->read_only= 0;
+ mem_root->flags &= ~ROOT_FLAG_READ_ONLY;
}
#endif
@@ -6081,7 +6087,8 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
for (uint pos= 0 ; (item= it++); pos++)
{
- if (store_item_metadata(thd, item, pos))
+ Send_field sf(thd, item);
+ if (store_field_metadata(thd, sf, item->charset_for_protocol(), pos))
goto err;
}
@@ -6473,10 +6480,9 @@ extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql)
new_thd->security_ctx->skip_grants();
new_thd->query_cache_is_applicable= 0;
new_thd->variables.wsrep_on= 0;
+ new_thd->client_capabilities= client_flag;
new_thd->variables.sql_log_bin= 0;
new_thd->set_binlog_bit();
- new_thd->client_capabilities= client_flag;
-
/*
TOSO: decide if we should turn the auditing off
for such threads.
@@ -6507,4 +6513,3 @@ extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql)
DBUG_PRINT("exit",("Mysql handler: %p", mysql));
DBUG_RETURN(mysql);
}
-
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index f576e693..863f0369 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -202,7 +202,8 @@ void PROF_MEASUREMENT::set_label(const char *status_arg,
allocated_status_memory= (char *) my_malloc(key_memory_PROFILE, sizes[0] +
sizes[1] + sizes[2], MYF(0));
- DBUG_ASSERT(allocated_status_memory != NULL);
+ if (!allocated_status_memory)
+ return;
cursor= allocated_status_memory;
@@ -266,6 +267,8 @@ QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg)
{
m_seq_counter= 1;
PROF_MEASUREMENT *prof= new PROF_MEASUREMENT(this, status_arg);
+ if (!prof)
+ return;
prof->m_seq= m_seq_counter++;
m_start_time_usecs= prof->time_usecs;
m_end_time_usecs= m_start_time_usecs;
@@ -307,6 +310,8 @@ void QUERY_PROFILE::new_status(const char *status_arg,
prof= new PROF_MEASUREMENT(this, status_arg, function_arg, base_name(file_arg), line_arg);
else
prof= new PROF_MEASUREMENT(this, status_arg);
+ if (!prof)
+ DBUG_VOID_RETURN;
prof->m_seq= m_seq_counter++;
m_end_time_usecs= prof->time_usecs;
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
index 88136559..c225f617 100644
--- a/sql/sql_profile.h
+++ b/sql/sql_profile.h
@@ -103,6 +103,8 @@ public:
new_item= (struct queue_item *) my_malloc(key_memory_queue_item,
sizeof(struct queue_item), MYF(0));
+ if (!new_item)
+ return;
new_item->payload= payload;
@@ -296,7 +298,11 @@ public:
{
DBUG_ASSERT(!current);
if (unlikely(enabled))
- current= new QUERY_PROFILE(this, initial_state);
+ {
+ QUERY_PROFILE *new_profile= new QUERY_PROFILE(this, initial_state);
+ if (new_profile)
+ current= new_profile;
+ }
}
void discard_current_query();
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 8bde0f3b..ad71bf6f 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -510,7 +510,7 @@ static enum enum_binlog_checksum_alg get_binlog_checksum_value_at_connect(THD *
}
else
{
- DBUG_ASSERT(entry->type == STRING_RESULT);
+ DBUG_ASSERT(entry->type_handler()->result_type() == STRING_RESULT);
String str;
uint dummy_errors;
str.copy(entry->value, entry->length, &my_charset_bin, &my_charset_bin,
@@ -2060,7 +2060,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
}
if (need_sync && repl_semisync_master.flush_net(info->thd,
- packet->c_ptr_safe()))
+ packet->c_ptr()))
{
info->error= ER_UNKNOWN_ERROR;
return "Failed to run hook 'after_send_event'";
@@ -3011,8 +3011,13 @@ err:
if (info->thd->killed == KILL_SLAVE_SAME_ID)
{
- info->errmsg= "A slave with the same server_uuid/server_id as this slave "
- "has connected to the master";
+ /*
+ Note that the text is limited to 64 characters in errmsg-utf8 in
+ ER_ABORTING_CONNECTION.
+ */
+ info->errmsg=
+ "A slave with the same server_uuid/server_id is already "
+ "connected";
info->error= ER_SLAVE_SAME_ID;
}
@@ -3385,6 +3390,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
@retval 0 success
@retval 1 error
*/
+
int reset_slave(THD *thd, Master_info* mi)
{
MY_STAT stat_area;
@@ -3482,8 +3488,6 @@ int reset_slave(THD *thd, Master_info* mi)
else if (global_system_variables.log_warnings > 1)
sql_print_information("Deleted Master_info file '%s'.", fname);
- if (rpl_semi_sync_slave_enabled)
- repl_semisync_slave.reset_slave(mi);
err:
mi->unlock_slave_threads();
if (unlikely(error))
@@ -3511,43 +3515,89 @@ err:
struct kill_callback_arg
{
- kill_callback_arg(uint32 id): slave_server_id(id), thd(0) {}
- uint32 slave_server_id;
+ kill_callback_arg(THD *thd_arg, uint32 id):
+ thd(thd_arg), slave_server_id(id), counter(0) {}
THD *thd;
+ uint32 slave_server_id;
+ uint counter;
};
-static my_bool kill_callback(THD *thd, kill_callback_arg *arg)
+
+/*
+ Collect all active dump threads
+*/
+
+static my_bool kill_callback_collect(THD *thd, kill_callback_arg *arg)
{
if (thd->get_command() == COM_BINLOG_DUMP &&
- thd->variables.server_id == arg->slave_server_id)
+ thd->variables.server_id == arg->slave_server_id &&
+ thd != arg->thd)
{
- arg->thd= thd;
+ arg->counter++;
mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete
mysql_mutex_lock(&thd->LOCK_thd_data);
- return 1;
+ thd->awake_no_mutex(KILL_SLAVE_SAME_ID); // Mark killed
+ /*
+ Remover the thread from ack_receiver to ensure it is not
+ sending acks to the master anymore.
+ */
+ ack_receiver.remove_slave(thd);
+
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
return 0;
}
-void kill_zombie_dump_threads(uint32 slave_server_id)
+/*
+ Check if there are any active dump threads
+*/
+
+static my_bool kill_callback_check(THD *thd, kill_callback_arg *arg)
+{
+ return (thd->get_command() == COM_BINLOG_DUMP &&
+ thd->variables.server_id == arg->slave_server_id &&
+ thd != arg->thd);
+}
+
+
+/**
+ Try to kill running dump threads on the master
+
+ @result 0 ok
+ @result 1 old slave thread exists and does not want to die
+
+ There should not be more than one dump thread with the same server id
+ this code has however in the past has several issues. To ensure that
+ things works in all cases (now and in the future), this code is collecting
+ all matching server id's and killing all of them.
+*/
+
+bool kill_zombie_dump_threads(THD *thd, uint32 slave_server_id)
{
- kill_callback_arg arg(slave_server_id);
- server_threads.iterate(kill_callback, &arg);
+ kill_callback_arg arg(thd, slave_server_id);
+ server_threads.iterate(kill_callback_collect, &arg);
+
+ if (!arg.counter)
+ return 0;
- if (arg.thd)
+ /*
+ Wait up to SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL for kill
+ of all dump thread, trying every 1/10 of second.
+ */
+ for (uint i= 10 * SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL ;
+ --i > 0 && !thd->killed;
+ i++)
{
- /*
- Here we do not call kill_one_thread() as
- it will be slow because it will iterate through the list
- again. We just to do kill the thread ourselves.
- */
- arg.thd->awake_no_mutex(KILL_SLAVE_SAME_ID);
- mysql_mutex_unlock(&arg.thd->LOCK_thd_kill);
- mysql_mutex_unlock(&arg.thd->LOCK_thd_data);
+ if (!server_threads.iterate(kill_callback_check, &arg))
+ return 0; // All dump thread are killed
+ my_sleep(1000000L / 10); // Wait 1/10 of a second
}
+ return 1;
}
+
/**
Get value for a string parameter with error checking
@@ -4303,11 +4353,17 @@ bool mysql_show_binlog_events(THD* thd)
}
}
+ /*
+ Omit error messages from server log in Log_event::read_log_event. That
+ is, we only need to notify the client to correct their 'from' offset;
+ writing about this in the server log would be confusing as it isn't
+ related to server operational status.
+ */
for (event_count = 0;
(ev = Log_event::read_log_event(&log,
description_event,
(opt_master_verify_checksum ||
- verify_checksum_once))); )
+ verify_checksum_once), false)); )
{
if (!unit->lim.check_offset(event_count) &&
ev->net_send(protocol, linfo.log_file_name, pos))
@@ -4595,6 +4651,10 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
/* buffer contains position where we started last read */
uchar* buffer= (uchar*) my_b_get_buffer_start(file);
uint max_event_size= lf_info->thd->variables.max_allowed_packet;
+ int res;
+#ifndef DBUG_OFF
+ bool did_dbug_inject= false;
+#endif
if (lf_info->thd->is_current_stmt_binlog_format_row())
goto ret;
@@ -4602,6 +4662,19 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
lf_info->last_pos_in_file >= my_b_get_pos_in_file(file))
goto ret;
+ DBUG_EXECUTE_IF("load_data_binlog_cache_error",
+ {
+ /*
+ Simulate "disk full" error in the middle of writing to
+ the binlog cache.
+ */
+ if (lf_info->last_pos_in_file >= 2*4096)
+ {
+ DBUG_SET("+d,simulate_file_write_error");
+ did_dbug_inject= true;
+ }
+ };);
+
for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0;
buffer += MY_MIN(block_len, max_event_size),
block_len -= MY_MIN(block_len, max_event_size))
@@ -4613,7 +4686,10 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&a))
- DBUG_RETURN(1);
+ {
+ res= 1;
+ goto err;
+ }
}
else
{
@@ -4622,12 +4698,20 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&b))
- DBUG_RETURN(1);
+ {
+ res= 1;
+ goto err;
+ }
lf_info->wrote_create_file= 1;
}
}
ret:
- int res= Buffer ? lf_info->real_read_function(file, Buffer, Count) : 0;
+ res= Buffer ? lf_info->real_read_function(file, Buffer, Count) : 0;
+err:
+#ifndef DBUG_OFF
+ if (did_dbug_inject)
+ DBUG_SET("-d,simulate_file_write_error");
+#endif
DBUG_RETURN(res);
}
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 95916e31..3be1e18c 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -43,7 +43,7 @@ void adjust_linfo_offsets(my_off_t purge_offset);
void show_binlogs_get_fields(THD *thd, List<Item> *field_list);
bool show_binlogs(THD* thd);
extern int init_master_info(Master_info* mi);
-void kill_zombie_dump_threads(uint32 slave_server_id);
+bool kill_zombie_dump_threads(THD *thd, uint32 slave_server_id);
int check_binlog_magic(IO_CACHE* log, const char** errmsg);
int compare_log_name(const char *log_1, const char *log_2);
diff --git a/sql/sql_schema.cc b/sql/sql_schema.cc
index f08204d2..7a6c0c99 100644
--- a/sql/sql_schema.cc
+++ b/sql/sql_schema.cc
@@ -33,6 +33,12 @@ public:
return src;
}
+ Create_func *find_native_function_builder(THD *thd, const LEX_CSTRING &name)
+ const
+ {
+ return native_functions_hash_oracle.find(thd, name);
+ }
+
Item *make_item_func_replace(THD *thd,
Item *subj,
Item *find,
@@ -64,6 +70,7 @@ Schema mariadb_schema(Lex_cstring(STRING_WITH_LEN("mariadb_schema")));
Schema_oracle oracle_schema(Lex_cstring(STRING_WITH_LEN("oracle_schema")));
Schema_maxdb maxdb_schema(Lex_cstring(STRING_WITH_LEN("maxdb_schema")));
+const Schema &oracle_schema_ref= oracle_schema;
Schema *Schema::find_by_name(const LEX_CSTRING &name)
{
@@ -88,6 +95,26 @@ Schema *Schema::find_implied(THD *thd)
}
+Create_func *
+Schema::find_native_function_builder(THD *thd, const LEX_CSTRING &name) const
+{
+ return native_functions_hash.find(thd, name);
+}
+
+
+Item *Schema::make_item_func_call_native(THD *thd,
+ const Lex_ident_sys &name,
+ List<Item> *args) const
+{
+ Create_func *builder= find_native_function_builder(thd, name);
+ if (builder)
+ return builder->create_func(thd, &name, args);
+ my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), name.str);
+ return NULL;
+}
+
+
+
Item *Schema::make_item_func_replace(THD *thd,
Item *subj,
Item *find,
diff --git a/sql/sql_schema.h b/sql/sql_schema.h
index 0258ff2d..af83c5e9 100644
--- a/sql/sql_schema.h
+++ b/sql/sql_schema.h
@@ -19,6 +19,9 @@
#include "mysqld.h"
#include "lex_string.h"
+class Lex_ident_sys;
+class Create_func;
+
class Schema
{
LEX_CSTRING m_name;
@@ -34,6 +37,24 @@ public:
return src;
}
+ /**
+ Find a native function builder, return an error if not found,
+ build an Item otherwise.
+ */
+ Item *make_item_func_call_native(THD *thd,
+ const Lex_ident_sys &name,
+ List<Item> *args) const;
+
+ /**
+ Find the native function builder associated with a given function name.
+ @param thd The current thread
+ @param name The native function name
+ @return The native function builder associated with the name, or NULL
+ */
+ virtual Create_func *find_native_function_builder(THD *thd,
+ const LEX_CSTRING &name)
+ const;
+
// Builders for native SQL function with a special syntax in sql_yacc.yy
virtual Item *make_item_func_replace(THD *thd,
Item *subj,
@@ -67,5 +88,6 @@ public:
extern Schema mariadb_schema;
+extern const Schema &oracle_schema_ref;
#endif // SQL_SCHEMA_H_INCLUDED
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index f4cbed58..c3ce21d1 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1457,6 +1457,26 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
if (setup_wild(thd, tables_list, fields_list, &all_fields, select_lex, false))
DBUG_RETURN(-1);
+ /*
+ If the select_lex is immediately contained within a derived table
+ AND this derived table is a CTE
+ WITH supplied column names
+ AND we have the correct number of elements in both lists
+ (mismatches found in mysql_derived_prepare/rename_columns_of_derived_unit)
+ THEN NOW is the time to take a copy of these item_names for
+ later restoration if required.
+ */
+ TABLE_LIST *derived= select_lex->master_unit()->derived;
+
+ if (derived &&
+ derived->with &&
+ derived->with->column_list.elements &&
+ (derived->with->column_list.elements == select_lex->item_list.elements))
+ {
+ if (select_lex->save_item_list_names(thd))
+ DBUG_RETURN(-1);
+ }
+
if (thd->lex->current_select->first_cond_optimization)
{
if ( conds && ! thd->lex->current_select->merged_into)
@@ -1965,9 +1985,14 @@ bool JOIN::make_range_rowid_filters()
bool force_index_save= tab->table->force_index;
tab->table->force_index= true;
quick_select_return rc;
+ /*
+ EQ_FUNC and EQUAL_FUNC already sent unusable key notes (if any)
+ during update_ref_and_keys(). Have only other functions raise notes
+ from can_optimize_scalar_range().
+ */
rc= sel->test_quick_select(thd, filter_map, (table_map) 0,
(ha_rows) HA_POS_ERROR, true, false, true,
- true);
+ true, Item_func::BITMAP_EXCEPT_ANY_EQUALITY);
tab->table->force_index= force_index_save;
if (rc == SQL_SELECT::ERROR || thd->is_error())
{
@@ -5194,13 +5219,19 @@ static bool get_quick_record_count(THD *thd, SQL_SELECT *select,
if (unlikely(check_stack_overrun(thd, STACK_MIN_SIZE, buff)))
DBUG_RETURN(false); // Fatal error flag is set
if (select)
- {
+ {
select->head=table;
table->reginfo.impossible_range=0;
+ /*
+ EQ_FUNC and EQUAL_FUNC already sent unusable key notes (if any)
+ during update_ref_and_keys(). Have only other functions raise notes
+ from can_optimize_scalar_range().
+ */
error= select->test_quick_select(thd, *(key_map *)keys, (table_map) 0,
limit, 0, FALSE,
TRUE, /* remove_where_parts*/
- FALSE, TRUE);
+ FALSE,
+ Item_func::BITMAP_EXCEPT_ANY_EQUALITY);
if (error == SQL_SELECT::OK && select->quick)
{
@@ -5915,11 +5946,15 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
This is can't be to high as otherwise we are likely to use
table scan.
*/
- s->worst_seeks= MY_MIN((double) s->found_records / 10,
- (double) s->read_time*3);
- if (s->worst_seeks < 2.0) // Fix for small tables
- s->worst_seeks=2.0;
-
+ /* Largest integer that can be stored in double (no compiler warning) */
+ s->worst_seeks= (double) (1ULL << 53);
+ if (thd->variables.optimizer_adjust_secondary_key_costs != 2)
+ {
+ s->worst_seeks= MY_MIN((double) s->found_records / 10,
+ (double) s->read_time*3);
+ if (s->worst_seeks < 2.0) // Fix for small tables
+ s->worst_seeks=2.0;
+ }
/*
Add to stat->const_keys those indexes for which all group fields or
all select distinct fields participate in one index.
@@ -7031,6 +7066,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
{
field->raise_note_cannot_use_key_part(thd, key, part,
equal_str,
+ key_field->cond->compare_collation(),
key_field->val,
compat);
}
@@ -7896,8 +7932,27 @@ double cost_for_index_read(const THD *thd, const TABLE *table, uint key,
if (table->covering_keys.is_set(key))
cost= file->keyread_time(key, 1, records);
else
+ {
cost= ((file->keyread_time(key, 0, records) +
file->read_time(key, 1, MY_MIN(records, worst_seeks))));
+ if (thd->variables.optimizer_adjust_secondary_key_costs == 1 &&
+ file->is_clustering_key(0))
+ {
+ /*
+ According to benchmarks done in 11.0 to calculate the new cost
+ model secondary key ranges are about 7x slower than primary
+ key ranges for big tables. Here we are a bit conservative and
+ only calculate with 5x. The reason for having it only 5x and
+ not for example 7x is is that choosing plans with more rows
+ that are read (ignored by the WHERE clause) causes the 10.x
+ optimizer to believe that there are more rows in the result
+ set, which can cause problems in finding the best join order.
+ Note: A clustering primary key is always key 0.
+ */
+ double clustering_key_cost= file->read_time(0, 1, records);
+ cost= MY_MAX(cost, clustering_key_cost * 5);
+ }
+ }
DBUG_PRINT("statistics", ("cost: %.3f", cost));
DBUG_RETURN(cost);
@@ -8071,6 +8126,14 @@ best_access_path(JOIN *join,
double keyread_tmp= 0;
ha_rows rec;
bool best_uses_jbuf= FALSE;
+ /*
+ if optimizer_use_condition_selectivity adjust filter cost to be slightly
+ higher to ensure that ref|filter is not less than range over same
+ number of rows
+ */
+ double filter_setup_cost= (thd->variables.
+ optimizer_adjust_secondary_key_costs == 2 ?
+ 1.0 : 0.0);
MY_BITMAP *eq_join_set= &s->table->eq_join_set;
KEYUSE *hj_start_key= 0;
SplM_plan_info *spl_plan= 0;
@@ -8301,6 +8364,9 @@ best_access_path(JOIN *join,
trace_access_idx.add("used_range_estimates", true);
tmp= adjust_quick_cost(table->opt_range[key].cost,
table->opt_range[key].rows);
+ keyread_tmp= table->file->keyread_time(key, 1,
+ table->opt_range[key].
+ rows);
goto got_cost;
}
else
@@ -8625,6 +8691,7 @@ best_access_path(JOIN *join,
type == JT_EQ_REF ? 0.5 * tmp : MY_MIN(tmp, keyread_tmp);
double access_cost_factor= MY_MIN((tmp - key_access_cost) / rows, 1.0);
+
if (!(records < s->worst_seeks &&
records <= thd->variables.max_seeks_for_key))
{
@@ -8641,7 +8708,9 @@ best_access_path(JOIN *join,
}
if (filter)
{
- tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows);
+ tmp-= (filter->get_adjusted_gain(rows) -
+ filter->get_cmp_gain(rows) -
+ filter_setup_cost);
DBUG_ASSERT(tmp >= 0);
trace_access_idx.add("rowid_filter_key",
table->key_info[filter->key_no].name);
@@ -8891,7 +8960,7 @@ best_access_path(JOIN *join,
access_cost_factor);
if (filter)
{
- tmp-= filter->get_adjusted_gain(rows);
+ tmp-= filter->get_adjusted_gain(rows) - filter_setup_cost;
DBUG_ASSERT(tmp >= 0);
}
@@ -13049,7 +13118,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
HA_POS_ERROR :
join->unit->lim.get_select_limit()),
0,
- FALSE, FALSE, FALSE)) ==
+ FALSE, FALSE, FALSE,
+ Item_func::BITMAP_ALL)) ==
SQL_SELECT::IMPOSSIBLE_RANGE)
{
/*
@@ -13064,7 +13134,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
join->unit->lim.get_select_limit()),
- 0, FALSE, FALSE, FALSE, TRUE)) ==
+ 0, FALSE, FALSE, FALSE,
+ Item_func::BITMAP_NONE)) ==
SQL_SELECT::IMPOSSIBLE_RANGE)
DBUG_RETURN(1); // Impossible WHERE
}
@@ -23199,7 +23270,9 @@ test_if_quick_select(JOIN_TAB *tab)
res= tab->select->test_quick_select(tab->join->thd, tab->keys,
(table_map) 0, HA_POS_ERROR, 0,
FALSE, /*remove where parts*/FALSE,
- FALSE, /* no warnings */ TRUE);
+ FALSE,
+ /* no unusable key notes */
+ Item_func::BITMAP_NONE);
if (tab->explain_plan && tab->explain_plan->range_checked_fer)
tab->explain_plan->range_checked_fer->collect_data(tab->select->quick);
@@ -25250,7 +25323,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
HA_POS_ERROR :
tab->join->unit->
lim.get_select_limit(),
- TRUE, TRUE, FALSE, FALSE);
+ TRUE, TRUE, FALSE, FALSE,
+ Item_func::BITMAP_ALL);
// if we cannot use quick select
if (res != SQL_SELECT::OK || !tab->select->quick)
{
@@ -25355,7 +25429,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
join->select_options & OPTION_FOUND_ROWS ?
HA_POS_ERROR :
join->unit->lim.get_select_limit(),
- TRUE, FALSE, FALSE, FALSE);
+ TRUE, FALSE, FALSE, FALSE,
+ Item_func::BITMAP_ALL);
if (res == SQL_SELECT::ERROR)
{
*fatal_error= true;
@@ -29163,7 +29238,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool distinct,const char *message)
{
THD *thd=join->thd;
- select_result *result=join->result;
DBUG_ENTER("select_describe");
if (join->select_lex->pushdown_select)
@@ -29198,7 +29272,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (unit->explainable())
{
- if (mysql_explain_union(thd, unit, result))
+ if (mysql_explain_union(thd, unit, unit->result))
DBUG_VOID_RETURN;
}
}
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
index f5652bd8..f6d7c310 100644
--- a/sql/sql_sequence.cc
+++ b/sql/sql_sequence.cc
@@ -921,6 +921,7 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
TABLE_LIST *first_table= lex->query_tables;
TABLE *table;
sequence_definition *new_seq= lex->create_info.seq_create_info;
+ uint saved_used_fields= new_seq->used_fields;
SEQUENCE *seq;
No_such_table_error_handler no_such_table_handler;
DBUG_ENTER("Sql_cmd_alter_sequence::execute");
@@ -1042,5 +1043,6 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
my_ok(thd);
end:
+ new_seq->used_fields= saved_used_fields;
DBUG_RETURN(error);
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 32b29468..980c2fa7 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2777,7 +2777,7 @@ static const char *thread_state_info(THD *tmp)
if (cond)
return "Waiting on cond";
}
- return NULL;
+ return "";
}
@@ -6398,7 +6398,8 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
tmp_cl->get_collation_name(MY_COLLATION_NAME_MODE_CONTEXT);
LEX_CSTRING full_collation_name=
tmp_cl->get_collation_name(MY_COLLATION_NAME_MODE_FULL);
- bool is_context= cmp(context_collation_name, full_collation_name);
+ bool is_context= cmp(context_collation_name, full_collation_name) &&
+ !(thd->variables.old_behavior & OLD_MODE_NO_NULL_COLLATION_IDS);
/*
Some collations are applicable to multiple character sets.
Display them only once, with the short name (without the
@@ -8833,9 +8834,9 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
}
List_iterator_fast<Item> it(sel->item_list);
if (!(transl=
- (Field_translator*)(thd->stmt_arena->
+ (Field_translator*)(thd->active_stmt_arena_to_use()->
alloc(sel->item_list.elements *
- sizeof(Field_translator)))))
+ sizeof(Field_translator))))) // ???
{
DBUG_RETURN(1);
}
@@ -9795,7 +9796,7 @@ ST_FIELD_INFO partitions_fields_info[]=
ST_FIELD_INFO variables_fields_info[]=
{
Column("VARIABLE_NAME", Varchar(64), NOT_NULL, "Variable_name"),
- Column("VARIABLE_VALUE", Varchar(2048), NOT_NULL, "Value"),
+ Column("VARIABLE_VALUE", Varchar(4096), NOT_NULL, "Value"),
CEnd()
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index b33d2ff4..7e4d271b 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -7208,6 +7208,14 @@ bool mysql_compare_tables(TABLE *table, Alter_info *alter_info,
(uint) (field->flags & NOT_NULL_FLAG))
DBUG_RETURN(false);
+ if (field->vcol_info)
+ {
+ if (!tmp_new_field->field->vcol_info)
+ DBUG_RETURN(false);
+ if (!field->vcol_info->is_equal(tmp_new_field->field->vcol_info))
+ DBUG_RETURN(false);
+ }
+
/*
mysql_prepare_alter_table() clears HA_OPTION_PACK_RECORD bit when
preparing description of existing table. In ALTER TABLE it is later
@@ -9089,6 +9097,30 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
goto err;
case Alter_drop::FOREIGN_KEY:
// Leave the DROP FOREIGN KEY names in the alter_info->drop_list.
+ /* If this is DROP FOREIGN KEY without IF EXIST,
+ we can now check does it exists and if not report a error. */
+ if (!drop->drop_if_exists)
+ {
+ List <FOREIGN_KEY_INFO> fk_child_key_list;
+ table->file->get_foreign_key_list(thd, &fk_child_key_list);
+ if (fk_child_key_list.is_empty())
+ {
+ fk_not_found:
+ my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(),
+ drop->name);
+ goto err;
+ }
+ List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list);
+ while (FOREIGN_KEY_INFO *f_key= fk_key_it++)
+ {
+ if (my_strcasecmp(system_charset_info, f_key->foreign_id->str,
+ drop->name) == 0)
+ goto fk_found;
+ }
+ goto fk_not_found;
+ fk_found:
+ break;
+ }
break;
}
}
@@ -12278,13 +12310,18 @@ bool check_engine(THD *thd, const char *db_name,
if (!*new_engine)
DBUG_RETURN(true);
- /* Enforced storage engine should not be used in
- ALTER TABLE that does not use explicit ENGINE = x to
- avoid unwanted unrelated changes.*/
- if (!(thd->lex->sql_command == SQLCOM_ALTER_TABLE &&
- !(create_info->used_fields & HA_CREATE_USED_ENGINE)))
- enf_engine= thd->variables.enforced_table_plugin ?
- plugin_hton(thd->variables.enforced_table_plugin) : NULL;
+ /*
+ Enforced storage engine should not be used in ALTER TABLE that does not
+ use explicit ENGINE = x to avoid unwanted unrelated changes. It should not
+ be used in CREATE INDEX too.
+ */
+ if (!((thd->lex->sql_command == SQLCOM_ALTER_TABLE &&
+ !(create_info->used_fields & HA_CREATE_USED_ENGINE)) ||
+ thd->lex->sql_command == SQLCOM_CREATE_INDEX))
+ {
+ plugin_ref enf_plugin= thd->variables.enforced_table_plugin;
+ enf_engine= enf_plugin ? plugin_hton(enf_plugin) : NULL;
+ }
if (enf_engine && enf_engine != *new_engine)
{
@@ -12381,8 +12418,18 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
Alter_info alter_info(lex->alter_info, thd->mem_root);
#ifdef WITH_WSREP
+ bool wsrep_ctas= false;
// If CREATE TABLE AS SELECT and wsrep_on
- const bool wsrep_ctas= (select_lex->item_list.elements && WSREP(thd));
+ if (WSREP(thd) && (select_lex->item_list.elements ||
+ // Only CTAS may be applied not using TOI.
+ (wsrep_thd_is_applying(thd) && !wsrep_thd_is_toi(thd))))
+ {
+ wsrep_ctas= true;
+
+ // MDEV-22232: Disable CTAS retry by setting the retry counter to the
+ // threshold value.
+ thd->wsrep_retry_counter= thd->variables.wsrep_retry_autocommit;
+ }
// This will be used in THD::decide_logging_format if CTAS
Enable_wsrep_ctas_guard wsrep_ctas_guard(thd, wsrep_ctas);
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 9163d8fc..5cc192af 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -29,11 +29,16 @@
#include <thr_alarm.h>
#include "sql_connect.h"
#include "thread_cache.h"
-#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
+
+#if defined(HAVE_MALLOC_H)
#include <malloc.h>
-#elif defined(HAVE_MALLINFO) && defined(HAVE_SYS_MALLOC_H)
+#endif
+
+#if defined(HAVE_SYS_MALLOC_H)
#include <sys/malloc.h>
-#elif defined(HAVE_MALLOC_ZONE)
+#endif
+
+#if defined(HAVE_MALLOC_ZONE)
#include <malloc/malloc.h>
#endif
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 067b921e..d7600108 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -2537,7 +2537,8 @@ add_tables_and_routines_for_triggers(THD *thd,
MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str);
- if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
+ if (sp_add_used_routine(prelocking_ctx,
+ thd->active_stmt_arena_to_use(),
&key, &sp_handler_trigger,
table_list->belong_to_view))
{
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index c5a2b16a..fc1db382 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -271,7 +271,10 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
if (!holders)
{
- holders= type_holders= new (thd->stmt_arena->mem_root) Type_holder[cnt];
+ DBUG_ASSERT(thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute() ||
+ thd->stmt_arena->is_conventional());
+ holders= type_holders=
+ new (thd->active_stmt_arena_to_use()->mem_root) Type_holder[cnt];
if (!holders ||
join_type_handlers_for_tvc(thd, li, holders, cnt) ||
get_type_attributes_for_tvc(thd, li, holders,
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 277f495a..a086e338 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -1446,22 +1446,6 @@ Type_handler_string_result::charset_for_protocol(const Item *item) const
}
-const Type_handler *
-Type_handler::get_handler_by_cmp_type(Item_result type)
-{
- switch (type) {
- case REAL_RESULT: return &type_handler_double;
- case INT_RESULT: return &type_handler_slonglong;
- case DECIMAL_RESULT: return &type_handler_newdecimal;
- case STRING_RESULT: return &type_handler_long_blob;
- case TIME_RESULT: return &type_handler_datetime;
- case ROW_RESULT: return &type_handler_row;
- }
- DBUG_ASSERT(0);
- return &type_handler_string;
-}
-
-
/*
If we have a mixture of:
- a MariaDB standard (built-in permanent) data type, and
@@ -2258,6 +2242,34 @@ Type_handler::get_handler_by_real_type(enum_field_types type)
}
+const Type_handler *
+Type_handler::handler_by_log_event_data_type(THD *thd,
+ const Log_event_data_type &type)
+{
+ if (type.data_type_name().length)
+ {
+ const Type_handler *th= handler_by_name(thd, type.data_type_name());
+ if (th)
+ return th;
+ }
+ switch (type.type()) {
+ case STRING_RESULT:
+ case ROW_RESULT:
+ case TIME_RESULT:
+ break;
+ case REAL_RESULT:
+ return &type_handler_double;
+ case INT_RESULT:
+ if (type.is_unsigned())
+ return &type_handler_ulonglong;
+ return &type_handler_slonglong;
+ case DECIMAL_RESULT:
+ return &type_handler_newdecimal;
+ }
+ return &type_handler_long_blob;
+}
+
+
/**
Create a DOUBLE field by default.
*/
@@ -5668,6 +5680,14 @@ Type_handler_string_result::Item_func_hybrid_field_type_get_date(
/***************************************************************************/
+bool Type_handler::Item_bool_rowready_func2_fix_length_and_dec(THD *thd,
+ Item_bool_rowready_func2 *func) const
+{
+ return func->fix_length_and_dec_generic(thd, this);
+}
+
+/***************************************************************************/
+
bool Type_handler_numeric::
Item_func_between_fix_length_and_dec(Item_func_between *func) const
{
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 35c13a38..5ce17447 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -30,6 +30,8 @@
#include "sql_type_string.h"
#include "sql_type_real.h"
#include "compat56.h"
+#include "log_event_data_type.h"
+
C_MODE_START
#include <ma_dyncol.h>
C_MODE_END
@@ -55,6 +57,7 @@ class Item_hybrid_func;
class Item_func_min_max;
class Item_func_hybrid_field_type;
class Item_bool_func2;
+class Item_bool_rowready_func2;
class Item_func_between;
class Item_func_in;
class Item_func_round;
@@ -150,8 +153,8 @@ scalar_comparison_op_to_lex_cstring(scalar_comparison_op op)
case SCALAR_CMP_EQUAL: return LEX_CSTRING{STRING_WITH_LEN("<=>")};
case SCALAR_CMP_LT: return LEX_CSTRING{STRING_WITH_LEN("<")};
case SCALAR_CMP_LE: return LEX_CSTRING{STRING_WITH_LEN("<=")};
- case SCALAR_CMP_GE: return LEX_CSTRING{STRING_WITH_LEN(">")};
- case SCALAR_CMP_GT: return LEX_CSTRING{STRING_WITH_LEN(">=")};
+ case SCALAR_CMP_GE: return LEX_CSTRING{STRING_WITH_LEN(">=")};
+ case SCALAR_CMP_GT: return LEX_CSTRING{STRING_WITH_LEN(">")};
}
DBUG_ASSERT(0);
return LEX_CSTRING{STRING_WITH_LEN("<?>")};
@@ -3672,6 +3675,9 @@ public:
static const Type_handler *handler_by_name(THD *thd, const LEX_CSTRING &name);
static const Type_handler *handler_by_name_or_error(THD *thd,
const LEX_CSTRING &name);
+ static const Type_handler *handler_by_log_event_data_type(
+ THD *thd,
+ const Log_event_data_type &type);
static const Type_handler *odbc_literal_type_handler(const LEX_CSTRING *str);
static const Type_handler *blob_type_handler(uint max_octet_length);
static const Type_handler *string_type_handler(uint max_octet_length);
@@ -3689,7 +3695,6 @@ public:
static const Type_handler *blob_type_handler(const Item *item);
static const Type_handler *get_handler_by_field_type(enum_field_types type);
static const Type_handler *get_handler_by_real_type(enum_field_types type);
- static const Type_handler *get_handler_by_cmp_type(Item_result type);
static const Type_collection *
type_collection_for_aggregation(const Type_handler *h1,
const Type_handler *h2);
@@ -3957,6 +3962,12 @@ public:
{
return false;
}
+
+ virtual Log_event_data_type user_var_log_event_data_type(uint charset_nr) const
+ {
+ return Log_event_data_type({NULL,0}/*data type name*/, result_type(),
+ charset_nr, is_unsigned());
+ }
virtual uint Column_definition_gis_options_image(uchar *buff,
const Column_definition &def)
const
@@ -4257,6 +4268,8 @@ public:
}
virtual bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const= 0;
+ virtual bool Item_bool_rowready_func2_fix_length_and_dec(THD *thd,
+ Item_bool_rowready_func2 *func) const;
virtual bool Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &name,
Type_handler_hybrid_field_type *,
diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h
index 88c24ba0..65418c34 100644
--- a/sql/sql_type_fixedbin.h
+++ b/sql/sql_type_fixedbin.h
@@ -136,6 +136,21 @@ public:
return Fbt_null(item, false).is_null();
}
+ /*
+ Check at fix_fields() time if any of the items can return a nullable
+ value on conversion to Fbt.
+ */
+ static bool fix_fields_maybe_null_on_conversion_to_fbt(Item **items,
+ uint count)
+ {
+ for (uint i= 0; i < count; i++)
+ {
+ if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(items[i]))
+ return true;
+ }
+ return false;
+ }
+
public:
Fbt(Item *item, bool *error, bool warn= true)
@@ -1534,6 +1549,16 @@ public:
Fbt_null na(a), nb(b);
return !na.is_null() && !nb.is_null() && !na.cmp(nb);
}
+ bool Item_bool_rowready_func2_fix_length_and_dec(THD *thd,
+ Item_bool_rowready_func2 *func) const override
+ {
+ if (Type_handler::Item_bool_rowready_func2_fix_length_and_dec(thd, func))
+ return true;
+ if (!func->maybe_null() &&
+ Fbt::fix_fields_maybe_null_on_conversion_to_fbt(func->arguments(), 2))
+ func->set_maybe_null();
+ return false;
+ }
bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name,
Type_handler_hybrid_field_type *h,
Type_all_attributes *attr,
@@ -1715,6 +1740,9 @@ public:
bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override
{
+ if (!func->maybe_null() &&
+ Fbt::fix_fields_maybe_null_on_conversion_to_fbt(func->arguments(), 3))
+ func->set_maybe_null();
return false;
}
longlong Item_func_between_val_int(Item_func_between *func) const override
@@ -1737,6 +1765,10 @@ public:
Item_func_in *func)
const override
{
+ if (!func->maybe_null() &&
+ Fbt::fix_fields_maybe_null_on_conversion_to_fbt(func->arguments(),
+ func->argument_count()))
+ func->set_maybe_null();
if (func->compatible_types_scalar_bisection_possible())
{
return func->value_list_convert_const_to_int(thd) ||
diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h
index d86d1181..14d3d062 100644
--- a/sql/sql_type_geom.h
+++ b/sql/sql_type_geom.h
@@ -82,6 +82,13 @@ public:
Field *make_conversion_table_field(MEM_ROOT *root,
TABLE *table, uint metadata,
const Field *target) const override;
+ Log_event_data_type user_var_log_event_data_type(uint charset_nr)
+ const override
+ {
+ return Log_event_data_type(name().lex_cstring(), result_type(),
+ charset_nr, false/*unsigned*/);
+ }
+
uint Column_definition_gis_options_image(uchar *buff,
const Column_definition &def)
const override;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 1be0bbcd..145299ac 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -587,7 +587,8 @@ int mysql_update(THD *thd,
select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
if (unlikely(error || !limit || thd->is_error() ||
- (select && select->check_quick(thd, safe_update, limit))))
+ (select && select->check_quick(thd, safe_update, limit,
+ Item_func::BITMAP_ALL))))
{
query_plan.set_impossible_where();
if (thd->lex->describe || thd->lex->analyze_stmt)
@@ -2444,7 +2445,8 @@ loop_end:
group.direction= ORDER::ORDER_ASC;
group.item= (Item**) temp_fields.head_ref();
- tmp_param->quick_group= 1;
+ tmp_param->init();
+ tmp_param->tmp_name="update";
tmp_param->field_count= temp_fields.elements;
tmp_param->func_count= temp_fields.elements - 1;
calc_group_buffer(tmp_param, &group);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 08208048..d9422272 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -297,7 +297,8 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
{
if (!tbl->with && tbl->select_lex)
- tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
+ tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
+ NULL);
/*
Ensure that we have some privileges on this table, more strict check
will be done on column level after preparation,
@@ -1005,7 +1006,8 @@ static int mysql_register_view(THD *thd, DDL_LOG_STATE *ddl_log_state,
{
Sql_mode_save_for_frm_handling sql_mode_save(thd);
- lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL |
+ lex->unit.print(&view_query, enum_query_type(QT_FOR_FRM |
+ QT_VIEW_INTERNAL |
QT_ITEM_ORIGINAL_FUNC_NULLIF |
QT_NO_WRAPPERS_FOR_TVC_IN_VIEW));
lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET |
@@ -1725,7 +1727,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
objects of the view.
*/
if (!(table->view_sctx= (Security_context *)
- thd->stmt_arena->calloc(sizeof(Security_context))))
+ thd->active_stmt_arena_to_use()->calloc(sizeof(Security_context))))
goto err;
security_ctx= table->view_sctx;
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ffa70dea..dcf0e995 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -733,7 +733,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> ACTION /* SQL-2003-N */
%token <kwd> ADMIN_SYM /* SQL-2003-N */
%token <kwd> ADDDATE_SYM /* MYSQL-FUNC */
-%token <kwd> ADD_MONTHS_SYM /* Oracle FUNC*/
%token <kwd> AFTER_SYM /* SQL-2003-N */
%token <kwd> AGAINST
%token <kwd> AGGREGATE_SYM
@@ -809,7 +808,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> DATAFILE_SYM
%token <kwd> DATA_SYM /* SQL-2003-N */
%token <kwd> DATETIME
-%token <kwd> DATE_FORMAT_SYM /* MYSQL-FUNC */
%token <kwd> DATE_SYM /* SQL-2003-R, Oracle-R, PLSQL-R */
%token <kwd> DAY_SYM /* SQL-2003-R */
%token <kwd> DEALLOCATE_SYM /* SQL-2003-R */
@@ -961,7 +959,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> NATIONAL_SYM /* SQL-2003-R */
%token <kwd> NCHAR_SYM /* SQL-2003-R */
%token <kwd> NEVER_SYM /* MySQL */
-%token <kwd> NEW_SYM /* SQL-2003-R */
%token <kwd> NEXT_SYM /* SQL-2003-N */
%token <kwd> NEXTVAL_SYM /* PostgreSQL sequence function */
%token <kwd> NOCACHE_SYM
@@ -1124,7 +1121,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> TRIGGERS_SYM
%token <kwd> TRIM_ORACLE
%token <kwd> TRUNCATE_SYM
-%token <kwd> TYPES_SYM
%token <kwd> TYPE_SYM /* SQL-2003-N */
%token <kwd> UDF_RETURNS_SYM
%token <kwd> UNBOUNDED_SYM /* SQL-2011-N */
@@ -1315,6 +1311,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <ident_sys>
IDENT_sys
+ ident_func
ident
label_ident
sp_decl_ident
@@ -1339,6 +1336,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
IDENT_cli
ident_cli
ident_cli_set_usual_case
+ ident_cli_func
%type <ident_sys_ptr>
ident_sys_alloc
@@ -1353,6 +1351,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
keyword_sp_block_section
keyword_sp_decl
keyword_sp_head
+ keyword_func_sp_var_and_label
+ keyword_func_sp_var_not_label
keyword_sp_var_and_label
keyword_sp_var_not_label
keyword_sysvar_name
@@ -1833,8 +1833,17 @@ rule:
%type <spvar> sp_param_anchored
%type <for_loop> sp_for_loop_index_and_bounds
%type <for_loop_bounds> sp_for_loop_bounds
-%type <trim> trim_operands
-%type <substring_spec> substring_operands
+
+%type <trim>
+ trim_operands
+ trim_operands_regular
+ trim_operands_special
+
+%type <substring_spec>
+ substring_operands
+ substring_operands_regular
+ substring_operands_special
+
%type <num> opt_sp_for_loop_direction
%type <spvar_mode> sp_parameter_type
%type <index_hint> index_hint_type
@@ -7176,11 +7185,7 @@ alter:
| ALTER SEQUENCE_SYM opt_if_exists
{
LEX *lex= Lex;
- lex->name= null_clex_str;
- lex->table_type= TABLE_TYPE_UNKNOWN;
lex->sql_command= SQLCOM_ALTER_SEQUENCE;
- lex->create_info.init();
- lex->no_write_to_binlog= 0;
DBUG_ASSERT(!lex->m_sql_cmd);
if (Lex->main_select_push())
MYSQL_YYABORT;
@@ -9656,8 +9661,16 @@ explicit_cursor_attr:
trim_operands:
+ trim_operands_regular
+ | trim_operands_special
+ ;
+
+trim_operands_regular:
expr { $$.set(TRIM_BOTH, $1); }
- | LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
+ ;
+
+trim_operands_special:
+ LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
| TRAILING expr FROM expr { $$.set(TRIM_TRAILING, $2, $4); }
| BOTH expr FROM expr { $$.set(TRIM_BOTH, $2, $4); }
| LEADING FROM expr { $$.set(TRIM_LEADING, $3); }
@@ -10067,6 +10080,11 @@ function_call_keyword:
;
substring_operands:
+ substring_operands_regular
+ | substring_operands_special
+ ;
+
+substring_operands_regular:
expr ',' expr ',' expr
{
$$= Lex_substring_spec_st::init($1, $3, $5);
@@ -10075,7 +10093,10 @@ substring_operands:
{
$$= Lex_substring_spec_st::init($1, $3);
}
- | expr FROM expr FOR_SYM expr
+ ;
+
+substring_operands_special:
+ expr FROM expr FOR_SYM expr
{
$$= Lex_substring_spec_st::init($1, $3, $5);
}
@@ -10099,14 +10120,7 @@ substring_operands:
discouraged.
*/
function_call_nonkeyword:
- ADD_MONTHS_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5,
- INTERVAL_MONTH, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ADDDATE_SYM '(' expr ',' expr ')'
+ ADDDATE_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5,
INTERVAL_DAY, 0);
@@ -10145,18 +10159,6 @@ function_call_nonkeyword:
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | DATE_FORMAT_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DATE_FORMAT_SYM '(' expr ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5, $7);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
| EXTRACT_SYM '(' interval FROM expr ')'
{
$$=new (thd->mem_root) Item_extract(thd, $3, $5);
@@ -10282,13 +10284,6 @@ function_call_nonkeyword:
MYSQL_YYABORT;
}
|
- COLUMN_CHECK_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_dyncol_check(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
COLUMN_CREATE_SYM '(' dyncall_create_list ')'
{
$$= create_func_dyncol_create(thd, *$3);
@@ -10323,43 +10318,12 @@ function_call_conflict:
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | COALESCE '(' expr_list ')'
- {
- $$= new (thd->mem_root) Item_func_coalesce(thd, *$3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | COLLATION_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_collation(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DATABASE '(' ')'
- {
- $$= new (thd->mem_root) Item_func_database(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
| IF_SYM '(' expr ',' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_if(thd, $3, $5, $7);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | FORMAT_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_format(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | FORMAT_SYM '(' expr ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_format(thd, $3, $5, $7);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
/* LAST_VALUE here conflicts with the definition for window functions.
We have these 2 separate rules to remove the shift/reduce conflict.
*/
@@ -10381,25 +10345,12 @@ function_call_conflict:
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | MICROSECOND_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_microsecond(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
| MOD_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_mod(thd, $3, $5);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | OLD_PASSWORD_SYM '(' expr ')'
- {
- $$= new (thd->mem_root)
- Item_func_password(thd, $3, Item_func_password::OLD);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
| PASSWORD_SYM '(' expr ')'
{
Item* i1;
@@ -10408,12 +10359,6 @@ function_call_conflict:
MYSQL_YYABORT;
$$= i1;
}
- | QUARTER_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_quarter(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
| REPEAT_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_repeat(thd, $3, $5);
@@ -10426,38 +10371,12 @@ function_call_conflict:
make_item_func_replace(thd, $3, $5, $7))))
MYSQL_YYABORT;
}
- | REVERSE_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_reverse(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ROW_COUNT_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_func_row_count(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
- Lex->safe_to_cache_query= 0;
- }
| TRUNCATE_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_round(thd, $3, $5, 1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | WEEK_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_week(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEEK_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_week(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
| WEIGHT_STRING_SYM '(' expr opt_ws_levels ')'
{
$$= new (thd->mem_root) Item_func_weight_string(thd, $3, 0, 0, $4);
@@ -10503,7 +10422,7 @@ function_call_conflict:
in sql/item_create.cc
*/
function_call_generic:
- IDENT_sys '('
+ ident_func '('
{
#ifdef HAVE_DLOPEN
udf_func *udf= 0;
@@ -10541,7 +10460,9 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
- if ((builder= native_functions_hash.find(thd, $1)))
+ builder= Schema::find_implied(thd)->
+ find_native_function_builder(thd, $1);
+ if (builder)
{
item= builder->create_func(thd, &$1, $4);
}
@@ -10605,6 +10526,43 @@ function_call_generic:
if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, &$5, $7))))
MYSQL_YYABORT;
}
+ | ident_cli '.' REPLACE '(' opt_expr_list ')'
+ {
+ if (unlikely(!($$= Lex->make_item_func_replace(thd, $1, $3, $5))))
+ MYSQL_YYABORT;
+ }
+ | ident_cli '.' SUBSTRING '(' opt_expr_list ')'
+ {
+ if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5))))
+ MYSQL_YYABORT;
+ }
+ | ident_cli '.' SUBSTRING '(' substring_operands_special ')'
+ {
+ if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5))))
+ MYSQL_YYABORT;
+ }
+ | ident_cli '.' TRIM '(' opt_expr_list ')'
+ {
+ if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5))))
+ MYSQL_YYABORT;
+ }
+ | ident_cli '.' TRIM '(' trim_operands_special ')'
+ {
+ if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5))))
+ MYSQL_YYABORT;
+ }
+ /*
+ We don't add a qualified syntax for TRIM_ORACLE here,
+ as this syntax is not absolutely required:
+ SELECT mariadb_schema.TRIM_ORACLE(..);
+ What absolutely required is only:
+ SELECT mariadb_schema.TRIM(..);
+ Adding a qualified syntax for TRIM_ORACLE would be tricky because
+ it is a non-reserved keyword. To avoid new shift/reduce conflicts
+ it would require grammar changes, like introducing a new rule
+ ident_step2_cli (which would include everything that ident_cli
+ includes but TRIM_ORACLE).
+ */
;
fulltext_options:
@@ -15159,10 +15117,7 @@ with_column_list:
ident_sys_alloc:
ident_cli
{
- void *buf= thd->alloc(sizeof(Lex_ident_sys));
- if (!buf)
- MYSQL_YYABORT;
- $$= new (buf) Lex_ident_sys(thd, &$1);
+ $$= new (thd->mem_root) Lex_ident_sys(thd, &$1);
}
;
@@ -15383,6 +15338,22 @@ IDENT_sys:
}
;
+ident_cli_func:
+ IDENT
+ | IDENT_QUOTED
+ | keyword_func_sp_var_and_label { $$= $1; }
+ | keyword_func_sp_var_not_label { $$= $1; }
+ ;
+
+ident_func:
+ ident_cli_func
+ {
+ if (unlikely(thd->to_ident_sys_alloc(&$$, &$1)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
TEXT_STRING_sys:
TEXT_STRING
{
@@ -15606,7 +15577,8 @@ non_reserved_keyword_udt:
TODO: check if some of them can migrate to keyword_sp_var_and_label.
*/
keyword_sp_var_not_label:
- ASCII_SYM
+ keyword_func_sp_var_not_label
+ | ASCII_SYM
| BACKUP_SYM
| BINLOG_SYM
| BYTE_SYM
@@ -15614,7 +15586,6 @@ keyword_sp_var_not_label:
| CHECKSUM_SYM
| CHECKPOINT_SYM
| COLUMN_ADD_SYM
- | COLUMN_CHECK_SYM
| COLUMN_CREATE_SYM
| COLUMN_DELETE_SYM
| COLUMN_GET_SYM
@@ -15626,7 +15597,6 @@ keyword_sp_var_not_label:
| EXECUTE_SYM
| FLUSH_SYM
| FOLLOWING_SYM
- | FORMAT_SYM
| GET_SYM
| HELP_SYM
| HOST_SYM
@@ -15780,21 +15750,15 @@ keyword_cast_type:
;
-/*
- These keywords are fine for both SP variable names and SP labels.
-*/
-keyword_sp_var_and_label:
- ACTION
+keyword_func_sp_var_and_label:
+ ACTION
| ACCOUNT_SYM
- | ADDDATE_SYM
- | ADD_MONTHS_SYM
| ADMIN_SYM
| AFTER_SYM
| AGAINST
| AGGREGATE_SYM
| ALGORITHM_SYM
| ALWAYS_SYM
- | ANY_SYM
| AT_SYM
| ATOMIC_SYM
| AUTHORS_SYM
@@ -15802,7 +15766,6 @@ keyword_sp_var_and_label:
| AUTOEXTEND_SIZE_SYM
| AUTO_SYM
| AVG_ROW_LENGTH
- | AVG_SYM
| BLOCK_SYM
| BODY_MARIADB_SYM
| BTREE_SYM
@@ -15815,7 +15778,6 @@ keyword_sp_var_and_label:
| CLIENT_SYM
| CLASS_ORIGIN_SYM
| COALESCE
- | CODE_SYM
| COLLATION_SYM
| COLUMN_NAME_SYM
| COLUMNS
@@ -15841,16 +15803,15 @@ keyword_sp_var_and_label:
| CURSOR_NAME_SYM
| CYCLE_SYM
| DATA_SYM
+ | DATABASE
| DATAFILE_SYM
- | DATE_FORMAT_SYM
- | DAY_SYM
| DEFINER_SYM
| DELAY_KEY_WRITE_SYM
| DES_KEY_FILE
| DIAGNOSTICS_SYM
+ | DISCARD
| DIRECTORY_SYM
| DISABLE_SYM
- | DISCARD
| DISK_SYM
| DUMPFILE
| DUPLICATE_SYM
@@ -15858,6 +15819,11 @@ keyword_sp_var_and_label:
| ELSEIF_ORACLE_SYM
| ELSIF_MARIADB_SYM
| EMPTY_SYM
+ | EXPIRE_SYM
+ | EXPORT_SYM
+ | EXTENDED_SYM
+ | EXTENT_SIZE_SYM
+ | ENABLE_SYM
| ENDS_SYM
| ENGINE_SYM
| ENGINES_SYM
@@ -15870,29 +15836,21 @@ keyword_sp_var_and_label:
| EXCEPTION_MARIADB_SYM
| EXCHANGE_SYM
| EXPANSION_SYM
- | EXPIRE_SYM
- | EXPORT_SYM
- | EXTENDED_SYM
- | EXTENT_SIZE_SYM
| FAULTS_SYM
| FAST_SYM
- | FOUND_SYM
- | ENABLE_SYM
| FEDERATED_SYM
- | FULL
| FILE_SYM
| FIRST_SYM
+ | FOUND_SYM
+ | FULL
| GENERAL
| GENERATED_SYM
- | GET_FORMAT
| GRANTS
| GOTO_MARIADB_SYM
| HASH_SYM
| HARD_SYM
| HISTORY_SYM
| HOSTS_SYM
- | HOUR_SYM
- | ID_SYM
| IDENTIFIED_SYM
| IGNORE_SERVER_IDS_SYM
| INCREMENT_SYM
@@ -15910,9 +15868,7 @@ keyword_sp_var_and_label:
| INVISIBLE_SYM
| JSON_TABLE_SYM
| KEY_BLOCK_SIZE
- | LAST_VALUE
| LAST_SYM
- | LASTVAL_SYM
| LEAVES
| LESS_SYM
| LEVEL_SYM
@@ -15954,7 +15910,6 @@ keyword_sp_var_and_label:
| MESSAGE_TEXT_SYM
| MICROSECOND_SYM
| MIGRATE_SYM
- | MINUTE_SYM
%ifdef MARIADB
| MINUS_ORACLE_SYM
%endif
@@ -15963,7 +15918,6 @@ keyword_sp_var_and_label:
| MODIFY_SYM
| MODE_SYM
| MONITOR_SYM
- | MONTH_SYM
| MUTEX_SYM
| MYSQL_SYM
| MYSQL_ERRNO_SYM
@@ -15971,8 +15925,6 @@ keyword_sp_var_and_label:
| NESTED_SYM
| NEVER_SYM
| NEXT_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | NEXTVAL_SYM
- | NEW_SYM
| NOCACHE_SYM
| NOCYCLE_SYM
| NOMINVALUE_SYM
@@ -15988,7 +15940,6 @@ keyword_sp_var_and_label:
| ONLINE_SYM
| ONLY_SYM
| ORDINALITY_SYM
- | OVERLAPS_SYM
| PACKAGE_MARIADB_SYM
| PACK_KEYS_SYM
| PAGE_SYM
@@ -16020,10 +15971,10 @@ keyword_sp_var_and_label:
| REDOFILE_SYM
| REDUNDANT_SYM
| RELAY
- | RELAYLOG_SYM
| RELAY_LOG_FILE_SYM
| RELAY_LOG_POS_SYM
| RELAY_THREAD
+ | RELAYLOG_SYM
| RELOAD
| REORGANIZE_SYM
| REPEATABLE_SYM
@@ -16038,20 +15989,15 @@ keyword_sp_var_and_label:
| REVERSE_SYM
| ROLLUP_SYM
| ROUTINE_SYM
+ | ROW_COUNT_SYM
| ROWCOUNT_SYM
| ROWTYPE_MARIADB_SYM
- | ROW_COUNT_SYM
| ROW_FORMAT_SYM
-%ifdef MARIADB
- | ROWNUM_SYM
-%endif
| RTREE_SYM
| SCHEDULE_SYM
| SCHEMA_NAME_SYM
- | SECOND_SYM
| SEQUENCE_SYM
| SERIALIZABLE_SYM
- | SETVAL_SYM
| SIMPLE_SYM
| SHARE_SYM
| SKIP_SYM
@@ -16059,7 +16005,6 @@ keyword_sp_var_and_label:
| SLOW
| SNAPSHOT_SYM
| SOFT_SYM
- | SOUNDS_SYM
| SOURCE_SYM
| SQL_CACHE_SYM
| SQL_BUFFER_RESULT
@@ -16072,7 +16017,6 @@ keyword_sp_var_and_label:
| STORAGE_SYM
| STRING_SYM
| SUBCLASS_ORIGIN_SYM
- | SUBDATE_SYM
| SUBJECT_SYM
| SUBPARTITION_SYM
| SUBPARTITIONS_SYM
@@ -16080,9 +16024,6 @@ keyword_sp_var_and_label:
| SUSPEND_SYM
| SWAPS_SYM
| SWITCHES_SYM
-%ifdef MARIADB
- | SYSDATE
-%endif
| SYSTEM
| SYSTEM_TIME_SYM
| TABLE_NAME_SYM
@@ -16096,10 +16037,6 @@ keyword_sp_var_and_label:
| TRANSACTIONAL_SYM
| THREADS_SYM
| TRIGGERS_SYM
- | TRIM_ORACLE
- | TIMESTAMP_ADD
- | TIMESTAMP_DIFF
- | TYPES_SYM
| TYPE_SYM
| UDF_RETURNS_SYM
| UNCOMMITTED_SYM
@@ -16108,23 +16045,61 @@ keyword_sp_var_and_label:
| UNDOFILE_SYM
| UNKNOWN_SYM
| UNTIL_SYM
- | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
| USE_FRM
| VARIABLES
| VERSIONING_SYM
| VIEW_SYM
| VIRTUAL_SYM
| VISIBLE_SYM
- | VALUE_SYM
| WARNINGS
| WAIT_SYM
- | WEEK_SYM
- | WEIGHT_STRING_SYM
| WITHOUT
| WORK_SYM
| X509_SYM
| XML_SYM
| VIA_SYM
+ | WEEK_SYM
+ ;
+
+keyword_func_sp_var_not_label:
+ FORMAT_SYM
+ | COLUMN_CHECK_SYM
+ ;
+/*
+ These keywords are fine for both SP variable names and SP labels.
+*/
+keyword_sp_var_and_label:
+ keyword_func_sp_var_and_label
+ | ADDDATE_SYM
+ | ANY_SYM
+ | AVG_SYM
+ | CODE_SYM
+ | DAY_SYM
+ | GET_FORMAT
+ | HOUR_SYM
+ | ID_SYM
+ | LAST_VALUE
+ | LASTVAL_SYM
+ | MINUTE_SYM
+ | MONTH_SYM
+ | NEXTVAL_SYM
+ | OVERLAPS_SYM
+%ifdef MARIADB
+ | ROWNUM_SYM
+%endif
+ | SECOND_SYM
+ | SETVAL_SYM
+ | SOUNDS_SYM
+ | SUBDATE_SYM
+%ifdef MARIADB
+ | SYSDATE
+%endif
+ | TRIM_ORACLE
+ | TIMESTAMP_ADD
+ | TIMESTAMP_DIFF
+ | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
+ | VALUE_SYM
+ | WEIGHT_STRING_SYM
;
@@ -16165,7 +16140,6 @@ reserved_keyword_udt_not_param_type:
| CURRENT_USER
| CURRENT_ROLE
| CURTIME
- | DATABASE
| DATABASES
| DATE_ADD_INTERVAL
| DATE_SUB_INTERVAL
diff --git a/sql/structs.h b/sql/structs.h
index a77bb8cb..318df056 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -34,6 +34,7 @@ struct TABLE;
class Type_handler;
class Field;
class Index_statistics;
+struct Lex_ident_cli_st;
class THD;
@@ -902,12 +903,6 @@ public:
}
Item *make_item_func_trim_std(THD *thd) const;
Item *make_item_func_trim_oracle(THD *thd) const;
- /*
- This method is still used to handle LTRIM and RTRIM,
- while the special syntax TRIM(... BOTH|LEADING|TRAILING)
- is now handled by Schema::make_item_func_trim().
- */
- Item *make_item_func_trim(THD *thd) const;
};
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 0df11e0b..dce04c59 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1037,11 +1037,10 @@ static Sys_var_charptr_fscs Sys_datadir(
static Sys_var_dbug Sys_dbug(
"debug", "Built-in DBUG debugger", sys_var::SESSION,
CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(check_has_super), ON_UPDATE(0),
- DEPRECATED("'@@debug_dbug'")); // since 5.5.37
+ ON_CHECK(check_has_super));
static Sys_var_dbug Sys_debug_dbug(
- "debug_dbug", "Built-in DBUG debugger", sys_var::SESSION,
+ "debug_dbug", "Built-in DBUG debugger. Alias for --debug", sys_var::SESSION,
CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_has_super));
#endif
@@ -1538,7 +1537,7 @@ static Sys_var_bit Sys_log_slow_slave_statements(
static Sys_var_ulong Sys_log_warnings(
"log_warnings",
- "Log some not critical warnings to the general log file."
+ "Log some non critical warnings to the error log."
"Value can be between 0 and 11. Higher values mean more verbosity",
SESSION_VAR(log_warnings),
CMD_LINE(OPT_ARG, 'W'),
@@ -2938,6 +2937,19 @@ static Sys_var_ulong Sys_optimizer_trace_max_mem_size(
SESSION_VAR(optimizer_trace_max_mem_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, ULONG_MAX), DEFAULT(1024 * 1024), BLOCK_SIZE(1));
+static Sys_var_ulong Sys_optimizer_adjust_secondary_key_costs(
+ "optimizer_adjust_secondary_key_costs",
+ "0 = No changes. "
+ "1 = Update secondary key costs for ranges to be at least 5x of clustered "
+ "primary key costs. "
+ "2 = Remove 'max_seek optimization' for secondary keys and slight "
+ "adjustment of filter cost. "
+ "This option will be deleted in MariaDB 11.0 as it is not needed with the "
+ "new 11.0 optimizer.",
+ SESSION_VAR(optimizer_adjust_secondary_key_costs), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 2), DEFAULT(0), BLOCK_SIZE(1));
+
+
static Sys_var_charptr_fscs Sys_pid_file(
"pid_file", "Pid file used by safe_mysqld",
READ_ONLY GLOBAL_VAR(pidfile_name_ptr), CMD_LINE(REQUIRED_ARG),
@@ -3348,8 +3360,9 @@ Sys_secure_auth(
"secure_auth",
"Disallow authentication for accounts that have old (pre-4.1) "
"passwords",
- GLOBAL_VAR(opt_secure_auth), CMD_LINE(OPT_ARG),
- DEFAULT(TRUE));
+ GLOBAL_VAR(opt_secure_auth), CMD_LINE(OPT_ARG, OPT_SECURE_AUTH),
+ DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("")); // since 10.6.17
static bool check_require_secure_transport(sys_var *self, THD *thd, set_var *var)
{
@@ -3599,13 +3612,6 @@ static bool fix_rpl_semi_sync_master_wait_point(sys_var *self, THD *thd,
return false;
}
-static bool fix_rpl_semi_sync_master_wait_no_slave(sys_var *self, THD *thd,
- enum_var_type type)
-{
- repl_semisync_master.check_and_switch();
- return false;
-}
-
static Sys_var_on_access_global<Sys_var_mybool,
PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_ENABLED>
Sys_semisync_master_enabled(
@@ -3632,12 +3638,11 @@ static Sys_var_on_access_global<Sys_var_mybool,
PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE>
Sys_semisync_master_wait_no_slave(
"rpl_semi_sync_master_wait_no_slave",
- "Wait until timeout when no semi-synchronous replication slave "
- "available (enabled by default).",
+ "Wait until timeout when no semi-synchronous replication slave is "
+ "available.",
GLOBAL_VAR(rpl_semi_sync_master_wait_no_slave),
CMD_LINE(OPT_ARG), DEFAULT(TRUE),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_rpl_semi_sync_master_wait_no_slave));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0));
static Sys_var_on_access_global<Sys_var_ulong,
PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_TRACE_LEVEL>
@@ -3664,13 +3669,6 @@ Sys_semisync_master_wait_point(
NO_MUTEX_GUARD, NOT_IN_BINLOG,ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_master_wait_point));
-static bool fix_rpl_semi_sync_slave_enabled(sys_var *self, THD *thd,
- enum_var_type type)
-{
- repl_semisync_slave.set_slave_enabled(rpl_semi_sync_slave_enabled != 0);
- return false;
-}
-
static bool fix_rpl_semi_sync_slave_trace_level(sys_var *self, THD *thd,
enum_var_type type)
{
@@ -3698,10 +3696,9 @@ static Sys_var_on_access_global<Sys_var_mybool,
Sys_semisync_slave_enabled(
"rpl_semi_sync_slave_enabled",
"Enable semi-synchronous replication slave (disabled by default).",
- GLOBAL_VAR(rpl_semi_sync_slave_enabled),
+ GLOBAL_VAR(global_rpl_semi_sync_slave_enabled),
CMD_LINE(OPT_ARG), DEFAULT(FALSE),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_rpl_semi_sync_slave_enabled));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0));
static Sys_var_on_access_global<Sys_var_ulong,
PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL>
@@ -3887,6 +3884,7 @@ static const char *old_mode_names[]=
"UTF8_IS_UTF8MB3",
"IGNORE_INDEX_ONLY_FOR_JOIN",
"COMPAT_5_1_CHECKSUM",
+ "NO_NULL_COLLATION_IDS",
0
};
@@ -5652,7 +5650,7 @@ Sys_slave_net_timeout(
*/
ulonglong Sys_var_multi_source_ulonglong::
-get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const
+get_master_info_ulonglong_value(THD *thd) const
{
Master_info *mi;
ulonglong res= 0; // Default value
@@ -5660,7 +5658,7 @@ get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const
if ((mi= get_master_info(&thd->variables.default_master_connection,
Sql_condition::WARN_LEVEL_WARN)))
{
- res= *((ulonglong*) (((uchar*) mi) + master_info_offset));
+ res= (mi->*mi_accessor_func)();
mi->release();
}
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -5730,7 +5728,7 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi)
static Sys_var_multi_source_ulonglong Sys_slave_skip_counter(
"sql_slave_skip_counter", "Skip the next N events from the master log",
SESSION_VAR(slave_skip_counter), NO_CMD_LINE,
- MASTER_INFO_VAR(rli.slave_skip_counter),
+ &Master_info::get_slave_skip_counter,
VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
ON_UPDATE(update_slave_skip_counter));
@@ -5746,7 +5744,7 @@ static Sys_var_multi_source_ulonglong Sys_max_relay_log_size(
"relay log will be rotated automatically when the size exceeds this "
"value. If 0 at startup, it's set to max_binlog_size",
SESSION_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG),
- MASTER_INFO_VAR(rli.max_relay_log_size),
+ &Master_info::get_max_relay_log_size,
VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE),
ON_UPDATE(update_max_relay_log_size));
diff --git a/sql/sys_vars.inl b/sql/sys_vars.inl
index 385ad897..2fd6d042 100644
--- a/sql/sys_vars.inl
+++ b/sql/sys_vars.inl
@@ -2378,10 +2378,10 @@ public:
like sql_slave_skip_counter are GLOBAL.
*/
-#define MASTER_INFO_VAR(X) my_offsetof(Master_info, X), sizeof(((Master_info *)0x10)->X)
class Sys_var_multi_source_ulonglong;
class Master_info;
+typedef ulonglong (Master_info::*mi_ulonglong_accessor_function)(void);
typedef bool (*on_multi_source_update_function)(sys_var *self, THD *thd,
Master_info *mi);
bool update_multi_source_variable(sys_var *self,
@@ -2390,26 +2390,23 @@ bool update_multi_source_variable(sys_var *self,
class Sys_var_multi_source_ulonglong :public Sys_var_ulonglong
{
- ptrdiff_t master_info_offset;
+ mi_ulonglong_accessor_function mi_accessor_func;
on_multi_source_update_function update_multi_source_variable_func;
public:
Sys_var_multi_source_ulonglong(const char *name_arg,
const char *comment, int flag_args,
ptrdiff_t off, size_t size,
CMD_LINE getopt,
- ptrdiff_t master_info_offset_arg,
- size_t master_info_arg_size,
+ mi_ulonglong_accessor_function mi_accessor_arg,
ulonglong min_val, ulonglong max_val,
ulonglong def_val, uint block_size,
on_multi_source_update_function on_update_func)
:Sys_var_ulonglong(name_arg, comment, flag_args, off, size,
getopt, min_val, max_val, def_val, block_size,
0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable),
- master_info_offset(master_info_offset_arg),
+ mi_accessor_func(mi_accessor_arg),
update_multi_source_variable_func(on_update_func)
- {
- SYSVAR_ASSERT(master_info_arg_size == size);
- }
+ { }
bool global_update(THD *thd, set_var *var)
{
return session_update(thd, var);
@@ -2423,7 +2420,7 @@ public:
{
ulonglong *tmp, res;
tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset);
- res= get_master_info_ulonglong_value(thd, master_info_offset);
+ res= get_master_info_ulonglong_value(thd);
*tmp= res;
return (uchar*) tmp;
}
@@ -2431,7 +2428,7 @@ public:
{
return session_value_ptr(thd, base);
}
- ulonglong get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const;
+ ulonglong get_master_info_ulonglong_value(THD *thd) const;
bool update_variable(THD *thd, Master_info *mi)
{
return update_multi_source_variable_func(this, thd, mi);
diff --git a/sql/table.cc b/sql/table.cc
index b85d23b2..88b65fe0 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -49,6 +49,7 @@
#ifdef WITH_WSREP
#include "wsrep_schema.h"
#endif
+#include "log_event.h" // MAX_TABLE_MAP_ID
/* For MySQL 5.7 virtual fields */
#define MYSQL57_GENERATED_FIELD 128
@@ -108,7 +109,7 @@ LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")};
*/
static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
-static std::atomic<ulong> last_table_id;
+static std::atomic<ulonglong> last_table_id;
/* Functions defined in this file */
@@ -384,17 +385,20 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
DBUG_EXECUTE_IF("simulate_big_table_id",
if (last_table_id < UINT_MAX32)
- last_table_id= UINT_MAX32 - 1;);
+ last_table_id= UINT_MAX32-1;);
/*
- There is one reserved number that cannot be used. Remember to
- change this when 6-byte global table id's are introduced.
+ Replication is using 6 bytes as table_map_id. Ensure that
+ the 6 lowest bytes are not 0.
+ We also have to ensure that we do not use the special value
+ UINT_MAX32 as this is used to mark a dummy event row event. See
+ comments in Rows_log_event::Rows_log_event().
*/
do
{
share->table_map_id=
last_table_id.fetch_add(1, std::memory_order_relaxed);
- } while (unlikely(share->table_map_id == ~0UL ||
- share->table_map_id == 0));
+ } while (unlikely((share->table_map_id & MAX_TABLE_MAP_ID) == 0) ||
+ unlikely((share->table_map_id & MAX_TABLE_MAP_ID) == UINT_MAX32));
}
DBUG_RETURN(share);
}
@@ -457,7 +461,7 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
table_map_id is also used for MERGE tables to suppress repeated
compatibility checks.
*/
- share->table_map_id= (ulong) thd->query_id;
+ share->table_map_id= (ulonglong) thd->query_id;
DBUG_VOID_RETURN;
}
@@ -1286,12 +1290,11 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
if (keypart->key_part_flag & HA_PART_KEY_SEG)
{
int length= keypart->length/keypart->field->charset()->mbmaxlen;
+ Field *kpf= table->field[keypart->field->field_index];
list_item= new (mem_root) Item_func_left(thd,
- new (mem_root) Item_field(thd, keypart->field),
+ new (mem_root) Item_field(thd, kpf),
new (mem_root) Item_int(thd, length));
list_item->fix_fields(thd, NULL);
- keypart->field->vcol_info=
- table->field[keypart->field->field_index]->vcol_info;
}
else
list_item= new (mem_root) Item_field(thd, keypart->field);
@@ -5958,7 +5961,7 @@ allocate:
/* Create view fields translation table */
if (!(transl=
- (Field_translator*)(thd->stmt_arena->
+ (Field_translator*)(thd->
alloc(select->item_list.elements *
sizeof(Field_translator)))))
{
@@ -7585,7 +7588,7 @@ inline void TABLE::mark_index_columns_for_read(uint index)
always set and sometimes read.
*/
-void TABLE::mark_auto_increment_column()
+void TABLE::mark_auto_increment_column(bool is_insert)
{
DBUG_ASSERT(found_next_number_field);
/*
@@ -7593,7 +7596,8 @@ void TABLE::mark_auto_increment_column()
store() to check overflow of auto_increment values
*/
bitmap_set_bit(read_set, found_next_number_field->field_index);
- bitmap_set_bit(write_set, found_next_number_field->field_index);
+ if (is_insert)
+ bitmap_set_bit(write_set, found_next_number_field->field_index);
if (s->next_number_keypart)
mark_index_columns_for_read(s->next_number_index);
file->column_bitmaps_signal();
@@ -7718,7 +7722,7 @@ void TABLE::mark_columns_needed_for_update()
else
{
if (found_next_number_field)
- mark_auto_increment_column();
+ mark_auto_increment_column(false);
}
if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
@@ -7794,7 +7798,7 @@ void TABLE::mark_columns_needed_for_insert()
triggers->mark_fields_used(TRG_EVENT_INSERT);
}
if (found_next_number_field)
- mark_auto_increment_column();
+ mark_auto_increment_column(true);
if (default_field)
mark_default_fields_for_write(TRUE);
if (s->versioned)
@@ -10467,6 +10471,12 @@ bool Vers_history_point::check_unit(THD *thd)
{
if (!item)
return false;
+ if (item->real_type() == Item::FIELD_ITEM)
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ item->full_name(), "FOR SYSTEM_TIME");
+ return true;
+ }
if (item->fix_fields_if_needed(thd, &item))
return true;
const Type_handler *t= item->this_item()->real_type_handler();
diff --git a/sql/table.h b/sql/table.h
index 12ad29b1..ebf582c1 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -861,7 +861,7 @@ struct TABLE_SHARE
/* 1 if frm version cannot be updated as part of upgrade */
bool keep_original_mysql_version;
- ulong table_map_id; /* for row-based replication */
+ ulonglong table_map_id; /* for row-based replication */
/*
Things that are incompatible between the stored version and the
@@ -1023,7 +1023,7 @@ struct TABLE_SHARE
return (table_category == TABLE_CATEGORY_LOG);
}
- inline ulong get_table_def_version()
+ inline ulonglong get_table_def_version()
{
return table_map_id;
}
@@ -1102,7 +1102,7 @@ struct TABLE_SHARE
@sa TABLE_LIST::is_the_same_definition()
*/
- ulong get_table_ref_version() const
+ ulonglong get_table_ref_version() const
{
return (tmp_table == SYSTEM_TMP_TABLE) ? 0 : table_map_id;
}
@@ -1585,7 +1585,7 @@ public:
void mark_index_columns_no_reset(uint index, MY_BITMAP *bitmap);
void mark_index_columns_for_read(uint index);
void restore_column_maps_after_keyread(MY_BITMAP *backup);
- void mark_auto_increment_column(void);
+ void mark_auto_increment_column(bool insert_fl);
void mark_columns_needed_for_update(void);
void mark_columns_needed_for_delete(void);
void mark_columns_needed_for_insert(void);
@@ -2088,7 +2088,6 @@ public:
void empty() { unit= VERS_TIMESTAMP; item= NULL; }
void print(String *str, enum_query_type, const char *prefix, size_t plen) const;
bool check_unit(THD *thd);
- void bad_expression_data_type_error(const char *type) const;
bool eq(const vers_history_point_t &point) const;
};
@@ -2827,7 +2826,7 @@ struct TABLE_LIST
{ set_table_ref_id(s->get_table_ref_type(), s->get_table_ref_version()); }
inline void set_table_ref_id(enum_table_ref_type table_ref_type_arg,
- ulong table_ref_version_arg)
+ ulonglong table_ref_version_arg)
{
m_table_ref_type= table_ref_type_arg;
m_table_ref_version= table_ref_version_arg;
@@ -2982,7 +2981,7 @@ private:
/** See comments for set_table_ref_id() */
enum enum_table_ref_type m_table_ref_type;
/** See comments for set_table_ref_id() */
- ulong m_table_ref_version;
+ ulonglong m_table_ref_version;
};
class Item;
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index 0039c96a..91292e18 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -1213,8 +1213,8 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
}
-int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
+int show_tc_active_instances(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum enum_var_type scope)
{
var->type= SHOW_UINT;
var->value= buff;
diff --git a/sql/table_cache.h b/sql/table_cache.h
index 433df5e0..71704ff2 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -87,8 +87,8 @@ extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
bool no_dups= false);
extern uint tc_records(void);
-int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope);
+int show_tc_active_instances(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum enum_var_type scope);
extern void tc_purge();
extern void tc_add_table(THD *thd, TABLE *table);
extern void tc_release_table(TABLE *table);
diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
index ecbfdde1..fa9abf7b 100644
--- a/sql/temporary_tables.cc
+++ b/sql/temporary_tables.cc
@@ -1588,6 +1588,11 @@ void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl)
{
/* Note: removing current list element doesn't invalidate iterator. */
share->all_tmp_tables.remove(table);
+ /*
+ At least one instance should be left (guaratead by calling this
+ function for table which is opened and the table is under processing)
+ */
+ DBUG_ASSERT(share->all_tmp_tables.front());
free_temporary_table(table);
}
}
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index 90ede81a..8767f698 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -204,6 +204,11 @@ int wsrep_apply_events(THD* thd,
(thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
(ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0);
+ if (ev->get_type_code() == GTID_EVENT)
+ {
+ thd->variables.option_bits &= ~OPTION_GTID_BEGIN;
+ }
+
ev->thd= thd;
exec_res= ev->apply_event(thd->wsrep_rgi);
DBUG_PRINT("info", ("exec_event result: %d", exec_res));
diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc
index d3b4a181..e26cac50 100644
--- a/sql/wsrep_client_service.cc
+++ b/sql/wsrep_client_service.cc
@@ -281,11 +281,18 @@ enum wsrep::provider::status Wsrep_client_service::replay()
original THD state during replication event applying.
*/
THD *replayer_thd= new THD(true, true);
+ // Replace the security context of the replayer with the security context
+ // of the original THD. Since security context class doesn't have proper
+ // copy constructors, we need to store the original one and set it back
+ // before destruction so that THD desctruction doesn't cause double-free
+ // on the replaced security context.
+ Security_context old_ctx = replayer_thd->main_security_ctx;
+ replayer_thd->main_security_ctx = m_thd->main_security_ctx;
replayer_thd->thread_stack= m_thd->thread_stack;
replayer_thd->real_id= pthread_self();
replayer_thd->prior_thr_create_utime=
replayer_thd->start_utime= microsecond_interval_timer();
- replayer_thd->set_command(COM_SLEEP);
+ replayer_thd->mark_connection_idle();
replayer_thd->reset_for_next_command(true);
enum wsrep::provider::status ret;
@@ -297,6 +304,7 @@ enum wsrep::provider::status Wsrep_client_service::replay()
replayer_service.replay_status(ret);
}
+ replayer_thd->main_security_ctx = old_ctx;
delete replayer_thd;
DBUG_RETURN(ret);
}
diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc
index e1508884..8762dd99 100644
--- a/sql/wsrep_dummy.cc
+++ b/sql/wsrep_dummy.cc
@@ -167,3 +167,5 @@ void wsrep_report_bf_lock_wait(const THD*,
void wsrep_thd_set_PA_unsafe(THD*)
{}
+uint32 wsrep_get_domain_id()
+{ return 0;}
diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc
index fb93273d..ecb0e487 100644
--- a/sql/wsrep_high_priority_service.cc
+++ b/sql/wsrep_high_priority_service.cc
@@ -569,6 +569,7 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
THD* thd= m_thd;
thd->variables.option_bits |= OPTION_BEGIN;
+ thd->variables.option_bits |= OPTION_GTID_BEGIN;
thd->variables.option_bits |= OPTION_NOT_AUTOCOMMIT;
DBUG_ASSERT(thd->wsrep_trx().active());
DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_executing);
@@ -600,6 +601,8 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
thd->wsrep_cs().fragment_applied(ws_meta.seqno());
}
thd_proc_info(thd, "wsrep applied write set");
+
+ thd->variables.option_bits &= ~OPTION_GTID_BEGIN;
DBUG_RETURN(ret);
}
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 0a615228..68649a95 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -584,7 +584,8 @@ my_bool wsrep_ready_get (void)
return ret;
}
-int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff)
+int wsrep_show_ready(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type)
{
var->type= SHOW_MY_BOOL;
var->value= buff;
@@ -1713,7 +1714,13 @@ bool wsrep_sync_wait(THD* thd, enum enum_sql_command command)
return res;
}
-void wsrep_keys_free(wsrep_key_arr_t* key_arr)
+typedef struct wsrep_key_arr
+{
+ wsrep_key_t* keys;
+ size_t keys_len;
+} wsrep_key_arr_t;
+
+static void wsrep_keys_free(wsrep_key_arr_t* key_arr)
{
for (size_t i= 0; i < key_arr->keys_len; ++i)
{
@@ -1729,7 +1736,7 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr)
* @param tables list of tables
* @param keys prepared keys
- * @return true if parent table append was successfull, otherwise false.
+ * @return 0 if parent table append was successful, non-zero otherwise.
*/
bool
wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys)
@@ -1785,6 +1792,8 @@ wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* key
}
exit:
+ DEBUG_SYNC(thd, "wsrep_append_fk_toi_keys_before_close_tables");
+
/* close the table and release MDL locks */
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
@@ -1803,6 +1812,24 @@ exit:
}
}
+ /*
+ MDEV-32938: Check if DDL operation has been killed before.
+
+ It may be that during collecting foreign keys this operation gets BF-aborted
+ by another already-running TOI operation because it got MDL locks on the same
+ table for checking foreign keys.
+ After `close_thread_tables()` has been called it's safe to assume that no-one
+ can BF-abort this operation as it's not holding any MDL locks any more.
+ */
+ if (!fail)
+ {
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ if (thd->killed)
+ {
+ fail= true;
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
+ }
return fail;
}
@@ -2006,18 +2033,43 @@ err:
}
/*
- * Prepare key list from db/table and table_list
+ * Prepare key list from db/table and table_list and append it to Wsrep
+ * with the given key type.
*
* Return zero in case of success, 1 in case of failure.
*/
+int wsrep_append_table_keys(THD* thd,
+ TABLE_LIST* first_table,
+ TABLE_LIST* table_list,
+ Wsrep_service_key_type key_type)
+{
+ wsrep_key_arr_t key_arr= {0, 0};
+ const char* db_name= first_table ? first_table->db.str : NULL;
+ const char* table_name= first_table ? first_table->table_name.str : NULL;
+ int rcode= wsrep_prepare_keys_for_isolation(thd, db_name, table_name,
+ table_list, NULL, &key_arr);
+
+ if (!rcode && key_arr.keys_len)
+ {
+ rcode= wsrep_thd_append_key(thd, key_arr.keys,
+ key_arr.keys_len, key_type);
+ }
+
+ wsrep_keys_free(&key_arr);
+ return rcode;
+}
-bool wsrep_prepare_keys_for_isolation(THD* thd,
- const char* db,
- const char* table,
- const TABLE_LIST* table_list,
- wsrep_key_arr_t* ka)
+extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd,
+ const char* db,
+ const char* table,
+ enum Wsrep_service_key_type key_type)
{
- return wsrep_prepare_keys_for_isolation(thd, db, table, table_list, NULL, ka);
+ wsrep_key_arr_t key_arr = {0, 0};
+ int ret = wsrep_prepare_keys_for_isolation(thd, db, table, NULL, NULL, &key_arr);
+ ret = ret || wsrep_thd_append_key(thd, key_arr.keys,
+ (int)key_arr.keys_len, key_type);
+ wsrep_keys_free(&key_arr);
+ return ret;
}
bool wsrep_prepare_key(const uchar* cache_key, size_t cache_key_len,
@@ -2939,6 +2991,15 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const wsrep::key_array *fk_tables,
const HA_CREATE_INFO *create_info)
{
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ const killed_state killed = thd->killed;
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
+ if (killed)
+ {
+ DBUG_ASSERT(FALSE);
+ return -1;
+ }
+
/*
No isolation for applier or replaying threads.
*/
@@ -3689,8 +3750,7 @@ void* start_wsrep_THD(void *arg)
thd->security_ctx->skip_grants();
/* handle_one_connection() again... */
- thd->proc_info= 0;
- thd->set_command(COM_SLEEP);
+ thd->mark_connection_idle();
thd->init_for_queries();
mysql_mutex_lock(&LOCK_wsrep_slave_threads);
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index 3efe3829..14ea07f4 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -162,7 +162,8 @@ extern char* wsrep_cluster_capabilities;
int wsrep_show_status(THD *thd, SHOW_VAR *var, void *buff,
system_status_var *status_var, enum_var_type scope);
-int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff);
+int wsrep_show_ready(THD *thd, SHOW_VAR *var, void *buff,
+ system_status_var *, enum_var_type);
void wsrep_free_status(THD *thd);
void wsrep_update_cluster_state_uuid(const char* str);
@@ -503,17 +504,10 @@ void wsrep_init_gtid();
bool wsrep_check_gtid_seqno(const uint32&, const uint32&, uint64&);
bool wsrep_get_binlog_gtid_seqno(wsrep_server_gtid_t&);
-typedef struct wsrep_key_arr
-{
- wsrep_key_t* keys;
- size_t keys_len;
-} wsrep_key_arr_t;
-bool wsrep_prepare_keys_for_isolation(THD* thd,
- const char* db,
- const char* table,
- const TABLE_LIST* table_list,
- wsrep_key_arr_t* ka);
-void wsrep_keys_free(wsrep_key_arr_t* key_arr);
+int wsrep_append_table_keys(THD* thd,
+ TABLE_LIST* first_table,
+ TABLE_LIST* table_list,
+ Wsrep_service_key_type key_type);
extern void
wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
diff --git a/sql/wsrep_plugin.cc b/sql/wsrep_plugin.cc
index d23c51b1..63bdc0a4 100644
--- a/sql/wsrep_plugin.cc
+++ b/sql/wsrep_plugin.cc
@@ -18,18 +18,6 @@
#include <mysql/plugin.h>
-static int wsrep_plugin_init(void *p)
-{
- WSREP_DEBUG("wsrep_plugin_init()");
- return 0;
-}
-
-static int wsrep_plugin_deinit(void *p)
-{
- WSREP_DEBUG("wsrep_plugin_deinit()");
- return 0;
-}
-
struct Mysql_replication wsrep_plugin= {
MYSQL_REPLICATION_INTERFACE_VERSION
};
@@ -42,8 +30,8 @@ maria_declare_plugin(wsrep)
"Codership Oy",
"Wsrep replication plugin",
PLUGIN_LICENSE_GPL,
- wsrep_plugin_init,
- wsrep_plugin_deinit,
+ NULL,
+ NULL,
0x0100,
NULL, /* Status variables */
NULL, /* System variables */
diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc
index 6f902130..c3df6e9f 100644
--- a/sql/wsrep_server_service.cc
+++ b/sql/wsrep_server_service.cc
@@ -39,7 +39,7 @@ static void init_service_thd(THD* thd, char* thread_stack)
thd->thread_stack= thread_stack;
thd->real_id= pthread_self();
thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer();
- thd->set_command(COM_SLEEP);
+ thd->mark_connection_idle();
thd->reset_for_next_command(true);
server_threads.insert(thd); // as wsrep_innobase_kill_one_trx() uses find_thread_by_id()
}
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index db138f25..573aa70c 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -732,7 +732,9 @@ static void* sst_joiner_thread (void* a)
{
proc.wait();
// Read state ID (UUID:SEQNO) followed by wsrep_gtid_domain_id (if any).
+ unsigned long int domain_id= wsrep_gtid_domain_id;
const char *pos= strchr(out, ' ');
+ WSREP_DEBUG("SST state ID tmp=%s out=%s pos=%p", tmp, out, pos);
if (!pos) {
@@ -742,6 +744,13 @@ static void* sst_joiner_thread (void* a)
WSREP_WARN("Did not find domain ID from SST script output '%s'. "
"Domain ID must be set manually to keep binlog consistent",
out);
+ if (wsrep_gtid_domain_id)
+ {
+ WSREP_INFO("This node is configured to use wsrep_gtid_domain_id=%lu by user.",
+ domain_id);
+ wsrep_gtid_server.domain_id= (uint32)domain_id;
+ wsrep_gtid_domain_id= (uint32)domain_id;
+ }
}
err= sst_scan_uuid_seqno (out, &ret_uuid, &ret_seqno);
@@ -1769,6 +1778,8 @@ static int sst_flush_tables(THD* thd)
char content[100];
snprintf(content, sizeof(content), "%s:%lld %d\n", wsrep_cluster_state_uuid,
(long long)wsrep_locked_seqno, wsrep_gtid_server.domain_id);
+ WSREP_DEBUG("sst_flush_tables : %s:%lld %d", wsrep_cluster_state_uuid,
+ (long long)wsrep_locked_seqno, wsrep_gtid_server.domain_id);
err= sst_create_file(flush_success, content);
if (err)
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index 682e6485..ede2c906 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -36,7 +36,7 @@ static Wsrep_thd_queue* wsrep_rollback_queue= 0;
static Atomic_counter<uint64_t> wsrep_bf_aborts_counter;
-int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff,
+int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, void *, system_status_var *,
enum enum_var_type scope)
{
wsrep_local_bf_aborts= wsrep_bf_aborts_counter;
@@ -487,6 +487,7 @@ void wsrep_backup_kill_for_commit(THD *thd)
thd->wsrep_trx().state() != wsrep::transaction::s_must_replay)
{
thd->wsrep_abort_by_kill= thd->killed;
+ my_free(thd->wsrep_abort_by_kill_err);
thd->wsrep_abort_by_kill_err= thd->killed_err;
thd->killed= NOT_KILLED;
thd->killed_err= 0;
@@ -499,6 +500,7 @@ void wsrep_restore_kill_after_commit(THD *thd)
DBUG_ASSERT(WSREP(thd));
mysql_mutex_assert_owner(&thd->LOCK_thd_kill);
thd->killed= thd->wsrep_abort_by_kill;
+ my_free(thd->killed_err);
thd->killed_err= thd->wsrep_abort_by_kill_err;
thd->wsrep_abort_by_kill= NOT_KILLED;
thd->wsrep_abort_by_kill_err= 0;
diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h
index f3790887..bf5baf9a 100644
--- a/sql/wsrep_thd.h
+++ b/sql/wsrep_thd.h
@@ -82,7 +82,7 @@ private:
mysql_cond_t COND_wsrep_thd_queue;
};
-int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff,
+int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, void *, system_status_var *,
enum enum_var_type scope);
bool wsrep_create_appliers(long threads, bool mutex_protected=false);
void wsrep_create_rollbacker();
diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h
index a963a2b1..25e71638 100644
--- a/sql/wsrep_trans_observer.h
+++ b/sql/wsrep_trans_observer.h
@@ -91,7 +91,13 @@ static inline bool wsrep_is_real(THD* thd, bool all)
*/
static inline bool wsrep_has_changes(THD* thd)
{
- return (thd->wsrep_trx().is_empty() == false);
+ // Transaction has changes to replicate if it
+ // has appended one or more certification keys,
+ // and has actual changes to replicate in binlog
+ // cache. Except for streaming replication,
+ // where commit message may have no payload.
+ return !thd->wsrep_trx().is_empty() &&
+ (!wsrep_is_binlog_cache_empty(thd) || thd->wsrep_trx().is_streaming());
}
/*