mysql5/mysql-5.7.27/sql/ndb_repl_tab.cc

561 lines
16 KiB
C++

/*
Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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
*/
#include "ha_ndbcluster_tables.h"
#include "ndb_repl_tab.h"
#ifdef HAVE_NDB_BINLOG
#include "ha_ndbcluster_glue.h"
#include "ha_ndbcluster_connection.h" /* do_retry_sleep() */
#include "ndb_table_guard.h"
#include "ndb_share.h"
Ndb_rep_tab_key::Ndb_rep_tab_key(const char* _db,
const char* _table_name,
uint _server_id)
{
uint db_len= (uint) strlen(_db);
uint tabname_len = (uint) strlen(_table_name);
assert(DB_MAXLEN < 256); /* Fits in Varchar */
assert(db_len <= DB_MAXLEN);
assert(tabname_len <= TABNAME_MAXLEN);
memcpy(&db[1], _db, db_len);
db[ 0 ]= db_len;
memcpy(&table_name[1], _table_name, tabname_len);
table_name[ 0 ]= tabname_len;
server_id= _server_id;
null_terminate_strings();
}
void Ndb_rep_tab_key::null_terminate_strings()
{
assert((uint) db[0] <= DB_MAXLEN);
assert((uint) table_name[0] <= TABNAME_MAXLEN);
db[ db[0] + 1] = '\0';
table_name[ table_name[0] + 1] = '\0';
}
int
Ndb_rep_tab_key::attempt_match(const char* keyptr,
const uint keylen,
const char* candidateptr,
const uint candidatelen,
const int exactmatchvalue)
{
if (my_strnncoll(system_charset_info,
(const uchar*) keyptr,
keylen,
(const uchar*) candidateptr,
candidatelen) == 0)
{
/* Exact match */
return exactmatchvalue;
}
else if (my_wildcmp(system_charset_info,
keyptr,
keyptr + keylen,
candidateptr,
candidateptr + candidatelen,
'\\', wild_one, wild_many) == 0)
{
/* Wild match */
return 0;
}
/* No match */
return -1;
};
int
Ndb_rep_tab_key::get_match_quality(const Ndb_rep_tab_key* key,
const Ndb_rep_tab_key* candidate_row)
{
/* 0= No match
1= Loosest match
8= Best match
Actual mapping is :
db table serverid Quality
W W W 1
W W = 2
W = W 3
W = = 4
= W W 5
= W = 6
= = W 7
= = = 8
*/
int quality = MIN_MATCH_VAL;
int rc;
if ((rc = attempt_match(&key->db[1],
key->db[0],
&candidate_row->db[1],
candidate_row->db[0],
EXACT_MATCH_DB)) == -1)
{
/* No match, drop out now */
return 0;
}
quality+= rc;
if ((rc = attempt_match(&key->table_name[1],
key->table_name[0],
&candidate_row->table_name[1],
candidate_row->table_name[0],
EXACT_MATCH_TABLE_NAME)) == -1)
{
/* No match, drop out now */
return 0;
}
quality+= rc;
if (candidate_row->server_id == key->server_id)
{
/* Exact match */
quality += EXACT_MATCH_SERVER_ID;
}
else if (candidate_row->server_id != 0)
{
/* No match */
return 0;
}
return quality;
};
Ndb_rep_tab_row::Ndb_rep_tab_row()
: binlog_type(0), cfs_is_null(true)
{
memset(conflict_fn_spec, 0, sizeof(conflict_fn_spec));
}
const char* Ndb_rep_tab_reader::ndb_rep_db= NDB_REP_DB;
const char* Ndb_rep_tab_reader::ndb_replication_table= NDB_REPLICATION_TABLE;
const char* Ndb_rep_tab_reader::nrt_db= "db";
const char* Ndb_rep_tab_reader::nrt_table_name= "table_name";
const char* Ndb_rep_tab_reader::nrt_server_id= "server_id";
const char* Ndb_rep_tab_reader::nrt_binlog_type= "binlog_type";
const char* Ndb_rep_tab_reader::nrt_conflict_fn= "conflict_fn";
Ndb_rep_tab_reader::Ndb_rep_tab_reader()
: binlog_flags(NBT_DEFAULT),
conflict_fn_spec(NULL),
warning_msg(NULL)
{
}
int Ndb_rep_tab_reader::check_schema(const NdbDictionary::Table* reptab,
NdbDictionary::Dictionary* dict,
const char** error_str)
{
DBUG_ENTER("check_schema");
*error_str= NULL;
const NdbDictionary::Column
*col_db, *col_table_name, *col_server_id, *col_binlog_type, *col_conflict_fn;
if (reptab->getNoOfPrimaryKeys() != 3)
{
*error_str= "Wrong number of primary key parts, expected 3";
DBUG_RETURN(-2);
}
col_db= reptab->getColumn(*error_str= nrt_db);
if (col_db == NULL ||
!col_db->getPrimaryKey() ||
col_db->getType() != NdbDictionary::Column::Varbinary)
DBUG_RETURN(-1);
col_table_name= reptab->getColumn(*error_str= nrt_table_name);
if (col_table_name == NULL ||
!col_table_name->getPrimaryKey() ||
col_table_name->getType() != NdbDictionary::Column::Varbinary)
DBUG_RETURN(-1);
col_server_id= reptab->getColumn(*error_str= nrt_server_id);
if (col_server_id == NULL ||
!col_server_id->getPrimaryKey() ||
col_server_id->getType() != NdbDictionary::Column::Unsigned)
DBUG_RETURN(-1);
col_binlog_type= reptab->getColumn(*error_str= nrt_binlog_type);
if (col_binlog_type == NULL ||
col_binlog_type->getPrimaryKey() ||
col_binlog_type->getType() != NdbDictionary::Column::Unsigned)
DBUG_RETURN(-1);
col_conflict_fn= reptab->getColumn(*error_str= nrt_conflict_fn);
if (col_conflict_fn != NULL)
{
if ((col_conflict_fn->getPrimaryKey()) ||
(col_conflict_fn->getType() != NdbDictionary::Column::Varbinary))
DBUG_RETURN(-1);
}
DBUG_RETURN(0);
}
int
Ndb_rep_tab_reader::scan_candidates(Ndb* ndb,
const NdbDictionary::Table* reptab,
const char* db,
const char* table_name,
uint server_id,
Ndb_rep_tab_row& best_match)
{
uint retries= 100;
int retry_sleep= 30; /* 30 milliseconds, transaction */
int best_match_quality= 0;
NdbError ok;
NdbError ndberror;
/* Loop to enable temporary error retries */
while(true)
{
ndberror = ok; /* reset */
NdbTransaction *trans= ndb->startTransaction();
if (trans == NULL)
{
ndberror= ndb->getNdbError();
if (ndberror.status == NdbError::TemporaryError)
{
if (retries--)
{
do_retry_sleep(retry_sleep);
continue;
}
}
break;
}
NdbRecAttr* ra_binlog_type= NULL;
NdbRecAttr* ra_conflict_fn_spec= NULL;
Ndb_rep_tab_row row;
bool have_conflict_fn_col = (reptab->getColumn(nrt_conflict_fn) != NULL);
/* Define scan op on ndb_replication */
NdbScanOperation* scanOp = trans->getNdbScanOperation(reptab);
if (scanOp == NULL) { ndberror= trans->getNdbError(); break; }
if ((scanOp->readTuples(NdbScanOperation::LM_CommittedRead) != 0) ||
(scanOp->getValue(nrt_db, (char*) row.key.db) == NULL) ||
(scanOp->getValue(nrt_table_name, (char*) row.key.table_name) == NULL) ||
(scanOp->getValue(nrt_server_id, (char*) &row.key.server_id) == NULL) ||
((ra_binlog_type = scanOp->getValue(nrt_binlog_type, (char*) &row.binlog_type)) == NULL) ||
(have_conflict_fn_col &&
((ra_conflict_fn_spec=
scanOp->getValue(nrt_conflict_fn, (char*) row.conflict_fn_spec)) == NULL)))
{
ndberror= scanOp->getNdbError();
break;
}
if (trans->execute(NdbTransaction::NoCommit,
NdbOperation::AO_IgnoreError))
{
ndberror= trans->getNdbError();
ndb->closeTransaction(trans);
if (ndberror.status == NdbError::TemporaryError)
{
if (retries--)
{
do_retry_sleep(retry_sleep);
continue;
}
}
break;
}
/* Scroll through results, looking for best match */
DBUG_PRINT("info", ("Searching ndb_replication for %s.%s %u",
db, table_name, server_id));
bool ambiguous_match = false;
Ndb_rep_tab_key searchkey(db, table_name, server_id);
int scan_rc;
while ((scan_rc= scanOp->nextResult(true)) == 0)
{
if (ra_binlog_type->isNULL() == 1)
{
row.binlog_type= NBT_DEFAULT;
}
if (ra_conflict_fn_spec)
{
row.set_conflict_fn_spec_null(ra_conflict_fn_spec->isNULL() == 1);
}
/* Compare row to searchkey to get quality of match */
int match_quality= Ndb_rep_tab_key::get_match_quality(&searchkey,
&row.key);
#ifndef DBUG_OFF
{
row.null_terminate_strings();
DBUG_PRINT("info", ("Candidate : %s.%s %u : %u %s"
" Match quality : %u.",
row.key.get_db(),
row.key.get_table_name(),
row.key.server_id,
row.binlog_type,
row.get_conflict_fn_spec(),
match_quality));
}
#endif
if (match_quality > 0)
{
if (match_quality == best_match_quality)
{
ambiguous_match = true;
/* Ambiguous matches...*/
my_snprintf(warning_msg_buffer, sizeof(warning_msg_buffer),
"Ambiguous matches in %s.%s for %s.%s (%u)."
"Candidates : %s.%s (%u), %s.%s (%u).",
ndb_rep_db, ndb_replication_table,
db, table_name, server_id,
&best_match.key.db[1],
&best_match.key.table_name[1],
best_match.key.server_id,
&row.key.db[1],
&row.key.table_name[1],
row.key.server_id);
DBUG_PRINT("info", ("%s", warning_msg_buffer));
}
if (match_quality > best_match_quality)
{
/* New best match */
best_match= row;
best_match_quality = match_quality;
ambiguous_match = false;
if (best_match_quality == Ndb_rep_tab_key::EXACT_MATCH_QUALITY)
{
/* We're done */
break;
}
}
} /* if (match_quality > 0) */
} /* while ((scan_rc= scanOp->nextResult(true)) */
if (scan_rc < 0)
{
ndberror= scanOp->getNdbError();
if (ndberror.status == NdbError::TemporaryError)
{
if (retries--)
{
ndb->closeTransaction(trans);
do_retry_sleep(retry_sleep);
continue;
}
}
}
ndb->closeTransaction(trans);
if (ambiguous_match)
{
warning_msg= warning_msg_buffer;
best_match_quality = -1;
}
break;
} /* while(true) */
if (ndberror.code != 0)
{
my_snprintf(warning_msg_buffer, sizeof(warning_msg_buffer),
"Unable to retrieve %s.%s, logging and "
"conflict resolution may not function "
"as intended (ndberror %u)",
ndb_rep_db, ndb_replication_table,
ndberror.code);
warning_msg= warning_msg_buffer;
best_match_quality = -1;
}
return best_match_quality;
}
int
Ndb_rep_tab_reader::lookup(Ndb* ndb,
/* Keys */
const char* db,
const char* table_name,
uint server_id)
{
DBUG_ENTER("lookup");
int error= 0;
NdbError ndberror;
const char *error_str= "<none>";
/* Set results to defaults */
binlog_flags= NBT_DEFAULT;
conflict_fn_spec= NULL;
warning_msg= NULL;
ndb->setDatabaseName(ndb_rep_db);
NdbDictionary::Dictionary *dict= ndb->getDictionary();
Ndb_table_guard ndbtab_g(dict, ndb_replication_table);
const NdbDictionary::Table *reptab= ndbtab_g.get_table();
do
{
if (reptab == NULL)
{
if (dict->getNdbError().classification == NdbError::SchemaError ||
dict->getNdbError().code == 4009)
{
DBUG_PRINT("info", ("No %s.%s table", ndb_rep_db, ndb_replication_table));
DBUG_RETURN(0);
}
else
{
error= 0;
ndberror= dict->getNdbError();
break;
}
}
if ((error= check_schema(reptab,
dict,
&error_str)) != 0)
{
DBUG_PRINT("info", ("check_schema failed : %u, error_str : %s",
error, error_str));
break;
}
Ndb_rep_tab_row best_match_row;
int best_match_quality = scan_candidates(ndb,
reptab,
db,
table_name,
server_id,
best_match_row);
DBUG_PRINT("info", ("Best match at quality : %u", best_match_quality));
if (best_match_quality == -1)
{
/* Problem in matching, message already set */
assert(warning_msg != NULL);
error= -3;
break;
}
if (best_match_quality == 0)
{
/* No match : Use defaults */
}
else
{
/* Have a matching row, copy out values */
/* Ensure VARCHARs are usable as strings */
best_match_row.null_terminate_strings();
binlog_flags= (enum Ndb_binlog_type) best_match_row.binlog_type;
if (best_match_row.cfs_is_null)
{
DBUG_PRINT("info", ("Conflict FN SPEC is Null"));
/* No conflict fn spec */
conflict_fn_spec= NULL;
}
else
{
const char* conflict_fn = best_match_row.get_conflict_fn_spec();
uint len= (uint) strlen(conflict_fn);
if ((len + 1) > sizeof(conflict_fn_buffer))
{
error= -2;
error_str= "Conflict function specification too long.";
break;
}
memcpy(conflict_fn_buffer, conflict_fn, len);
conflict_fn_buffer[len] = '\0';
conflict_fn_spec = conflict_fn_buffer;
}
}
} while(0);
/* Error handling */
if (error == 0)
{
if (ndberror.code != 0)
{
my_snprintf(warning_msg_buffer, sizeof(warning_msg_buffer),
"Unable to retrieve %s.%s, logging and "
"conflict resolution may not function "
"as intended (ndberror %u)",
ndb_rep_db, ndb_replication_table,
ndberror.code);
warning_msg= warning_msg_buffer;
error= -4;
}
}
else
{
switch (error)
{
case -1:
my_snprintf(warning_msg_buffer, sizeof(warning_msg_buffer),
"Missing or wrong type for column '%s'", error_str);
break;
case -2:
my_snprintf(warning_msg_buffer, sizeof(warning_msg_buffer), "%s", error_str);
break;
case -3:
/* Message already set */
break;
default:
abort();
}
warning_msg= warning_msg_buffer;
error= 0; /* No real error, just use defaults */
}
DBUG_PRINT("info", ("Rc : %d Retrieved Binlog flags : %u and function spec : %s",
error, binlog_flags, (conflict_fn_spec != NULL ?conflict_fn_spec:
"NULL")));
DBUG_RETURN(error);
};
Uint32
Ndb_rep_tab_reader::get_binlog_flags() const
{
return binlog_flags;
}
const char*
Ndb_rep_tab_reader::get_conflict_fn_spec() const
{
return conflict_fn_spec;
}
const char*
Ndb_rep_tab_reader::get_warning_message() const
{
return warning_msg;
}
/* #ifdef HAVE_NDB_BINLOG */
#endif