Skip to content
Permalink
Browse files

Bug#27401817: ENFORCE THAT ALL MEMBERS DO HAVE THE SAME LOWER_CASE_TA…

…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
Jaideep Karande committed Feb 19, 2018
1 parent 62f55b4 commit 8162de58b7baaaccca653fe7cd5a00074b670d19
@@ -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
it under the terms of the GNU General Public License as published by
@@ -104,8 +104,11 @@ class Group_member_info: public Plugin_gcs_message
// Length of the payload item: 2 bytes
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.
PIT_MAX= 15
PIT_MAX= 16
};

/*
@@ -151,6 +154,8 @@ class Group_member_info: public Plugin_gcs_message
@param[in] role_arg member role within the group
@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] member_weight_arg member weight
@param[in] lower_case_table_names_arg lower case table names
*/
Group_member_info(char* hostname_arg,
uint port_arg,
@@ -163,7 +168,8 @@ class Group_member_info: public Plugin_gcs_message
Group_member_info::Group_member_role role_arg,
bool in_single_primary_mode,
bool has_enforces_update_everywhere_checks,
uint member_weight_arg);
uint member_weight_arg,
uint lower_case_table_names_arg);

/**
Copy constructor
@@ -245,6 +251,11 @@ class Group_member_info: public Plugin_gcs_message
*/
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
group_replication_single_primary_mode
@@ -394,6 +405,7 @@ class Group_member_info: public Plugin_gcs_message
uint32 configuration_flags;
bool conflict_detection_enable;
uint member_weight;
uint lower_case_table_names;
};


@@ -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
it under the terms of the GNU General Public License as published by
@@ -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());
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:
@@ -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
it under the terms of the GNU General Public License as published by
@@ -32,7 +32,8 @@ Group_member_info(char* hostname_arg,
Group_member_info::Group_member_role role_arg,
bool in_single_primary_mode,
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),
hostname(hostname_arg), port(port_arg), uuid(uuid_arg),
status(status_arg),
@@ -41,7 +42,8 @@ Group_member_info(char* hostname_arg,
unreachable(false),
role(role_arg),
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);
member_version= new Member_version(member_version_arg.get_version());
@@ -69,7 +71,8 @@ Group_member_info::Group_member_info(Group_member_info& other)
role(other.get_role()),
configuration_flags(other.get_configuration_flags()),
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()
.get_member_id());
@@ -154,6 +157,10 @@ Group_member_info::encode_payload(std::vector<unsigned char>* buffer) const
encode_payload_item_int2(buffer, PIT_MEMBER_WEIGHT,
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;
}

@@ -267,6 +274,15 @@ Group_member_info::decode_payload(const unsigned char* buffer,
member_weight= (uint)member_weight_aux;
}
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;
}
}

@@ -365,6 +381,12 @@ Group_member_info::get_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()
{
return get_configuration_flags() & CNF_SINGLE_PRIMARY_MODE_F;
@@ -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
it under the terms of the GNU General Public License as published by
@@ -150,6 +150,9 @@ ulong recovery_reconnect_interval_var= 0;
/* Write set extraction algorithm*/
int write_set_extraction_algorithm= HASH_ALGORITHM_OFF;

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

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

@@ -672,7 +675,8 @@ int configure_group_member_manager(char *hostname, char *uuid,
Group_member_info::MEMBER_ROLE_SECONDARY,
single_primary_mode_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
delete group_member_mgr;
@@ -1650,6 +1654,9 @@ static int check_if_server_properly_configured()
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);
}

@@ -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.
You can’t perform that action at this time.