372 lines
14 KiB
C++
372 lines
14 KiB
C++
/*
|
|
Copyright (c) 2018, 2019, 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, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, 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
|
|
*/
|
|
|
|
// Implements the interface defined in
|
|
#include "storage/ndb/plugin/ndb_schema_dist_table.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "storage/ndb/plugin/ndb_thd_ndb.h"
|
|
|
|
const std::string Ndb_schema_dist_table::DB_NAME = "mysql";
|
|
const std::string Ndb_schema_dist_table::TABLE_NAME = "ndb_schema";
|
|
|
|
const char *Ndb_schema_dist_table::COL_DB = "db";
|
|
const char *Ndb_schema_dist_table::COL_NAME = "name";
|
|
const char *Ndb_schema_dist_table::COL_QUERY = "query";
|
|
const char *Ndb_schema_dist_table::COL_ID = "id";
|
|
const char *Ndb_schema_dist_table::COL_VERSION = "version";
|
|
static const char *COL_SLOCK = "slock";
|
|
static const char *COL_NODEID = "node_id";
|
|
static const char *COL_EPOCH = "epoch";
|
|
static const char *COL_TYPE = "type";
|
|
static const char *COL_SCHEMA_OP_ID = "schema_op_id";
|
|
|
|
// Length of the schema object identifiers which can be distributed by the
|
|
// ndb_schema table. The legacy limit of 63 was increased in 8.0.18 to allow for
|
|
// "any" identifier to be distributed. NOTE! Code still supports working with a
|
|
// ndb_schema table using the legacy length, warning will be printed suggesting
|
|
// upgrade
|
|
static constexpr int IDENTIFIER_LENGTH = 255;
|
|
static constexpr int LEGACY_IDENTIFIER_LENGTH = 63;
|
|
|
|
Ndb_schema_dist_table::Ndb_schema_dist_table(Thd_ndb *thd_ndb)
|
|
: Ndb_util_table(thd_ndb, DB_NAME, TABLE_NAME, true) {}
|
|
|
|
Ndb_schema_dist_table::~Ndb_schema_dist_table() {}
|
|
|
|
bool Ndb_schema_dist_table::check_schema() const {
|
|
// db
|
|
// varbinary, at least 63 bytes long
|
|
// NOTE! The 63 bytes length for the db and name column is a legacy bug
|
|
// which doesn't have enough room for MySQL's max identitfier size. For
|
|
// backwards compatiblity reasons it's allowed to use such a schema
|
|
// distribution table but not all identifiers will be possible to distribute.
|
|
if (!(check_column_exist(COL_DB) && check_column_varbinary(COL_DB) &&
|
|
check_column_minlength(COL_DB, LEGACY_IDENTIFIER_LENGTH))) {
|
|
return false;
|
|
}
|
|
|
|
// name
|
|
// varbinary, at least 63 bytes long
|
|
if (!(check_column_exist(COL_NAME) && check_column_varbinary(COL_NAME) &&
|
|
check_column_minlength(COL_NAME, LEGACY_IDENTIFIER_LENGTH))) {
|
|
return false;
|
|
}
|
|
// Check that db + name is the primary key, otherwise pk operations
|
|
// using that key won't work
|
|
if (!check_primary_key({COL_DB, COL_NAME})) {
|
|
return false;
|
|
}
|
|
|
|
// slock
|
|
// binary, need room for at least 32 bytes(i.e 32*8 bits for 256 nodes)
|
|
if (!(check_column_exist(COL_SLOCK) && check_column_binary(COL_SLOCK) &&
|
|
check_column_minlength(COL_SLOCK, 32))) {
|
|
return false;
|
|
}
|
|
|
|
// query
|
|
// blob
|
|
if (!(check_column_exist(COL_QUERY) && check_column_blob(COL_QUERY))) {
|
|
return false;
|
|
}
|
|
|
|
// nodeid
|
|
// unsigned int
|
|
if (!(check_column_exist(COL_NODEID) && check_column_unsigned(COL_NODEID))) {
|
|
return false;
|
|
}
|
|
|
|
// epoch
|
|
// unsigned bigint
|
|
if (!(check_column_exist(COL_EPOCH) && check_column_bigunsigned(COL_EPOCH))) {
|
|
return false;
|
|
}
|
|
|
|
// id
|
|
// unsigned int
|
|
if (!(check_column_exist(COL_ID) && check_column_unsigned(COL_ID))) {
|
|
return false;
|
|
}
|
|
|
|
// version
|
|
// unsigned int
|
|
if (!(check_column_exist(COL_VERSION) &&
|
|
check_column_unsigned(COL_VERSION))) {
|
|
return false;
|
|
}
|
|
|
|
// type
|
|
// unsigned int
|
|
if (!(check_column_exist(COL_TYPE) && check_column_unsigned(COL_TYPE))) {
|
|
return false;
|
|
}
|
|
|
|
// schema_op_id
|
|
// unsigned int
|
|
if (!check_column_exist(COL_SCHEMA_OP_ID)) {
|
|
// This is an optional column added in 8.0.17. Functionality depending on
|
|
// this column will be conditional but warnings are pushed to alert the user
|
|
// that upgrade is eventually necessary
|
|
;
|
|
} else {
|
|
// Column exists, check proper type
|
|
if (!(check_column_unsigned(COL_SCHEMA_OP_ID) &&
|
|
check_column_nullable(COL_SCHEMA_OP_ID, true))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Ndb_schema_dist_table::check_column_identifier_limit(
|
|
const char *column_name, const std::string &identifier) const {
|
|
DBUG_TRACE;
|
|
DBUG_PRINT("enter", ("column_name: '%s', identifier: '%s'", column_name,
|
|
identifier.c_str()));
|
|
|
|
if (!check_column_exist(column_name)) {
|
|
return false;
|
|
}
|
|
|
|
const int max_length = DBUG_EVALUATE_IF("ndb_schema_dist_63byte_limit", 63,
|
|
get_column_max_length(column_name));
|
|
|
|
if (identifier.length() > static_cast<size_t>(max_length)) {
|
|
push_warning("Identifier length exceeds the %d byte limit", max_length);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Ndb_schema_dist_table::define_table_ndb(NdbDictionary::Table &new_table,
|
|
unsigned mysql_version) const {
|
|
// Set metadata for backwards compatibility support, earlier versions
|
|
// will see what they expect and can connect to NDB properly. The physical
|
|
// table in NDB may be extended to support new functionality but should still
|
|
// be possible to use.
|
|
constexpr uint8 legacy_metadata[418] = {
|
|
0x01, 0x00, 0x00, 0x00, 0x6a, 0x22, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00,
|
|
0x78, 0x9c, 0xed, 0xd8, 0x3d, 0x4b, 0xc3, 0x50, 0x14, 0x06, 0xe0, 0x37,
|
|
0x89, 0x89, 0x37, 0xb1, 0xd4, 0x0f, 0x82, 0x83, 0xd3, 0x75, 0x10, 0xb4,
|
|
0x83, 0x6d, 0x45, 0xdd, 0xa4, 0xa6, 0x28, 0x5a, 0xfc, 0x2a, 0xa5, 0x83,
|
|
0x9d, 0xc4, 0x36, 0x01, 0xeb, 0x47, 0xab, 0xad, 0x0a, 0x0e, 0x4a, 0xfd,
|
|
0x29, 0xce, 0x0e, 0x8e, 0x0e, 0x0e, 0x42, 0x07, 0x7f, 0x88, 0xbf, 0x43,
|
|
0x7a, 0x3d, 0x89, 0x55, 0x3a, 0xba, 0x45, 0xf0, 0x3c, 0x4b, 0xce, 0x79,
|
|
0x39, 0xe1, 0xde, 0x33, 0x26, 0x3d, 0xcd, 0x49, 0x1a, 0xc0, 0x98, 0x06,
|
|
0x64, 0x80, 0xba, 0xd6, 0xc5, 0x0f, 0x3d, 0x05, 0x1b, 0x30, 0xc3, 0x52,
|
|
0x7c, 0x67, 0x75, 0x9a, 0x9b, 0x79, 0x03, 0xf6, 0xa3, 0x2e, 0x09, 0xa4,
|
|
0xd3, 0x80, 0x04, 0x63, 0x8c, 0x31, 0xc6, 0x18, 0x63, 0x8c, 0x31, 0xc6,
|
|
0xfe, 0x32, 0x4d, 0x07, 0x1c, 0x7a, 0xde, 0x41, 0x37, 0xa8, 0xeb, 0xd0,
|
|
0xf7, 0xbd, 0xb6, 0x9a, 0x83, 0xde, 0xf1, 0xbe, 0x0a, 0x55, 0x2c, 0x15,
|
|
0x76, 0xbc, 0x52, 0x45, 0xc5, 0x7d, 0x51, 0x16, 0x3f, 0x07, 0x0d, 0xbf,
|
|
0x5a, 0x3b, 0xbd, 0x6a, 0x5f, 0x06, 0xad, 0x79, 0xea, 0x65, 0xd1, 0x2b,
|
|
0x95, 0x0b, 0xe5, 0xc2, 0xde, 0xae, 0xcc, 0x57, 0xe4, 0xd6, 0x7a, 0x45,
|
|
0xa6, 0x53, 0xd3, 0x4b, 0x99, 0xe5, 0x6c, 0x56, 0x7a, 0xdb, 0x1b, 0x7b,
|
|
0xa5, 0x42, 0x79, 0x73, 0x47, 0xae, 0xc8, 0x05, 0x99, 0x4a, 0xcb, 0xd9,
|
|
0x39, 0x68, 0x13, 0x71, 0x2f, 0xc0, 0x18, 0x63, 0x8c, 0x31, 0xc6, 0x18,
|
|
0x63, 0xff, 0xd7, 0xb1, 0x8e, 0xb1, 0xb8, 0xef, 0x10, 0x27, 0x0d, 0x36,
|
|
0x6e, 0xf1, 0x4e, 0x55, 0x17, 0x8b, 0x3f, 0x69, 0x11, 0x93, 0xfd, 0xea,
|
|
0x16, 0x8e, 0xad, 0xbb, 0x73, 0xf2, 0x97, 0x30, 0x04, 0xc3, 0xaf, 0xc2,
|
|
0x84, 0xd9, 0x38, 0x3c, 0x0b, 0x60, 0xc1, 0x6a, 0x9f, 0x36, 0x6b, 0x27,
|
|
0x18, 0x86, 0x75, 0x71, 0x15, 0xb4, 0x6e, 0x20, 0x20, 0x1a, 0x4d, 0x3f,
|
|
0x38, 0xa8, 0xfb, 0x74, 0xb0, 0x15, 0x9c, 0x37, 0x6b, 0x47, 0xf4, 0x59,
|
|
0x6d, 0x50, 0x3b, 0x02, 0x71, 0x1d, 0xb4, 0xda, 0xf5, 0x66, 0x03, 0x09,
|
|
0x98, 0x97, 0x37, 0xe7, 0x01, 0x86, 0x8c, 0x5c, 0x0e, 0xd1, 0x1f, 0x19,
|
|
0xba, 0xc9, 0x68, 0x0e, 0x30, 0x4d, 0x0a, 0xbc, 0x81, 0xc0, 0xb2, 0xe8,
|
|
0xcc, 0xfb, 0x7e, 0xd0, 0xa3, 0x60, 0xd8, 0x12, 0x02, 0x0f, 0xc0, 0xf8,
|
|
0x9a, 0x0b, 0x7c, 0x50, 0x20, 0x84, 0xe3, 0xe0, 0x11, 0x98, 0x0a, 0x27,
|
|
0x0c, 0x01, 0xd8, 0x96, 0xeb, 0xe2, 0x09, 0xc8, 0x87, 0x01, 0x0d, 0xc3,
|
|
0x31, 0x68, 0xe2, 0x79, 0x60, 0x62, 0x24, 0x7c, 0xe5, 0x65, 0x20, 0x48,
|
|
0x98, 0x14, 0xbc, 0x0e, 0x04, 0xca, 0xaf, 0xaa, 0x70, 0x41, 0x15, 0x6d,
|
|
0xa7, 0xa2, 0xd5, 0x54, 0x7f, 0x2f, 0x15, 0x2d, 0xa5, 0xa8, 0xe8, 0xaf,
|
|
0xa3, 0xc2, 0x5d, 0x14, 0x3e, 0x01, 0x4d, 0x53, 0x5e, 0x81};
|
|
if (new_table.setFrm(legacy_metadata, sizeof(legacy_metadata)) != 0) {
|
|
push_warning("Failed to set legacy metadata");
|
|
return false;
|
|
}
|
|
|
|
new_table.setForceVarPart(true);
|
|
|
|
// Allow table to be read+write also in single user mode
|
|
new_table.setSingleUserMode(NdbDictionary::Table::SingleUserModeReadWrite);
|
|
|
|
// The length of "db" and "name" was adjusted in 8.0.18 to allow
|
|
// passing 255 bytes long identifiers
|
|
int db_and_name_length = IDENTIFIER_LENGTH;
|
|
if (mysql_version < 80018) {
|
|
// Use legacy identifier length when creating the table for
|
|
// backwards compatibility testing
|
|
db_and_name_length = LEGACY_IDENTIFIER_LENGTH;
|
|
}
|
|
|
|
{
|
|
// db VARBINARY(255) NOT NULL
|
|
NdbDictionary::Column col_db(COL_DB);
|
|
col_db.setType(NdbDictionary::Column::Varbinary);
|
|
col_db.setLength(db_and_name_length);
|
|
col_db.setNullable(false);
|
|
col_db.setPrimaryKey(true);
|
|
if (!define_table_add_column(new_table, col_db)) return false;
|
|
}
|
|
|
|
{
|
|
// name VARBINARY(255) NOT NULL
|
|
NdbDictionary::Column col_name(COL_NAME);
|
|
col_name.setType(NdbDictionary::Column::Varbinary);
|
|
col_name.setLength(db_and_name_length);
|
|
col_name.setNullable(false);
|
|
col_name.setPrimaryKey(true);
|
|
if (!define_table_add_column(new_table, col_name)) return false;
|
|
}
|
|
|
|
{
|
|
// slock BINARY(32) NOT NULL
|
|
NdbDictionary::Column col_slock(COL_SLOCK);
|
|
col_slock.setType(NdbDictionary::Column::Binary);
|
|
col_slock.setLength(32);
|
|
col_slock.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_slock)) return false;
|
|
}
|
|
|
|
{
|
|
// query BLOB NOT NULL
|
|
NdbDictionary::Column col_query(COL_QUERY);
|
|
col_query.setType(NdbDictionary::Column::Blob);
|
|
col_query.setInlineSize(256);
|
|
col_query.setPartSize(2000);
|
|
col_query.setStripeSize(0);
|
|
col_query.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_query)) return false;
|
|
}
|
|
|
|
{
|
|
// node_id INT UNSIGNED NOT NULL
|
|
NdbDictionary::Column col_nodeid(COL_NODEID);
|
|
col_nodeid.setType(NdbDictionary::Column::Unsigned);
|
|
col_nodeid.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_nodeid)) return false;
|
|
}
|
|
|
|
{
|
|
// epoch BIGINT UNSIGNED NOT NULL
|
|
NdbDictionary::Column col_epoch(COL_EPOCH);
|
|
col_epoch.setType(NdbDictionary::Column::Bigunsigned);
|
|
col_epoch.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_epoch)) return false;
|
|
}
|
|
|
|
{
|
|
// id INT UNSIGNED NOT NULL
|
|
NdbDictionary::Column col_id(COL_ID);
|
|
col_id.setType(NdbDictionary::Column::Unsigned);
|
|
col_id.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_id)) return false;
|
|
}
|
|
|
|
{
|
|
// version INT UNSIGNED NOT NULL
|
|
NdbDictionary::Column col_version(COL_VERSION);
|
|
col_version.setType(NdbDictionary::Column::Unsigned);
|
|
col_version.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_version)) return false;
|
|
}
|
|
|
|
{
|
|
// type INT UNSIGNED NOT NULL
|
|
NdbDictionary::Column col_type(COL_TYPE);
|
|
col_type.setType(NdbDictionary::Column::Unsigned);
|
|
col_type.setNullable(false);
|
|
if (!define_table_add_column(new_table, col_type)) return false;
|
|
}
|
|
|
|
if (mysql_version >= 80017) {
|
|
// schema_op_id INT UNSIGNED NULL
|
|
NdbDictionary::Column col_schema_op_id(COL_SCHEMA_OP_ID);
|
|
col_schema_op_id.setType(NdbDictionary::Column::Unsigned);
|
|
col_schema_op_id.setNullable(true); // NULL!
|
|
if (!define_table_add_column(new_table, col_schema_op_id)) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Ndb_schema_dist_table::need_upgrade() const {
|
|
// Check that 'schema_op_id' column exists. If exists, it's used for sending
|
|
// the schema_op_id from client to participants who can then use it when
|
|
// replying using ndb_schema_result (if they support that table)
|
|
if (!have_schema_op_id_column()) {
|
|
return true;
|
|
}
|
|
// The 'db' and 'name' column need to be upgrade if length is shorter than
|
|
// current identifier length
|
|
if (get_column_max_length(COL_DB) < IDENTIFIER_LENGTH ||
|
|
get_column_max_length(COL_NAME) < IDENTIFIER_LENGTH) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Ndb_schema_dist_table::drop_events_in_NDB() const {
|
|
// Drop the default event on ndb_schema table
|
|
if (!drop_event_in_NDB("REPL$mysql/ndb_schema")) return false;
|
|
|
|
// Legacy event on ndb_schema table, drop since it might
|
|
// have been created(although ages ago)
|
|
if (!drop_event_in_NDB("REPLF$mysql/ndb_schema")) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string Ndb_schema_dist_table::define_table_dd() const {
|
|
std::stringstream ss;
|
|
ss << "CREATE TABLE " << db_name() << "." << table_name() << "(\n";
|
|
ss << "db VARBINARY(" << get_column_max_length(COL_DB) << ") NOT NULL,";
|
|
ss << "name VARBINARY(" << get_column_max_length(COL_NAME) << ") NOT NULL,";
|
|
ss << "slock BINARY(32) NOT NULL,"
|
|
"query BLOB NOT NULL,"
|
|
"node_id INT UNSIGNED NOT NULL,"
|
|
"epoch BIGINT UNSIGNED NOT NULL,"
|
|
"id INT UNSIGNED NOT NULL,"
|
|
"version INT UNSIGNED NOT NULL,"
|
|
"type INT UNSIGNED NOT NULL,";
|
|
if (have_schema_op_id_column()) {
|
|
ss << "schema_op_id INT UNSIGNED NULL,";
|
|
}
|
|
ss << "PRIMARY KEY USING HASH (db,name)"
|
|
<< ") ENGINE=ndbcluster CHARACTER SET latin1";
|
|
return ss.str();
|
|
}
|
|
|
|
int Ndb_schema_dist_table::get_slock_bytes() const {
|
|
return get_column_max_length(COL_SLOCK);
|
|
}
|
|
|
|
bool Ndb_schema_dist_table::have_schema_op_id_column() const {
|
|
const NdbDictionary::Table *ndb_tab = get_table();
|
|
return ndb_tab->getColumn(COL_SCHEMA_OP_ID);
|
|
}
|