Skip to content

Commit

Permalink
Bug#27401817: ENFORCE THAT ALL MEMBERS DO HAVE THE SAME LOWER_CASE_TA…
Browse files Browse the repository at this point in the history
…BLE_NAMES VALUE

Description:
Group Replication conflict detection does use schema and table names
as part of the Primary Key Equivalent (PKE) in order to detect and
disallow conflicting transactions.
The value of lower_case_table_names option does change how schema
and tables names are stored and externalized, which depending on the
configuration value may persist a T1 as t1. This difference on a
group can cause inconsistencies:
Example:
  M1: lower_case_table_names=0
  M2: lower_case_table_names=0
  M3: lower_case_table_names=1

  CREATE TABLE T1;
  would be stored as T1 on M1 and M2, but as t1 on M3.

Resolution:
When a server tries to join a group, its lower_case_table_names
value must be compared with the group one, if the value is different
the server must not be allowed to join.
  • Loading branch information
Jaideep Karande committed Feb 19, 2018
1 parent 62f55b4 commit 8162de5
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 17 deletions.
18 changes: 15 additions & 3 deletions rapid/plugin/group_replication/include/member_info.h
@@ -1,4 +1,4 @@
/* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -104,8 +104,11 @@ class Group_member_info: public Plugin_gcs_message
// Length of the payload item: 2 bytes // Length of the payload item: 2 bytes
PIT_MEMBER_WEIGHT= 14, PIT_MEMBER_WEIGHT= 14,


// Length of the payload item: 2 bytes
PIT_LOWER_CASE_TABLE_NAME= 15,

// No valid type codes can appear after this one. // No valid type codes can appear after this one.
PIT_MAX= 15 PIT_MAX= 16
}; };


/* /*
Expand Down Expand Up @@ -151,6 +154,8 @@ class Group_member_info: public Plugin_gcs_message
@param[in] role_arg member role within the group @param[in] role_arg member role within the group
@param[in] in_single_primary_mode is member in single mode @param[in] in_single_primary_mode is member in single mode
@param[in] has_enforces_update_everywhere_checks has member enforce update check @param[in] has_enforces_update_everywhere_checks has member enforce update check
@param[in] member_weight_arg member weight
@param[in] lower_case_table_names_arg lower case table names
*/ */
Group_member_info(char* hostname_arg, Group_member_info(char* hostname_arg,
uint port_arg, uint port_arg,
Expand All @@ -163,7 +168,8 @@ class Group_member_info: public Plugin_gcs_message
Group_member_info::Group_member_role role_arg, Group_member_info::Group_member_role role_arg,
bool in_single_primary_mode, bool in_single_primary_mode,
bool has_enforces_update_everywhere_checks, bool has_enforces_update_everywhere_checks,
uint member_weight_arg); uint member_weight_arg,
uint lower_case_table_names_arg);


/** /**
Copy constructor Copy constructor
Expand Down Expand Up @@ -245,6 +251,11 @@ class Group_member_info: public Plugin_gcs_message
*/ */
uint32 get_configuration_flags(); uint32 get_configuration_flags();


/**
@return the global-variable lower case table names value
*/
uint get_lower_case_table_names() const;

/** /**
@return the member state of system variable @return the member state of system variable
group_replication_single_primary_mode group_replication_single_primary_mode
Expand Down Expand Up @@ -394,6 +405,7 @@ class Group_member_info: public Plugin_gcs_message
uint32 configuration_flags; uint32 configuration_flags;
bool conflict_detection_enable; bool conflict_detection_enable;
uint member_weight; uint member_weight;
uint lower_case_table_names;
}; };




Expand Down
18 changes: 17 additions & 1 deletion rapid/plugin/group_replication/src/gcs_event_handlers.cc
@@ -1,4 +1,4 @@
/* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -1683,6 +1683,22 @@ Plugin_gcs_events_handler::compare_member_option_compatibility() const
Group_member_info::get_configuration_flags_string(member_configuration_flags).c_str()); Group_member_info::get_configuration_flags_string(member_configuration_flags).c_str());
goto cleaning; goto cleaning;
} }

if (local_member_info->get_lower_case_table_names() !=
(*all_members_it)->get_lower_case_table_names())
{
result= 1;
log_message(MY_ERROR_LEVEL,
"The member is configured with a lower_case_table_names "
"option value '%lu' different from the group '%lu'. "
"The member will now exit the group. If there is existing "
"data on member, it may be incompatible with group if "
"created with a lower_case_table_names value different from "
"the group.",
local_member_info->get_lower_case_table_names(),
(*all_members_it)->get_lower_case_table_names());
goto cleaning;
}
} }


cleaning: cleaning:
Expand Down
30 changes: 26 additions & 4 deletions rapid/plugin/group_replication/src/member_info.cc
@@ -1,4 +1,4 @@
/* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -32,7 +32,8 @@ Group_member_info(char* hostname_arg,
Group_member_info::Group_member_role role_arg, Group_member_info::Group_member_role role_arg,
bool in_single_primary_mode, bool in_single_primary_mode,
bool has_enforces_update_everywhere_checks, bool has_enforces_update_everywhere_checks,
uint member_weight_arg) uint member_weight_arg,
uint lower_case_table_names_arg)
: Plugin_gcs_message(CT_MEMBER_INFO_MESSAGE), : Plugin_gcs_message(CT_MEMBER_INFO_MESSAGE),
hostname(hostname_arg), port(port_arg), uuid(uuid_arg), hostname(hostname_arg), port(port_arg), uuid(uuid_arg),
status(status_arg), status(status_arg),
Expand All @@ -41,7 +42,8 @@ Group_member_info(char* hostname_arg,
unreachable(false), unreachable(false),
role(role_arg), role(role_arg),
configuration_flags(0), conflict_detection_enable(false), configuration_flags(0), conflict_detection_enable(false),
member_weight(member_weight_arg) member_weight(member_weight_arg),
lower_case_table_names(lower_case_table_names_arg)
{ {
gcs_member_id= new Gcs_member_identifier(gcs_member_id_arg); gcs_member_id= new Gcs_member_identifier(gcs_member_id_arg);
member_version= new Member_version(member_version_arg.get_version()); member_version= new Member_version(member_version_arg.get_version());
Expand Down Expand Up @@ -69,7 +71,8 @@ Group_member_info::Group_member_info(Group_member_info& other)
role(other.get_role()), role(other.get_role()),
configuration_flags(other.get_configuration_flags()), configuration_flags(other.get_configuration_flags()),
conflict_detection_enable(other.is_conflict_detection_enabled()), conflict_detection_enable(other.is_conflict_detection_enabled()),
member_weight(other.get_member_weight()) member_weight(other.get_member_weight()),
lower_case_table_names(other.get_lower_case_table_names())
{ {
gcs_member_id= new Gcs_member_identifier(other.get_gcs_member_id() gcs_member_id= new Gcs_member_identifier(other.get_gcs_member_id()
.get_member_id()); .get_member_id());
Expand Down Expand Up @@ -154,6 +157,10 @@ Group_member_info::encode_payload(std::vector<unsigned char>* buffer) const
encode_payload_item_int2(buffer, PIT_MEMBER_WEIGHT, encode_payload_item_int2(buffer, PIT_MEMBER_WEIGHT,
member_weight_aux); member_weight_aux);


uint16 lower_case_table_names_aux= static_cast <uint16> (lower_case_table_names);
encode_payload_item_int2(buffer, PIT_LOWER_CASE_TABLE_NAME,
lower_case_table_names_aux);

DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }


Expand Down Expand Up @@ -267,6 +274,15 @@ Group_member_info::decode_payload(const unsigned char* buffer,
member_weight= (uint)member_weight_aux; member_weight= (uint)member_weight_aux;
} }
break; break;

case PIT_LOWER_CASE_TABLE_NAME:
if (slider + payload_item_length <= end)
{
uint16 lower_case_table_names_aux= uint2korr(slider);
slider += payload_item_length;
lower_case_table_names= static_cast <uint>(lower_case_table_names_aux);
}
break;
} }
} }


Expand Down Expand Up @@ -365,6 +381,12 @@ Group_member_info::get_configuration_flags()
return configuration_flags; return configuration_flags;
} }


uint
Group_member_info::get_lower_case_table_names() const
{
return lower_case_table_names;
}

bool Group_member_info::in_primary_mode() bool Group_member_info::in_primary_mode()
{ {
return get_configuration_flags() & CNF_SINGLE_PRIMARY_MODE_F; return get_configuration_flags() & CNF_SINGLE_PRIMARY_MODE_F;
Expand Down
11 changes: 9 additions & 2 deletions rapid/plugin/group_replication/src/plugin.cc
@@ -1,4 +1,4 @@
/* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -150,6 +150,9 @@ ulong recovery_reconnect_interval_var= 0;
/* Write set extraction algorithm*/ /* Write set extraction algorithm*/
int write_set_extraction_algorithm= HASH_ALGORITHM_OFF; int write_set_extraction_algorithm= HASH_ALGORITHM_OFF;


/* Lower case table name */
uint gr_lower_case_table_names= 0;

/* Generic components variables */ /* Generic components variables */
ulong components_stop_timeout_var= LONG_TIMEOUT; ulong components_stop_timeout_var= LONG_TIMEOUT;


Expand Down Expand Up @@ -672,7 +675,8 @@ int configure_group_member_manager(char *hostname, char *uuid,
Group_member_info::MEMBER_ROLE_SECONDARY, Group_member_info::MEMBER_ROLE_SECONDARY,
single_primary_mode_var, single_primary_mode_var,
enforce_update_everywhere_checks_var, enforce_update_everywhere_checks_var,
member_weight_var); member_weight_var,
gr_lower_case_table_names);


//Create the membership info visible for the group //Create the membership info visible for the group
delete group_member_mgr; delete group_member_mgr;
Expand Down Expand Up @@ -1650,6 +1654,9 @@ static int check_if_server_properly_configured()
DBUG_RETURN(1); DBUG_RETURN(1);
} }


gr_lower_case_table_names= startup_pre_reqs.lower_case_table_names;
DBUG_ASSERT (gr_lower_case_table_names <= 2);

DBUG_RETURN(0); DBUG_RETURN(0);
} }


Expand Down
@@ -0,0 +1,50 @@
include/group_replication.inc
Warnings:
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
[connection server1]

## 1. Verify lower_case_table_names cannot be set while server is
## running and start GR on server-1.

[connection server1]
SET GLOBAL lower_case_table_names= 1;
ERROR HY000: Variable 'lower_case_table_names' is a read only variable
include/start_and_bootstrap_group_replication.inc

## 2. Store variables of server-2 for restart.

[connection server2]
set session sql_log_bin=0;
call mtr.add_suppression("The member is configured with a lower_case_table_names option value .*");
call mtr.add_suppression("lower_case_table_names was set to 2, .*");
set session sql_log_bin=1;

## 3A. Test GR start with command when lower_case_table_names matches.

include/start_group_replication.inc
include/assert.inc ['Assert server-1 and server-2 are ONLINE']

## 3B. Test GR start on boot when lower_case_table_names matches.

# restart:--group_replication_start_on_boot=1 --group_replication_local_address=GROUP_REPLICATION_LOCAL_ADDRESS --group_replication_group_seeds=GROUP_REPLICATION_GROUP_SEEDS --group_replication_group_name=GROUP_REPLICATION_GROUP_NAME --lower_case_table_names=1
include/rpl_reconnect.inc

## 4A. Test GR does not start on boot when lower_case_table_names
## does not match.

# restart:--group_replication_start_on_boot=1 --group_replication_local_address=GROUP_REPLICATION_LOCAL_ADDRESS --group_replication_group_seeds=GROUP_REPLICATION_GROUP_SEEDS --group_replication_group_name=GROUP_REPLICATION_GROUP_NAME --lower_case_table_names=2
include/rpl_reconnect.inc
include/rpl_gr_wait_for_number_of_members.inc

## 4B. Test GR does not start with command when lower_case_table_names
## does not match.

START GROUP_REPLICATION;
ERROR HY000: The server is not configured properly to be an active member of the group. Please see more details on error log.
include/assert_grep.inc [Found the expected error about lower_case_table_names]
include/assert.inc ['Assert server-2 is OFFLINE']

## 5. Cleanup.

include/group_replication_end.inc
@@ -0,0 +1 @@
--lower_case_table_names=1
@@ -0,0 +1 @@
--lower_case_table_names=1
@@ -0,0 +1,124 @@
################################################################################
## This test proves that group replication does not start on server when it's
## lower_case_table_names does not match with the group.
##
## Test:
## 0. This test requires 2 members.
## 1. Verify lower_case_table_names cannot be set while server is
## running and start GR on server-1.
## 2. Store variables of server-2 for restart.
## 3. Test GR start on server when lower_case_table_names matches.
## 3A. Test GR start with command when lower_case_table_names matches.
## 3B. Test GR start on boot when lower_case_table_names matches.
## 4. Test GR does not start on server when lower_case_table_names
## does not match.
## 4A. Test GR does not start on boot when lower_case_table_names
## does not match.
## 4B. Test GR does not start with command when lower_case_table_names
## does not match.
## 5. Cleanup.
################################################################################

--source include/big_test.inc
--source ../inc/have_group_replication_plugin.inc
--let $rpl_skip_group_replication_start= 1
--source ../inc/group_replication.inc
--source include/force_restart.inc

--let $allow_rpl_inited=1

--echo
--echo ## 1. Verify lower_case_table_names cannot be set while server is
--echo ## running and start GR on server-1.
--echo
--let $rpl_connection_name= server1
--source include/rpl_connection.inc

## Verify lower_case_table_names cannot be set while server is running.
## This make sures we do not need to test variable changes while GR is running.
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
SET GLOBAL lower_case_table_names= 1;

--source ../inc/start_and_bootstrap_group_replication.inc

--echo
--echo ## 2. Store variables of server-2 for restart.
--echo
--let $rpl_connection_name= server2
--source include/rpl_connection.inc

--let $_group_replication_local_address= `SELECT @@GLOBAL.group_replication_local_address`
--let $_group_replication_group_seeds= `SELECT @@GLOBAL.group_replication_group_seeds`

set session sql_log_bin=0;
call mtr.add_suppression("The member is configured with a lower_case_table_names option value .*");
call mtr.add_suppression("lower_case_table_names was set to 2, .*");
set session sql_log_bin=1;

--echo
--echo ## 3A. Test GR start with command when lower_case_table_names matches.
--echo
--source include/start_group_replication.inc

--let $assert_text= 'Assert server-1 and server-2 are ONLINE'
--let $assert_cond= "[SELECT COUNT(*) from performance_schema.replication_group_members WHERE MEMBER_STATE=\"ONLINE\"]" = 2
--source include/assert.inc

--echo
--echo ## 3B. Test GR start on boot when lower_case_table_names matches.
--echo
--let $restart_parameters=restart:--group_replication_start_on_boot=1 --group_replication_local_address=$_group_replication_local_address --group_replication_group_seeds=$_group_replication_group_seeds --group_replication_group_name=$group_replication_group_name --lower_case_table_names=1
--replace_result $group_replication_group_name GROUP_REPLICATION_GROUP_NAME $_group_replication_local_address GROUP_REPLICATION_LOCAL_ADDRESS $_group_replication_group_seeds GROUP_REPLICATION_GROUP_SEEDS
--source include/restart_mysqld.inc

#Needed as we are not using rpl_restart_server.inc
--let $rpl_server_number= 2
--source include/rpl_reconnect.inc

--let $wait_condition= SELECT COUNT(*) = 2 FROM performance_schema.replication_group_members WHERE MEMBER_STATE="ONLINE"
--source include/wait_condition.inc

--echo
--echo ## 4A. Test GR does not start on boot when lower_case_table_names
--echo ## does not match.
--echo

--let $restart_parameters=restart:--group_replication_start_on_boot=1 --group_replication_local_address=$_group_replication_local_address --group_replication_group_seeds=$_group_replication_group_seeds --group_replication_group_name=$group_replication_group_name --lower_case_table_names=2
--replace_result $group_replication_group_name GROUP_REPLICATION_GROUP_NAME $_group_replication_local_address GROUP_REPLICATION_LOCAL_ADDRESS $_group_replication_group_seeds GROUP_REPLICATION_GROUP_SEEDS
--source include/restart_mysqld.inc

#Needed as we are not using rpl_restart_server.inc
--let $rpl_server_number= 2
--source include/rpl_reconnect.inc

## Wait for server-2 to join group, needed to get group lower_case_table_names
--let $group_replication_number_of_members=2
--source ../inc/gr_wait_for_number_of_members.inc

--let $wait_condition= SELECT COUNT(*) = 1 FROM performance_schema.replication_group_members WHERE MEMBER_STATE="OFFLINE"
--source include/wait_condition.inc

--echo
--echo ## 4B. Test GR does not start with command when lower_case_table_names
--echo ## does not match.
--echo

--error ER_GROUP_REPLICATION_CONFIGURATION
START GROUP_REPLICATION;

--let $assert_only_after = CURRENT_TEST: group_replication.gr_lower_case_table_names
--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.2.err
--let $assert_text = Found the expected error about lower_case_table_names
--let $assert_select = The member is configured with a lower_case_table_names option value
--let $assert_count = 2
--source include/assert_grep.inc

--let $assert_text= 'Assert server-2 is OFFLINE'
--let $assert_cond= "[SELECT COUNT(*) from performance_schema.replication_group_members WHERE MEMBER_STATE=\"OFFLINE\"]" = 1
--source include/assert.inc

--echo
--echo ## 5. Cleanup.
--echo

--source ../inc/group_replication_end.inc

0 comments on commit 8162de5

Please sign in to comment.