mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
Atomic CREATE OR REPLACE allows to keep an old table intact if the
command fails or during the crash. That is done by renaming the
original table to temporary name, as a backup and restoring it if the
CREATE fails. When the command is complete and logged the backup
table is deleted.
Atomic replace algorithm
Two DDL chains are used for CREATE OR REPLACE:
ddl_log_state_create (C) and ddl_log_state_rm (D).
1. (C) Log rename of ORIG to TMP table (Rename TMP to original).
2. Rename orignal to TMP.
3. (C) Log CREATE_TABLE_ACTION of ORIG (drops ORIG);
4. Do everything with ORIG (like insert data)
5. (D) Log drop of TMP
6. Write query to binlog (this marks (C) to be closed in
case of failure)
7. Execute drop of TMP through (D)
8. Close (C) and (D)
If there is a failure before 6) we revert the changes in (C)
Chain (D) is only executed if 6) succeded (C is closed on
crash recovery).
Foreign key errors will be found at the 1) stage.
Additional notes
- CREATE TABLE without REPLACE and temporary tables is not affected
by this commit.
set @@drop_before_create_or_replace=1 can be used to
get old behaviour where existing tables are dropped
in CREATE OR REPLACE.
- CREATE TABLE is reverted if binlogging the query fails.
- Engines having HTON_EXPENSIVE_RENAME flag set are not affected by
this commit. Conflicting tables marked with this flag will be
deleted with CREATE OR REPLACE.
- Replication execution is not affected by this commit.
- Replication will first drop the conflicting table and then
creating the new one.
- CREATE TABLE .. SELECT XID usage is fixed and now there is no need
to log DROP TABLE via DDL_CREATE_TABLE_PHASE_LOG (see comments in
do_postlock()). XID is now correctly updated so it disables
DDL_LOG_DROP_TABLE_ACTION. Note that binary log is flushed at the
final stage when the table is ready. So if we have XID in the
binary log we don't need to drop the table.
- Three variations of CREATE OR REPLACE handled:
1. CREATE OR REPLACE TABLE t1 (..);
2. CREATE OR REPLACE TABLE t1 LIKE t2;
3. CREATE OR REPLACE TABLE t1 SELECT ..;
- Test case uses 6 combinations for engines (aria, aria_notrans,
myisam, ib, lock_tables, expensive_rename) and 2 combinations for
binlog types (row, stmt). Combinations help to check differences
between the results. Error failures are tested for the above three
variations.
- expensive_rename tests CREATE OR REPLACE without atomic
replace. The effect should be the same as with the old behaviour
before this commit.
- Triggers mechanism is unaffected by this change. This is tested in
create_replace.test.
- LOCK TABLES is affected. Lock restoration must be done after new
table is created or TMP is renamed back to ORIG
- Moved ddl_log_complete() from send_eof() to finalize_ddl(). This
checkpoint was not executed before for normal CREATE TABLE but is
executed now.
- CREATE TABLE will now rollback also if writing to the binary
logging failed. See rpl_gtid_strict.test
backup ddl log changes
- In case of a successfull CREATE OR REPLACE we only log
the CREATE event, not the DROP TABLE event of the old table.
ddl_log.cc changes
ddl_log_execute_action() now properly return error conditions.
ddl_log_disable_entry() added to allow one to disable one entry.
The entry on disk is still reserved until ddl_log_complete() is
executed.
On XID usage
Like with all other atomic DDL operations XID is used to avoid
inconsistency between master and slave in the case of a crash after
binary log is written and before ddl_log_state_create is closed. On
recovery XIDs are taken from binary log and corresponding DDL log
events get disabled. That is done by
ddl_log_close_binlogged_events().
On linking two chains together
Chains are executed in the ascending order of entry_pos of execute
entries. But entry_pos assignment order is undefined: it may assign
bigger number for the first chain and then smaller number for the
second chain. So the execution order in that case will be reverse:
second chain will be executed first.
To avoid that we link one chain to another. While the base chain
(ddl_log_state_create) is active the secondary chain
(ddl_log_state_rm) is not executed. That is: only one chain can be
executed in two linked chains.
The interface ddl_log_link_chains() was defined in "MDEV-22166
ddl_log_write_execute_entry() extension".
Atomic info parameters in HA_CREATE_INFO
Many functions in CREATE TABLE pass the same parameters. These
parameters are part of table creation info and should be in
HA_CREATE_INFO (or whatever). Passing parameters via single
structure is much easier for adding new data and
refactoring.
Aria changes:
- Fixed issue in Aria engine with CREATE + locked tables
that data was not properly commited in some cases in
case of crashes.
InnoDB related changes (by Marko Mäkelä):
- table_name_t::is_create_or_replace(): A new predicate to check for
CREATE OR REPLACE TABLE will rename an old table to
and eventually drop after creating the replacement.
- dict_table_t::parse_name(): Do acquire MDL on #sql-create- names
for partitioned tables.
- dict_table_rename_in_cache(): On CREATE OR REPLACE TABLE ... SELECT,
forget the original dict_table_t::mdl_name so that purge will
acquire MDL on the #sql-create- name instead. In this way, the
MDL_EXCLUSIVE that the CREATE OR REPLACE TABLE holds on the
user-visible name will not unnecessarily block any purge of old history
until the very end when the #sql-create- table will be dropped.
- ha_innobase::delete_table(): Do not check FOREIGN KEY consistency
when dropping an #sql-create- table.
- row_rename_table_for_mysql(): Update SYS_FOREIGN.ID also
when renaming to #sql-create- in order to avoid any
duplicate key error when CREATE OR REPLACE TABLE is
creating some FOREIGN KEY constraints by names
that existed in the old table.
Other changes:
- Removed some auto variables in log.cc for better code readability.
- Fixed old bug that CREATE ... SELECT would not be able to auto repair
a table that is part of the SELECT.
- Marked MyISAM that it does not support ROLLBACK (not required but
done for better consistency with other engines).
- maria_create_trn_for_mysql() does not register a new transaction
handler for commits. This was needed to ensure create or replace
will not end with an active transaction.
- We do not get anymore warnings about "Engine not supporting atomic
create" when doing a legal CREATE OR REPLACE on a table with
foreign key constraints.
- Updated VIDEX engine flags to disable CREATE SEQUENCE.
- Removed mysql_mutex_unlock(&LOCK_gdl) / mysql_mutex_lock(&LOCK_gdl)
around calls to binlog as these are unsafe. The binlog code uses
global variables that needs protection from other caller.
- EITS data is preserved if create or replace fails if
drop_before_create_or_replace=OFF. If ON, then create or replace
will drop EITS before the drop of the original table (as before).
- Using CREATE OR REPLACE on a encrypted table that the user cannot
decrypt will fail instead of replacing the encrypted table.
The encrypted table will unchanged.
Known issues:
- One cannot use create or replace on an InnoDB tables that has foreign
key references point to it
- CREATE OR REPLACE TEMPORARY table is not full atomic. Any conflicting
table will always be dropped before creating a new one. (Old behaviour).
Bug fixes related to this MDEV:
MDEV-36435 Assertion failure in finalize_locked_tables()
MDEV-36439 Assertion `thd_arg->lex->sql_command != SQLCOM_CREATE_SEQUENCE...
MDEV-36498 Failed CoR in non-atomic mode no longer generates DROP in RBR...
MDEV-36508 Temporary files #sql-create-....frm occasionally stay after
crash recovery
MDEV-38479 Crash in CREATE OR REPLACE SEQUENCE when new sequence cannot
be created
MDEV-36497 Assertion failure after atomic CoR with Aria under lock in
transactional context
MDEV-36501 EITS data is lost after failed attempt to CREATE OR REPLACE
table
MDEV-36493 Atomic CREATE OR REPLACE ... SELECT blocks InnoDB purge
MDEV-39367 MSAN/valgrind errors in temp_file_size_cb_func,
main.tmp_space_usage fails
MDEV-39446 Atomic CREATE OR REPLACE fails if a table cannot be decrypted
MDEV-40776 Atomic CREATE OR REPLACE silently breaks the foreign key
MDEV-40765 Assertion `"unexpected references" == 0' failed upon failing
CREATE OR REPLACE
MDEV-40994 Atomic CoR: Transaction not registered for MariaDB 2PC, but
transaction is active
Reverted commits:
- MDEV-36685 "CREATE-SELECT may lose in binlog side-effects of
stored-routine" as it did not take into account that it safe to
clear binlogs if the created table is non transactional and there
are no other non transactional tables used.
This was done because it caused extra logging when it is not
needed (not using any non transactional tables) and it also did
not solve side effects when using statement based loggging.
1272 lines
37 KiB
C++
1272 lines
37 KiB
C++
/* Copyright 2008-2026 Codership Oy <http://www.codership.com>
|
|
Copyright 2025-2026 MariaDB plc <http://www.mariadb.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
#include "wsrep_var.h"
|
|
|
|
#include <mysqld.h>
|
|
#include <sql_class.h>
|
|
#include <set_var.h>
|
|
#include <sql_acl.h>
|
|
#include "wsrep_priv.h"
|
|
#include "wsrep_thd.h"
|
|
#include "wsrep_xid.h"
|
|
#include <my_dir.h>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include "wsrep_trans_observer.h"
|
|
#include "wsrep_server_state.h"
|
|
#include "wsrep_plugin.h" /* wsrep_provider_plugin_enabled() */
|
|
#include "wsrep_schema.h"
|
|
#include "wsrep_mysqld.h"
|
|
|
|
#include <sys/types.h> // statinfo
|
|
#include <sys/stat.h> // stat
|
|
|
|
ulong wsrep_reject_queries;
|
|
|
|
int wsrep_init_vars()
|
|
{
|
|
wsrep_provider = my_strdup(PSI_INSTRUMENT_ME, WSREP_NONE, MYF(MY_WME));
|
|
wsrep_provider_options= my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
|
|
wsrep_cluster_address = my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
|
|
wsrep_cluster_name = my_strdup(PSI_INSTRUMENT_ME, WSREP_CLUSTER_NAME, MYF(MY_WME));
|
|
wsrep_node_name = my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
|
|
wsrep_node_address = my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
|
|
wsrep_node_incoming_address= my_strdup(PSI_INSTRUMENT_ME, WSREP_NODE_INCOMING_AUTO, MYF(MY_WME));
|
|
if (wsrep_gtid_mode)
|
|
wsrep_start_position = my_strdup(PSI_INSTRUMENT_ME, WSREP_START_POSITION_ZERO_GTID, MYF(MY_WME));
|
|
else
|
|
wsrep_start_position = my_strdup(PSI_INSTRUMENT_ME, WSREP_START_POSITION_ZERO, MYF(MY_WME));
|
|
return 0;
|
|
}
|
|
|
|
static int get_provider_option_value(const char* opts,
|
|
const char* opt_name,
|
|
ulong* opt_value)
|
|
{
|
|
int ret= 1;
|
|
ulong opt_value_tmp;
|
|
char *opt_value_str, *s, *opts_copy= my_strdup(PSI_INSTRUMENT_ME, opts, MYF(MY_WME));
|
|
|
|
if ((opt_value_str= strstr(opts_copy, opt_name)) == NULL)
|
|
goto end;
|
|
opt_value_str= strtok_r(opt_value_str, "=", &s);
|
|
if (opt_value_str == NULL) goto end;
|
|
opt_value_str= strtok_r(NULL, ";", &s);
|
|
if (opt_value_str == NULL) goto end;
|
|
|
|
opt_value_tmp= strtoul(opt_value_str, NULL, 10);
|
|
if (errno == ERANGE) goto end;
|
|
|
|
*opt_value= opt_value_tmp;
|
|
ret= 0;
|
|
|
|
end:
|
|
my_free(opts_copy);
|
|
return ret;
|
|
}
|
|
|
|
static bool refresh_provider_options()
|
|
{
|
|
WSREP_DEBUG("refresh_provider_options: %s",
|
|
(wsrep_provider_options) ? wsrep_provider_options : "null");
|
|
|
|
try
|
|
{
|
|
std::string opts= Wsrep_server_state::instance().provider().options();
|
|
wsrep_provider_options_init(opts.c_str());
|
|
get_provider_option_value(opts.c_str(),
|
|
(char*)"repl.max_ws_size",
|
|
&wsrep_max_ws_size);
|
|
return false;
|
|
}
|
|
catch (...)
|
|
{
|
|
WSREP_ERROR("Failed to get provider options");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool wsrep_refresh_provider_options()
|
|
{
|
|
return refresh_provider_options();
|
|
}
|
|
|
|
void wsrep_set_wsrep_on(THD* thd)
|
|
{
|
|
if (thd)
|
|
thd->wsrep_was_on= WSREP_ON_;
|
|
WSREP_PROVIDER_EXISTS_= wsrep_provider && *wsrep_provider &&
|
|
strcasecmp(wsrep_provider, WSREP_NONE);
|
|
WSREP_ON_= global_system_variables.wsrep_on && WSREP_PROVIDER_EXISTS_;
|
|
}
|
|
|
|
bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
|
|
{
|
|
if (var_type == OPT_GLOBAL)
|
|
{
|
|
my_bool saved_wsrep_on= global_system_variables.wsrep_on;
|
|
|
|
thd->variables.wsrep_on= saved_wsrep_on;
|
|
|
|
// If wsrep has not been inited we need to do it now
|
|
if (!wsrep_inited &&
|
|
saved_wsrep_on &&
|
|
wsrep_provider &&
|
|
*wsrep_provider &&
|
|
strcasecmp(wsrep_provider, WSREP_NONE))
|
|
{
|
|
// wsrep_init() rewrites provide if it fails
|
|
char* tmp= strdup(wsrep_provider);
|
|
mysql_mutex_unlock(&LOCK_global_system_variables);
|
|
|
|
if (wsrep_init())
|
|
{
|
|
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp, errno, "wsrep_init failed");
|
|
saved_wsrep_on= false;
|
|
}
|
|
|
|
if (!wsrep_ready_get())
|
|
{
|
|
my_error(ER_GALERA_REPLICATION_NOT_SUPPORTED, MYF(0));
|
|
WSREP_INFO("Failed to start Galera replication. Please check your "
|
|
"configuration.");
|
|
saved_wsrep_on= false;
|
|
}
|
|
|
|
free(tmp);
|
|
mysql_mutex_lock(&LOCK_global_system_variables);
|
|
}
|
|
|
|
thd->variables.wsrep_on= global_system_variables.wsrep_on= saved_wsrep_on;
|
|
}
|
|
|
|
wsrep_set_wsrep_on(thd);
|
|
|
|
if (var_type == OPT_GLOBAL)
|
|
{
|
|
if (thd->variables.wsrep_on &&
|
|
thd->wsrep_cs().state() == wsrep::client_state::s_none)
|
|
{
|
|
wsrep_open(thd);
|
|
wsrep_before_command(thd);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_on_check(sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
bool new_wsrep_on= (bool)var->save_result.ulonglong_value;
|
|
|
|
if (check_has_super(self, thd, var))
|
|
return true;
|
|
|
|
if (new_wsrep_on)
|
|
{
|
|
if (!WSREP_PROVIDER_EXISTS)
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) can't be enabled "
|
|
"if the wsrep_provider is unset or set to 'none'", MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (var->type == OPT_SESSION &&
|
|
!global_system_variables.wsrep_on)
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS,
|
|
"Can't enable @@session.wsrep_on, "
|
|
"while @@global.wsrep_on is disabled", MYF(0));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (thd->in_active_multi_stmt_transaction())
|
|
{
|
|
my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (var->type == OPT_GLOBAL)
|
|
{
|
|
/*
|
|
The global value is about to change. Cleanup
|
|
the transaction state and close the client
|
|
state. wsrep_on_update() will take care of
|
|
reopening it should wsrep_on be re-enabled.
|
|
*/
|
|
if (global_system_variables.wsrep_on && !new_wsrep_on)
|
|
{
|
|
wsrep_commit_empty(thd, true);
|
|
wsrep_after_statement(thd);
|
|
wsrep_after_command_ignore_result(thd);
|
|
wsrep_close(thd);
|
|
wsrep_cleanup(thd);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
static T parse_value(char** startptr, char** endptr)
|
|
{
|
|
T val= strtoll(*startptr, *&endptr, 10);
|
|
*startptr= *endptr;
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
Verify the format of the given UUID:seqno.
|
|
|
|
@return
|
|
true Fail
|
|
false Pass
|
|
*/
|
|
static
|
|
bool wsrep_start_position_verify (const char* start_str)
|
|
{
|
|
size_t start_len;
|
|
wsrep_uuid_t uuid;
|
|
ssize_t uuid_len;
|
|
|
|
// Check whether it has minimum acceptable length.
|
|
start_len= strlen (start_str);
|
|
if (start_len < 34)
|
|
return true;
|
|
|
|
/*
|
|
Parse the input to check whether UUID length is acceptable
|
|
and seqno has been provided.
|
|
*/
|
|
uuid_len= wsrep_uuid_scan (start_str, start_len, &uuid);
|
|
if (uuid_len < 0 || (start_len - uuid_len) < 2)
|
|
return true;
|
|
|
|
// Separator must follow the UUID.
|
|
if (start_str[uuid_len] != ':')
|
|
return true;
|
|
|
|
char* endptr;
|
|
char* startptr= (char *)start_str + uuid_len + 1;
|
|
wsrep_seqno_t const seqno(parse_value<uint64_t>(&startptr, &endptr));
|
|
|
|
// Do not allow seqno < -1
|
|
if (seqno < -1)
|
|
return true;
|
|
|
|
// Start parsing native GTID part
|
|
if (*startptr == ',')
|
|
{
|
|
startptr++;
|
|
uint32_t domain __attribute__((unused))
|
|
(parse_value<uint32_t>(&startptr, &endptr));
|
|
if (*endptr != '-') return true;
|
|
startptr++;
|
|
uint32_t server __attribute__((unused))
|
|
(parse_value<uint32_t>(&startptr, &endptr));
|
|
if (*endptr != '-') return true;
|
|
startptr++;
|
|
uint64_t seq __attribute__((unused))
|
|
(parse_value<uint64_t>(&startptr, &endptr));
|
|
}
|
|
|
|
// Remaining string was seqno.
|
|
if (*endptr == '\0') return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static
|
|
bool wsrep_set_local_position(THD* thd, const char* const value,
|
|
size_t length, bool const sst)
|
|
{
|
|
char* endptr;
|
|
char* startptr;
|
|
wsrep_uuid_t uuid;
|
|
size_t const uuid_len= wsrep_uuid_scan(value, length, &uuid);
|
|
startptr= (char *)value + uuid_len + 1;
|
|
wsrep_seqno_t const seqno= parse_value<uint64_t>(&startptr, &endptr);
|
|
|
|
if (*startptr == ',')
|
|
{
|
|
startptr++;
|
|
wsrep_gtid_server.domain_id= parse_value<uint32_t>(&startptr, &endptr);
|
|
startptr++;
|
|
wsrep_gtid_server.server_id= parse_value<uint32_t>(&startptr, &endptr);
|
|
startptr++;
|
|
wsrep_gtid_server.seqno(parse_value<uint64_t>(&startptr, &endptr));
|
|
}
|
|
|
|
char start_pos_buf[FN_REFLEN];
|
|
memcpy(start_pos_buf, value, length);
|
|
start_pos_buf[length]='\0';
|
|
|
|
// If both are same as WSREP_START_POSITION_ZERO just set local
|
|
if (!strcmp(start_pos_buf, WSREP_START_POSITION_ZERO) &&
|
|
!strcmp(wsrep_start_position, WSREP_START_POSITION_ZERO))
|
|
goto set;
|
|
else
|
|
WSREP_INFO("SST setting local position to %s current %s", start_pos_buf, wsrep_start_position);
|
|
|
|
if (sst)
|
|
return (wsrep_sst_received (thd, uuid, seqno, NULL, 0));
|
|
|
|
set:
|
|
local_uuid= uuid;
|
|
local_seqno= seqno;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wsrep_start_position_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
char start_pos_buf[FN_REFLEN];
|
|
|
|
if ((! var->save_result.string_value.str) ||
|
|
(var->save_result.string_value.length > (FN_REFLEN - 1))) // safety
|
|
goto err;
|
|
|
|
memcpy(start_pos_buf, var->save_result.string_value.str,
|
|
var->save_result.string_value.length);
|
|
start_pos_buf[var->save_result.string_value.length]= 0;
|
|
|
|
WSREP_DEBUG("SST wsrep_start_position check for new position %s old %s",
|
|
start_pos_buf, wsrep_start_position);
|
|
|
|
// Verify the format.
|
|
if (wsrep_start_position_verify(start_pos_buf)) return true;
|
|
|
|
// Give error if position is updated when wsrep is not enabled or
|
|
// provider is not loaded.
|
|
if ((!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
|
|
&& strcmp(start_pos_buf, WSREP_START_POSITION_ZERO))
|
|
{
|
|
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_start_position' because "
|
|
"wsrep is switched off or provider is not loaded");
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
As part of further verification, we try to update the value and catch
|
|
errors (if any) only when value actually has been changed.
|
|
*/
|
|
if (wsrep_set_local_position(thd, var->save_result.string_value.str,
|
|
var->save_result.string_value.length,
|
|
true))
|
|
goto err;
|
|
|
|
return false;
|
|
|
|
err:
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
|
|
var->save_result.string_value.str ?
|
|
var->save_result.string_value.str : "NULL");
|
|
return true;
|
|
}
|
|
|
|
bool wsrep_start_position_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
// Print a confirmation that wsrep_start_position has been updated.
|
|
WSREP_INFO ("wsrep_start_position set to '%s'", wsrep_start_position);
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_start_position_init (const char* val)
|
|
{
|
|
if (NULL == val || wsrep_start_position_verify (val))
|
|
{
|
|
WSREP_ERROR("Bad initial value for wsrep_start_position: %s",
|
|
(val ? val : ""));
|
|
return true;
|
|
}
|
|
|
|
if (wsrep_set_local_position (NULL, val, strlen(val), false))
|
|
{
|
|
WSREP_ERROR("Failed to set initial wsep_start_position: %s", val);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int wsrep_provider_verify (const char* provider_str)
|
|
{
|
|
MY_STAT f_stat;
|
|
char path[FN_REFLEN];
|
|
|
|
if (!provider_str || strlen(provider_str)== 0)
|
|
return 1;
|
|
|
|
if (!strcmp(provider_str, WSREP_NONE))
|
|
return 0;
|
|
|
|
if (!unpack_filename(path, provider_str))
|
|
return 1;
|
|
|
|
/* check that provider file exists */
|
|
memset(&f_stat, 0, sizeof(MY_STAT));
|
|
if (!my_stat(path, &f_stat, MYF(0)))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (MY_S_ISDIR(f_stat.st_mode))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool wsrep_provider_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if (wsrep_provider_plugin_enabled())
|
|
{
|
|
my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->var->name.str, "read only");
|
|
return true;
|
|
}
|
|
|
|
char wsrep_provider_buf[FN_REFLEN];
|
|
|
|
if ((! var->save_result.string_value.str) ||
|
|
(var->save_result.string_value.length > (FN_REFLEN - 1))) // safety
|
|
goto err;
|
|
|
|
memcpy(wsrep_provider_buf, var->save_result.string_value.str,
|
|
var->save_result.string_value.length);
|
|
wsrep_provider_buf[var->save_result.string_value.length]= 0;
|
|
|
|
if (!wsrep_provider_verify(wsrep_provider_buf)) return 0;
|
|
|
|
err:
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
|
|
var->save_result.string_value.str ?
|
|
var->save_result.string_value.str : "NULL");
|
|
return 1;
|
|
}
|
|
|
|
bool wsrep_provider_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
bool rcode= false;
|
|
|
|
|
|
WSREP_DEBUG("wsrep_provider_update: %s", wsrep_provider);
|
|
|
|
/* stop replication is heavy operation, and includes closing all client
|
|
connections. Closing clients may need to get LOCK_global_system_variables
|
|
at least in MariaDB.
|
|
|
|
Note: releasing LOCK_global_system_variables may cause race condition, if
|
|
there can be several concurrent clients changing wsrep_provider
|
|
*/
|
|
mysql_mutex_unlock(&LOCK_global_system_variables);
|
|
wsrep_stop_replication(thd);
|
|
|
|
/* provider status variables are allocated in provider library
|
|
and need to freed here, otherwise a dangling reference to
|
|
wsrep_status_vars would remain in THD
|
|
*/
|
|
wsrep_free_status(thd);
|
|
|
|
if (wsrep_inited == 1)
|
|
wsrep_deinit(false);
|
|
|
|
char* tmp= strdup(wsrep_provider); // wsrep_init() rewrites provider
|
|
//when fails
|
|
|
|
if (wsrep_init())
|
|
{
|
|
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp, my_error, "wsrep_init failed");
|
|
rcode= true;
|
|
}
|
|
free(tmp);
|
|
|
|
// we sure don't want to use old address with new provider
|
|
wsrep_cluster_address_init(NULL);
|
|
wsrep_provider_options_init(NULL);
|
|
if (!rcode)
|
|
refresh_provider_options();
|
|
|
|
wsrep_set_wsrep_on(thd);
|
|
mysql_mutex_lock(&LOCK_global_system_variables);
|
|
|
|
return rcode;
|
|
}
|
|
|
|
void wsrep_provider_init (const char* value)
|
|
{
|
|
WSREP_DEBUG("wsrep_provider_init: %s -> %s",
|
|
(wsrep_provider) ? wsrep_provider : "null",
|
|
(value) ? value : "null");
|
|
if (NULL == value || wsrep_provider_verify (value))
|
|
{
|
|
WSREP_ERROR("Bad initial value for wsrep_provider: %s",
|
|
(value ? value : ""));
|
|
return;
|
|
}
|
|
|
|
if (wsrep_provider) my_free((void *)wsrep_provider);
|
|
wsrep_provider= my_strdup(PSI_INSTRUMENT_MEM, value, MYF(0));
|
|
wsrep_set_wsrep_on(NULL);
|
|
}
|
|
|
|
bool wsrep_provider_options_check(sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if (!WSREP_ON)
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
|
|
return true;
|
|
}
|
|
if (wsrep_provider_plugin_enabled())
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS,
|
|
"wsrep_provider_options cannot be changed while the "
|
|
"wsrep-provider plugin is loaded", MYF(0));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_provider_options_update(sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
if (wsrep_provider_options)
|
|
{
|
|
enum wsrep::provider::status ret=
|
|
Wsrep_server_state::instance().provider().options(wsrep_provider_options);
|
|
if (ret)
|
|
{
|
|
WSREP_ERROR("Set options returned %d", ret);
|
|
goto err;
|
|
}
|
|
|
|
return refresh_provider_options();
|
|
}
|
|
err:
|
|
refresh_provider_options();
|
|
return true;
|
|
}
|
|
|
|
void wsrep_provider_options_init(const char* value)
|
|
{
|
|
if (wsrep_provider_options && wsrep_provider_options != value)
|
|
my_free((void *)wsrep_provider_options);
|
|
wsrep_provider_options= value ? my_strdup(PSI_INSTRUMENT_MEM, value, MYF(0)) : NULL;
|
|
}
|
|
|
|
bool wsrep_reject_queries_update(sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
switch (wsrep_reject_queries) {
|
|
case WSREP_REJECT_NONE:
|
|
WSREP_INFO("Allowing client queries due to manual setting");
|
|
break;
|
|
case WSREP_REJECT_ALL:
|
|
WSREP_INFO("Rejecting client queries due to manual setting");
|
|
break;
|
|
case WSREP_REJECT_ALL_KILL:
|
|
/* close all client connections, but this one */
|
|
wsrep_close_client_connections(FALSE, thd);
|
|
WSREP_INFO("Rejecting client queries and killing connections due to manual setting");
|
|
break;
|
|
default:
|
|
WSREP_INFO("Unknown value for wsrep_reject_queries: %lu",
|
|
wsrep_reject_queries);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_debug_update(sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
// Give warnings if wsrep_debug is set and wsrep is disabled or
|
|
// provider is not loaded, it will not have any effect
|
|
if ((!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
|
|
&& wsrep_debug)
|
|
{
|
|
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Setting 'wsrep_debug' has no effect because "
|
|
"wsrep is switched off");
|
|
wsrep_debug= 0;
|
|
}
|
|
else
|
|
Wsrep_server_state::instance().debug_log_level(wsrep_debug);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
wsrep_gtid_seq_no_check(sys_var *self, THD *thd, set_var *var)
|
|
{
|
|
ulonglong new_wsrep_gtid_seq_no= var->save_result.ulonglong_value;
|
|
if (wsrep_gtid_mode && new_wsrep_gtid_seq_no > wsrep_gtid_server.seqno())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static int wsrep_cluster_address_verify (const char* cluster_address_str)
|
|
{
|
|
/* There is no predefined address format, it depends on provider. */
|
|
return 0;
|
|
}
|
|
|
|
bool wsrep_cluster_address_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
char addr_buf[FN_REFLEN+1];
|
|
|
|
if ((! var->save_result.string_value.str) ||
|
|
(var->save_result.string_value.length >= FN_REFLEN)) // safety
|
|
goto err;
|
|
|
|
strmake(addr_buf, var->save_result.string_value.str,
|
|
MY_MIN(sizeof(addr_buf)-1, var->save_result.string_value.length));
|
|
|
|
if (!wsrep_cluster_address_verify(addr_buf))
|
|
return 0;
|
|
|
|
err:
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
|
|
var->save_result.string_value.str ?
|
|
var->save_result.string_value.str : "NULL");
|
|
return 1;
|
|
}
|
|
|
|
bool wsrep_cluster_address_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
if (!Wsrep_server_state::instance().is_provider_loaded())
|
|
{
|
|
WSREP_INFO("WSREP (galera) provider is not loaded, can't re(start) replication.");
|
|
return false;
|
|
}
|
|
|
|
/* stop replication is heavy operation, and includes closing all client
|
|
connections. Closing clients may need to get LOCK_global_system_variables
|
|
at least in MariaDB.
|
|
*/
|
|
char *tmp= my_strdup(PSI_INSTRUMENT_ME, wsrep_cluster_address, MYF(MY_WME));
|
|
WSREP_DEBUG("wsrep_cluster_address_update: %s", wsrep_cluster_address);
|
|
mysql_mutex_unlock(&LOCK_global_system_variables);
|
|
|
|
mysql_mutex_lock(&LOCK_wsrep_cluster_config);
|
|
wsrep_stop_replication(thd);
|
|
|
|
if (*tmp && wsrep_start_replication(tmp))
|
|
{
|
|
wsrep_create_rollbacker();
|
|
WSREP_DEBUG("Cluster address update creating %ld applier threads running %lu",
|
|
wsrep_slave_threads, wsrep_running_applier_threads);
|
|
wsrep_create_appliers(wsrep_slave_threads);
|
|
}
|
|
mysql_mutex_unlock(&LOCK_wsrep_cluster_config);
|
|
|
|
mysql_mutex_lock(&LOCK_global_system_variables);
|
|
if (strcmp(tmp, wsrep_cluster_address))
|
|
{
|
|
my_free((void*)wsrep_cluster_address);
|
|
wsrep_cluster_address= tmp;
|
|
}
|
|
else
|
|
my_free(tmp);
|
|
|
|
return false;
|
|
}
|
|
|
|
void wsrep_cluster_address_init (const char* value)
|
|
{
|
|
WSREP_DEBUG("wsrep_cluster_address_init: %s -> %s",
|
|
(wsrep_cluster_address) ? wsrep_cluster_address : "null",
|
|
(value) ? value : "null");
|
|
|
|
my_free(const_cast<char*>(wsrep_cluster_address));
|
|
wsrep_cluster_address= my_strdup(PSI_INSTRUMENT_MEM, safe_str(value), MYF(0));
|
|
}
|
|
|
|
/* wsrep_cluster_name cannot be NULL or an empty string. */
|
|
bool wsrep_cluster_name_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if (!var->save_result.string_value.str ||
|
|
(var->save_result.string_value.length == 0))
|
|
{
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
|
|
(var->save_result.string_value.str ?
|
|
var->save_result.string_value.str : "NULL"));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool wsrep_cluster_name_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool wsrep_node_name_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
// TODO: for now 'allow' 0-length string to be valid (default)
|
|
if (!var->save_result.string_value.str)
|
|
{
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
|
|
(var->save_result.string_value.str ?
|
|
var->save_result.string_value.str : "NULL"));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool wsrep_node_name_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool wsrep_node_address_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if ((! var->save_result.string_value.str) ||
|
|
(var->save_result.string_value.length >= FN_REFLEN)) // safety
|
|
goto err;
|
|
|
|
if (var->save_result.string_value.length)
|
|
{
|
|
if (wsrep_check_request_str(var->save_result.string_value.str,
|
|
wsrep_address_char, false))
|
|
goto err;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
|
|
var->save_result.string_value.str ?
|
|
var->save_result.string_value.str : "NULL");
|
|
return 1;
|
|
}
|
|
|
|
bool wsrep_node_address_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void wsrep_node_address_init (const char* value)
|
|
{
|
|
if (wsrep_node_address && strcmp(wsrep_node_address, value))
|
|
my_free ((void*)wsrep_node_address);
|
|
|
|
wsrep_node_address= value ? my_strdup(PSI_INSTRUMENT_MEM, value, MYF(0)) : NULL;
|
|
}
|
|
|
|
static void wsrep_slave_count_change_update ()
|
|
{
|
|
wsrep_slave_count_change = (wsrep_slave_threads - wsrep_running_applier_threads);
|
|
WSREP_DEBUG("Change on slave threads: New %ld old %lu difference %d",
|
|
wsrep_slave_threads, wsrep_running_applier_threads, wsrep_slave_count_change);
|
|
}
|
|
|
|
bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
if (!wsrep_cluster_address_exists())
|
|
return false;
|
|
|
|
mysql_mutex_unlock(&LOCK_global_system_variables);
|
|
mysql_mutex_lock(&LOCK_wsrep_slave_threads);
|
|
mysql_mutex_lock(&LOCK_global_system_variables);
|
|
bool res= false;
|
|
|
|
wsrep_slave_count_change_update();
|
|
|
|
if (wsrep_slave_count_change > 0)
|
|
{
|
|
WSREP_DEBUG("Creating %d applier threads, total %ld", wsrep_slave_count_change, wsrep_slave_threads);
|
|
wsrep_thread_create_failed.store(false, std::memory_order_relaxed);
|
|
res= wsrep_create_appliers(wsrep_slave_count_change, true);
|
|
mysql_mutex_unlock(&LOCK_global_system_variables);
|
|
mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
|
|
// Thread creation and execution is asynchronous, therefore we need
|
|
// wait them to be started or error produced
|
|
while (wsrep_running_applier_threads != (ulong)wsrep_slave_threads &&
|
|
!wsrep_thread_create_failed.load(std::memory_order_relaxed))
|
|
{
|
|
my_sleep(1000);
|
|
}
|
|
|
|
mysql_mutex_lock(&LOCK_global_system_variables);
|
|
|
|
if (wsrep_thread_create_failed.load(std::memory_order_relaxed)) {
|
|
wsrep_slave_threads= wsrep_running_applier_threads;
|
|
return true;
|
|
}
|
|
|
|
WSREP_DEBUG("Running %lu applier threads", wsrep_running_applier_threads);
|
|
wsrep_slave_count_change = 0;
|
|
}
|
|
else
|
|
mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if (!WSREP_ON)
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (thd->global_read_lock.is_acquired())
|
|
{
|
|
my_message (ER_CANNOT_USER, "Global read lock acquired. Can't set 'wsrep_desync'", MYF(0));
|
|
return true;
|
|
}
|
|
|
|
bool new_wsrep_desync= (bool) var->save_result.ulonglong_value;
|
|
if (wsrep_desync == new_wsrep_desync) {
|
|
if (new_wsrep_desync) {
|
|
push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"'wsrep_desync' is already ON.");
|
|
} else {
|
|
push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"'wsrep_desync' is already OFF.");
|
|
}
|
|
return false;
|
|
}
|
|
int ret= 1;
|
|
if (new_wsrep_desync) {
|
|
ret= Wsrep_server_state::instance().provider().desync();
|
|
if (ret) {
|
|
WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret,
|
|
thd->db.str, wsrep_thd_query(thd));
|
|
my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query());
|
|
return true;
|
|
}
|
|
} else {
|
|
THD_STAGE_INFO(thd, stage_waiting_flow);
|
|
ret= Wsrep_server_state::instance().provider().resync();
|
|
if (ret != WSREP_OK) {
|
|
WSREP_WARN ("SET resync failed %d for schema: %s, query: %s", ret,
|
|
thd->get_db(), wsrep_thd_query(thd));
|
|
my_error (ER_CANNOT_USER, MYF(0), "'resync'", thd->query());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_trx_fragment_size_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if (var->value == NULL) {
|
|
return false;
|
|
}
|
|
|
|
const ulong new_trx_fragment_size= var->value->val_uint();
|
|
|
|
if (new_trx_fragment_size)
|
|
{
|
|
// Give error if wsrep_trx_fragment_size is set and wsrep is disabled or
|
|
// provider is not loaded
|
|
if (!WSREP(thd) || !Wsrep_server_state::instance().is_provider_loaded())
|
|
{
|
|
push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_trx_fragment_size' to a value other than "
|
|
"0 because wsrep is switched off.");
|
|
return true;
|
|
}
|
|
|
|
if (!wsrep_provider_is_SR_capable())
|
|
{
|
|
push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_trx_fragment_size' to a value other than "
|
|
"0 because the wsrep_provider does not support streaming "
|
|
"replication.");
|
|
return true;
|
|
}
|
|
|
|
if (wsrep_protocol_version < 4)
|
|
{
|
|
push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_trx_fragment_size' to a value other than "
|
|
"0 because cluster is not yet operating in Galera 4 mode.");
|
|
return true;
|
|
}
|
|
|
|
if (!ha_table_exists(thd, &WSREP_LEX_SCHEMA, &WSREP_LEX_STREAMING, 0,
|
|
0, 0, 0))
|
|
{
|
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_trx_fragment_size' to a value other than "
|
|
"0 because streaming table %s.%s does not exists.",
|
|
WSREP_LEX_SCHEMA.str, WSREP_LEX_STREAMING.str);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_trx_fragment_size_update(sys_var* self, THD *thd, enum_var_type)
|
|
{
|
|
WSREP_DEBUG("wsrep_trx_fragment_size_update: %llu",
|
|
thd->variables.wsrep_trx_fragment_size);
|
|
|
|
if (thd->variables.wsrep_trx_fragment_size)
|
|
{
|
|
return thd->wsrep_cs().enable_streaming(
|
|
wsrep_fragment_unit(thd->variables.wsrep_trx_fragment_unit),
|
|
size_t(thd->variables.wsrep_trx_fragment_size));
|
|
}
|
|
else if (wsrep_streaming_enabled(thd))
|
|
{
|
|
thd->wsrep_cs().disable_streaming();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_trx_fragment_unit_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
uint new_trx_fragment_unit= (uint)var->save_result.ulonglong_value;
|
|
// Do not allow setting fragment unit anything else than
|
|
// default if galera is not enabled and streaming replication
|
|
// table does not exists.
|
|
if (thd->variables.wsrep_trx_fragment_size ||
|
|
new_trx_fragment_unit != WSREP_FRAG_BYTES)
|
|
{
|
|
// Give error if wsrep_trx_fragment_unit is set and wsrep is disabled or
|
|
// provider is not loaded
|
|
if (!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
|
|
{
|
|
push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_trx_fragment_unit' because "
|
|
"wsrep is switched off");
|
|
return true;
|
|
}
|
|
|
|
if (!ha_table_exists(thd, &WSREP_LEX_SCHEMA, &WSREP_LEX_STREAMING,
|
|
0, 0, 0, 0))
|
|
{
|
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_trx_fragment_unit' "
|
|
"because streaming table %s.%s does not exists.",
|
|
WSREP_LEX_SCHEMA.str, WSREP_LEX_STREAMING.str);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_trx_fragment_unit_update(sys_var* self, THD *thd, enum_var_type)
|
|
{
|
|
WSREP_DEBUG("wsrep_trx_fragment_unit_update: %lu",
|
|
thd->variables.wsrep_trx_fragment_unit);
|
|
|
|
if (thd->variables.wsrep_trx_fragment_size)
|
|
{
|
|
return thd->wsrep_cs().enable_streaming(
|
|
wsrep_fragment_unit(thd->variables.wsrep_trx_fragment_unit),
|
|
size_t(thd->variables.wsrep_trx_fragment_size));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_max_ws_size_check(sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
if (!WSREP_ON)
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
|
|
return true;
|
|
}
|
|
if (thd->wsrep_trx().active())
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP transaction is active", MYF(0));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_max_ws_size_update(sys_var *self, THD *thd, enum_var_type)
|
|
{
|
|
char max_ws_size_opt[128];
|
|
my_snprintf(max_ws_size_opt, sizeof(max_ws_size_opt),
|
|
"repl.max_ws_size=%lu", wsrep_max_ws_size);
|
|
enum wsrep::provider::status ret= Wsrep_server_state::instance().provider().options(max_ws_size_opt);
|
|
if (ret)
|
|
{
|
|
WSREP_ERROR("Set options returned %d", ret);
|
|
return true;
|
|
}
|
|
return refresh_provider_options();
|
|
}
|
|
|
|
bool wsrep_mode_check(sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
ulonglong new_wsrep_mode= var->save_result.ulonglong_value;
|
|
ulonglong old_wsrep_mode= wsrep_mode;
|
|
wsrep_mode= new_wsrep_mode;
|
|
if (wsrep_check_mode(WSREP_MODE_REPLICATE_MYISAM) ||
|
|
wsrep_check_mode(WSREP_MODE_REPLICATE_ARIA))
|
|
{
|
|
if (!(wsrep_forced_binlog_format == BINLOG_FORMAT_UNSPEC ||
|
|
wsrep_forced_binlog_format == BINLOG_FORMAT_ROW))
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "wsrep_mode=[REPLICATE_MYISAM|REPLICATE_ARIA] "
|
|
"can't be enabled if wsrep_forced_binlog != [NONE|ROW]", MYF(0));
|
|
wsrep_mode= old_wsrep_mode;
|
|
return true;
|
|
}
|
|
}
|
|
wsrep_mode= old_wsrep_mode;
|
|
return false;
|
|
}
|
|
|
|
#if DYNAMIC
|
|
// somehow this mysql status thing works only with statically allocated arrays.
|
|
static SHOW_VAR* mysql_status_vars= NULL;
|
|
static int mysql_status_len= -1;
|
|
#else
|
|
static SHOW_VAR mysql_status_vars[512 + 1];
|
|
static const int mysql_status_len= 512;
|
|
#endif
|
|
|
|
static void export_wsrep_status_to_mysql(THD* thd)
|
|
{
|
|
int wsrep_status_len, i;
|
|
|
|
thd->wsrep_status_vars= Wsrep_server_state::instance().status();
|
|
|
|
/* Add wsrep_checkpoint_position and SE checkpoint */
|
|
wsrep::provider::status_variable checkpoint("checkpoint_position",
|
|
wsrep_get_checkpoint());
|
|
thd->wsrep_status_vars.push_back(checkpoint);
|
|
XID xid;
|
|
wsrep_get_SE_checkpoint(xid);
|
|
const std::string se_checkpoint= wsrep_xid_print(&xid);
|
|
wsrep::provider::status_variable se_chkpoint("se_checkpoint",
|
|
se_checkpoint);
|
|
thd->wsrep_status_vars.push_back(se_chkpoint);
|
|
|
|
wsrep_status_len= thd->wsrep_status_vars.size();
|
|
|
|
#if DYNAMIC
|
|
if (wsrep_status_len != mysql_status_len) {
|
|
void* tmp= my_realloc(key_memory_WSREP,
|
|
mysql_status_vars,
|
|
(wsrep_status_len + 1) * sizeof(SHOW_VAR),
|
|
MYF(MY_ALLOW_ZERO_PTR));
|
|
if (!tmp) {
|
|
|
|
sql_print_error ("Out of memory for wsrep status variables."
|
|
"Number of variables: %d", wsrep_status_len);
|
|
return;
|
|
}
|
|
|
|
mysql_status_len= wsrep_status_len;
|
|
mysql_status_vars= (SHOW_VAR*)tmp;
|
|
}
|
|
/* @TODO: fix this: */
|
|
#else
|
|
if (mysql_status_len < wsrep_status_len) wsrep_status_len= mysql_status_len;
|
|
#endif
|
|
|
|
for (i= 0; i < wsrep_status_len; i++)
|
|
{
|
|
mysql_status_vars[i].name= (char*)thd->wsrep_status_vars[i].name().c_str();
|
|
mysql_status_vars[i].value= (char*)thd->wsrep_status_vars[i].value().c_str();
|
|
mysql_status_vars[i].type= SHOW_CHAR;
|
|
}
|
|
|
|
mysql_status_vars[wsrep_status_len].name = NullS;
|
|
mysql_status_vars[wsrep_status_len].value = NullS;
|
|
mysql_status_vars[wsrep_status_len].type = SHOW_LONG;
|
|
}
|
|
|
|
int wsrep_show_status (THD *thd, SHOW_VAR *var, void *,
|
|
system_status_var *, enum_var_type)
|
|
{
|
|
/* Note that we should allow show status like 'wsrep%' even
|
|
when WSREP(thd) is false. */
|
|
if (WSREP_ON)
|
|
{
|
|
export_wsrep_status_to_mysql(thd);
|
|
var->type= SHOW_ARRAY;
|
|
var->value= (char *) &mysql_status_vars;
|
|
}
|
|
else
|
|
{
|
|
var->type= SHOW_CHAR;
|
|
var->value= (char*) "0";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void wsrep_free_status (THD* thd)
|
|
{
|
|
thd->wsrep_status_vars.clear();
|
|
}
|
|
|
|
void wsrep_free_status_vars()
|
|
{
|
|
#if DYNAMIC
|
|
my_free(mysql_status_vars);
|
|
mysql_status_vars= NULL;
|
|
mysql_status_len= 0;
|
|
#endif
|
|
}
|
|
|
|
bool wsrep_gtid_domain_id_update(sys_var* self, THD *thd, enum_var_type)
|
|
{
|
|
WSREP_DEBUG("wsrep_gtid_domain_id_update: %llu",
|
|
wsrep_gtid_domain_id);
|
|
wsrep_gtid_server.domain_id= wsrep_gtid_domain_id;
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_forced_binlog_format_check(sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
ulonglong new_forced_binlog_format= var->save_result.ulonglong_value;
|
|
|
|
if (!(new_forced_binlog_format == BINLOG_FORMAT_UNSPEC ||
|
|
new_forced_binlog_format == BINLOG_FORMAT_ROW))
|
|
{
|
|
if (wsrep_check_mode(WSREP_MODE_BINLOG_ROW_FORMAT_ONLY))
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "wsrep_forced_binlog_format=[MIXED|STATEMENT] can't be set "
|
|
"if wsrep_mode=BINLOG_ROW_FORMAT_ONLY", MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (wsrep_check_mode(WSREP_MODE_REPLICATE_MYISAM) ||
|
|
wsrep_check_mode(WSREP_MODE_REPLICATE_ARIA))
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "wsrep_forced_binlog_format=[MIXED|STATEMENT] can't be set "
|
|
"if wsrep_mode=[REPLICATE_MYISAM|REPLICATE_ARIA]", MYF(0));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_slave_threads_check (sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
ulonglong new_slave_threads= var->save_result.ulonglong_value;
|
|
|
|
if (!WSREP_ON)
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (new_slave_threads &&
|
|
Wsrep_server_state::instance().state() == wsrep::server_state::s_disconnected)
|
|
{
|
|
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_WRONG_VALUE_FOR_VAR,
|
|
"Cannot set 'wsrep_slave_threads' because "
|
|
"wsrep is disconnected");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wsrep_max_ws_rows_check(sys_var *self, THD* thd, set_var* var)
|
|
{
|
|
unsigned long long max_rows= (unsigned long long)var->save_result.ulonglong_value;
|
|
|
|
// Default 0 is always allowed
|
|
if (max_rows == 0)
|
|
return false;
|
|
|
|
// Note that we allow changing this even when WSREP is not on
|
|
if (thd->wsrep_trx().active())
|
|
{
|
|
my_message(ER_WRONG_ARGUMENTS, "WSREP transaction is active", MYF(0));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Function is used to check if user given sst temporary directory
|
|
is valid. Function allows nullptr or empty string i.e. no directory
|
|
given. If something real is given it must be an path that exists,
|
|
short enough, path must be a directory and it must not be
|
|
same as datadir. If proper path was not given actual used
|
|
parameter value remains as nullptr. If proper path was given
|
|
it is copied to wsrep_sst_tmp_dir_real and used on SST. */
|
|
void wsrep_sst_tmp_dir_check(void)
|
|
{
|
|
if (wsrep_sst_tmp_dir == nullptr || strlen(wsrep_sst_tmp_dir) == 0)
|
|
return; // DEFAULT is ok
|
|
|
|
if (strlen(wsrep_sst_tmp_dir) >= FN_REFLEN)
|
|
{
|
|
WSREP_ERROR("Option --wsrep-sst-tmp-dir value %s is too long", wsrep_sst_tmp_dir);
|
|
return;
|
|
}
|
|
|
|
struct stat statinfo;
|
|
int ret = stat(wsrep_sst_tmp_dir, &statinfo);
|
|
|
|
if (!ret)
|
|
{
|
|
/* path exists, ok */
|
|
} else {
|
|
/* path does not exist */
|
|
WSREP_WARN("SST temporary path %s does not exists, path not used.",
|
|
wsrep_sst_tmp_dir);
|
|
return;
|
|
}
|
|
|
|
if (!S_ISDIR(statinfo.st_mode))
|
|
{
|
|
WSREP_WARN("SST temporary path %s is not a directory, path not used.",
|
|
wsrep_sst_tmp_dir);
|
|
return;
|
|
}
|
|
|
|
/* If path is almost same, do not allow it. */
|
|
struct stat datadir_stat;
|
|
if (stat(mysql_real_data_home_ptr, &datadir_stat) == 0)
|
|
{
|
|
if (statinfo.st_dev == datadir_stat.st_dev &&
|
|
statinfo.st_ino == datadir_stat.st_ino)
|
|
{
|
|
WSREP_WARN("SST temporary path %s is same prefix as datadir %s, path not used.",
|
|
wsrep_sst_tmp_dir, mysql_real_data_home_ptr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
wsrep_sst_tmp_dir_real= my_strdup(PSI_INSTRUMENT_ME, wsrep_sst_tmp_dir, MYF(0));
|
|
if (wsrep_sst_tmp_dir_real)
|
|
WSREP_INFO("SST temporary path set to %s", wsrep_sst_tmp_dir_real);
|
|
}
|