/* Copyright (c) 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 #include "storage/ndb/plugin/ndb_metadata_sync.h" #include "sql/sql_class.h" // THD #include "sql/sql_table.h" // build_table_filename #include "storage/ndb/include/ndbapi/Ndb.hpp" // Ndb #include "storage/ndb/plugin/ha_ndbcluster_binlog.h" // ndbcluster_binlog_setup_table #include "storage/ndb/plugin/ndb_dd_client.h" // Ndb_dd_client #include "storage/ndb/plugin/ndb_dd_disk_data.h" // ndb_dd_disk_data_get_object_id_and_version #include "storage/ndb/plugin/ndb_dd_table.h" // ndb_dd_table_get_object_id_and_version #include "storage/ndb/plugin/ndb_log.h" // ndb_log_* #include "storage/ndb/plugin/ndb_ndbapi_util.h" // ndb_logfile_group_exists #include "storage/ndb/plugin/ndb_schema_dist.h" // Ndb_schema_dist #include "storage/ndb/plugin/ndb_table_guard.h" // Ndb_table_guard #include "storage/ndb/plugin/ndb_tdc.h" // ndb_tdc_close_cached_table #include "storage/ndb/plugin/ndb_thd.h" // get_thd_ndb #include "storage/ndb/plugin/ndb_thd_ndb.h" // Thd_ndb const char *Ndb_metadata_sync::object_type_str( object_detected_type type) const { switch (type) { case LOGFILE_GROUP_OBJECT: return "LOGFILE GROUP"; case TABLESPACE_OBJECT: return "TABLESPACE"; case TABLE_OBJECT: return "TABLE"; default: DBUG_ASSERT(false); return ""; } } bool Ndb_metadata_sync::object_sync_pending( const Detected_object &object) const { for (const auto &detected_object : m_objects) { if (detected_object.m_type == object.m_type && detected_object.m_db_name == object.m_db_name && detected_object.m_name == object.m_name) { if (object.m_type == object_detected_type::TABLE_OBJECT) { ndb_log_info( "Object '%s.%s' of type %s is already in the queue of " "objects waiting to be synchronized", object.m_db_name.c_str(), object.m_name.c_str(), object_type_str(object.m_type)); } else { ndb_log_info( "Object '%s' of type %s is already in the queue of objects" " waiting to be synchronized", object.m_name.c_str(), object_type_str(object.m_type)); } return true; } } return false; } bool Ndb_metadata_sync::object_blacklisted( const Detected_object &object) const { std::lock_guard guard(m_blacklist_mutex); for (const auto &blacklisted_object : m_blacklist) { if (blacklisted_object.m_type == object.m_type && blacklisted_object.m_db_name == object.m_db_name && blacklisted_object.m_name == object.m_name) { if (object.m_type == object_detected_type::TABLE_OBJECT) { ndb_log_info( "Object '%s.%s' of type %s is currently blacklisted and needs to " "be synced manually", object.m_db_name.c_str(), object.m_name.c_str(), object_type_str(object.m_type)); } else { ndb_log_info( "Object '%s' of type %s is currently blacklisted and needs to be " "synced manually", object.m_name.c_str(), object_type_str(object.m_type)); } return true; } } return false; } bool Ndb_metadata_sync::add_logfile_group(const std::string &lfg_name) { std::lock_guard guard(m_objects_mutex); const Detected_object obj("", lfg_name, object_detected_type::LOGFILE_GROUP_OBJECT); if (object_sync_pending(obj) || object_blacklisted(obj)) { return false; } m_objects.emplace_back(obj); ndb_log_info( "Logfile group '%s' added to queue of objects waiting to be " "synchronized", lfg_name.c_str()); return true; } bool Ndb_metadata_sync::add_tablespace(const std::string &tablespace_name) { std::lock_guard guard(m_objects_mutex); const Detected_object obj("", tablespace_name, object_detected_type::TABLESPACE_OBJECT); if (object_sync_pending(obj) || object_blacklisted(obj)) { return false; } m_objects.emplace_back(obj); ndb_log_info( "Tablespace '%s' added to queue of objects waiting to be " "synchronized", tablespace_name.c_str()); return true; } bool Ndb_metadata_sync::add_table(const std::string &schema_name, const std::string &table_name) { std::lock_guard guard(m_objects_mutex); const Detected_object obj(schema_name, table_name, object_detected_type::TABLE_OBJECT); if (object_sync_pending(obj) || object_blacklisted(obj)) { return false; } m_objects.emplace_back(obj); ndb_log_info( "Table '%s.%s' added to queue of objects waiting to be " "synchronized", schema_name.c_str(), table_name.c_str()); return true; } bool Ndb_metadata_sync::object_queue_empty() const { std::lock_guard guard(m_objects_mutex); return m_objects.empty(); } void Ndb_metadata_sync::get_next_object(std::string &db_name, std::string &name, object_detected_type &type) { std::lock_guard guard(m_objects_mutex); const Detected_object obj = m_objects.front(); db_name = obj.m_db_name; name = obj.m_name; type = obj.m_type; m_objects.pop_front(); } static long long g_blacklist_size = 0; // protected implicitly by m_blacklist_mutex static void increment_blacklist_size() { g_blacklist_size++; } static void decrement_blacklist_size() { g_blacklist_size--; } static SHOW_VAR ndb_status_vars_blacklist_size[] = { {"metadata_blacklist_size", reinterpret_cast(&g_blacklist_size), SHOW_LONGLONG, SHOW_SCOPE_GLOBAL}, {NullS, NullS, SHOW_LONG, SHOW_SCOPE_GLOBAL}}; int show_ndb_metadata_blacklist_size(THD *, SHOW_VAR *var, char *) { var->type = SHOW_ARRAY; var->value = reinterpret_cast(&ndb_status_vars_blacklist_size); return 0; } void Ndb_metadata_sync::add_object_to_blacklist(const std::string &db_name, const std::string &name, object_detected_type type) { std::lock_guard guard(m_blacklist_mutex); const Detected_object obj(db_name, name, type); m_blacklist.emplace_back(obj); ndb_log_info("Object '%s' of type %s added to blacklist", name.c_str(), object_type_str(type)); increment_blacklist_size(); } bool Ndb_metadata_sync::get_blacklist_object_for_validation( std::string &db_name, std::string &name, object_detected_type &type) { std::lock_guard guard(m_blacklist_mutex); for (Detected_object &obj : m_blacklist) { switch (obj.m_validation_state) { case object_validation_state::PENDING: { // Found object pending validation. Retrieve details and mark the object // as being validated db_name = obj.m_db_name; name = obj.m_name; type = obj.m_type; obj.m_validation_state = object_validation_state::IN_PROGRESS; return true; } break; case object_validation_state::DONE: { } break; case object_validation_state::IN_PROGRESS: { // Not possible since there can't be two objects being validated at once DBUG_ASSERT(false); return false; } break; default: // Unknown state, not possible DBUG_ASSERT(false); return false; } } // Reached the end of the blacklist having found no objects pending validation return false; } bool Ndb_metadata_sync::check_blacklist_object_mismatch( THD *thd, const std::string &db_name, const std::string &name, object_detected_type type) const { Thd_ndb *thd_ndb = get_thd_ndb(thd); Ndb *ndb = thd_ndb->ndb; NdbDictionary::Dictionary *dict = ndb->getDictionary(); Ndb_dd_client dd_client(thd); switch (type) { case object_detected_type::LOGFILE_GROUP_OBJECT: { bool exists_in_NDB; if (!ndb_logfile_group_exists(dict, name, exists_in_NDB)) { ndb_log_info( "Failed to determine if logfile group '%s' exists " "in NDB, object remains blacklisted", name.c_str()); return true; } if (!dd_client.mdl_lock_logfile_group(name.c_str(), true)) { ndb_log_info( "Failed to acquire MDL on logfile group '%s', object " "remains blacklisted", name.c_str()); return true; } bool exists_in_DD; if (!dd_client.logfile_group_exists(name.c_str(), exists_in_DD)) { ndb_log_info( "Failed to determine if logfile group '%s' exists in DD, " "object remains blacklisted", name.c_str()); return true; } if (exists_in_NDB == exists_in_DD) { ndb_log_info( "Mismatch doesn't exist any more, logfile group '%s' " "will be removed from blacklist", name.c_str()); return false; } else { ndb_log_info( "Mismatch still exists, logfile group '%s' remains " "blacklisted", name.c_str()); return true; } } break; case object_detected_type::TABLESPACE_OBJECT: { bool exists_in_NDB; if (!ndb_tablespace_exists(dict, name, exists_in_NDB)) { ndb_log_info( "Failed to determine if tablespace '%s' exists in NDB, " "object remains blacklisted", name.c_str()); return true; } if (!dd_client.mdl_lock_tablespace(name.c_str(), true)) { ndb_log_info( "Failed to acquire MDL on tablespace '%s', object " "remains blacklisted", name.c_str()); return true; } bool exists_in_DD; if (!dd_client.tablespace_exists(name.c_str(), exists_in_DD)) { ndb_log_info( "Failed to determine if tablespace '%s' exists in DD, " "object remains blacklisted", name.c_str()); return true; } if (exists_in_NDB == exists_in_DD) { ndb_log_info( "Mismatch doesn't exist any more, tablespace '%s' " "will be removed from blacklist", name.c_str()); return false; } else { ndb_log_info( "Mismatch still exists, tablespace '%s' remains " "blacklisted", name.c_str()); return true; } } break; case object_detected_type::TABLE_OBJECT: { bool exists_in_NDB; if (!ndb_table_exists(dict, db_name, name, exists_in_NDB)) { ndb_log_info( "Failed to determine if table '%s.%s' exists in NDB, " "object remains blacklisted", db_name.c_str(), name.c_str()); return true; } if (!dd_client.mdl_lock_table(db_name.c_str(), name.c_str())) { ndb_log_info( "Failed to acquire MDL on table '%s.%s', object " "remains blacklisted", db_name.c_str(), name.c_str()); return true; } bool exists_in_DD; if (!dd_client.table_exists(db_name.c_str(), name.c_str(), exists_in_DD)) { ndb_log_info( "Failed to determine if table '%s.%s' exists in DD, " "object remains blacklisted", db_name.c_str(), name.c_str()); return true; } if (exists_in_NDB == exists_in_DD) { ndb_log_info( "Mismatch doesn't exist any more, table '%s.%s' will be removed " "from blacklist", db_name.c_str(), name.c_str()); return false; } else { ndb_log_info( "Mismatch still exists, table '%s.%s' remains " "blacklisted", db_name.c_str(), name.c_str()); return true; } } break; default: ndb_log_error("Unknown object type found in blacklist"); DBUG_ASSERT(false); } return true; } void Ndb_metadata_sync::validate_blacklist_object(bool check_mismatch_result) { std::lock_guard guard(m_blacklist_mutex); for (auto it = m_blacklist.begin(); it != m_blacklist.end(); it++) { Detected_object &obj = *it; if (obj.m_validation_state == object_validation_state::IN_PROGRESS) { if (!check_mismatch_result) { // Mismatch no longer exists, remove object from blacklist m_blacklist.erase(it); decrement_blacklist_size(); } else { // Mark object as already validated for this cycle obj.m_validation_state = object_validation_state::DONE; } return; } } DBUG_ASSERT(false); } void Ndb_metadata_sync::reset_blacklist_state() { std::lock_guard guard(m_blacklist_mutex); for (Detected_object &obj : m_blacklist) { obj.m_validation_state = object_validation_state::PENDING; } } void Ndb_metadata_sync::validate_blacklist(THD *thd) { while (true) { std::string db_name, name; object_detected_type type; if (!get_blacklist_object_for_validation(db_name, name, type)) { // No more objects pending validation break; } const bool check_mismatch_result = check_blacklist_object_mismatch(thd, db_name, name, type); validate_blacklist_object(check_mismatch_result); } // Reset the states of all blacklisted objects reset_blacklist_state(); } bool Ndb_metadata_sync::sync_logfile_group(THD *thd, const std::string &lfg_name, bool &temp_error) const { Ndb_dd_client dd_client(thd); if (!dd_client.mdl_lock_logfile_group_exclusive(lfg_name.c_str(), true, 10)) { ndb_log_info("Failed to acquire MDL on logfile group '%s'", lfg_name.c_str()); temp_error = true; // Since it's a temporary error, the THD conditions should be cleared but // not logged clear_thd_conditions(thd); return false; } ndb_log_info("Synchronizing logfile group '%s'", lfg_name.c_str()); // Errors detected in the remainder of the function are not temporary temp_error = false; Thd_ndb *thd_ndb = get_thd_ndb(thd); NdbDictionary::Dictionary *dict = thd_ndb->ndb->getDictionary(); bool exists_in_NDB; if (!ndb_logfile_group_exists(dict, lfg_name, exists_in_NDB)) { ndb_log_warning("Failed to determine if logfile group '%s' exists in NDB", lfg_name.c_str()); return false; } bool exists_in_DD; if (!dd_client.logfile_group_exists(lfg_name.c_str(), exists_in_DD)) { log_and_clear_thd_conditions(thd, condition_logging_level::WARNING); ndb_log_warning("Failed to determine if logfile group '%s' exists in DD", lfg_name.c_str()); return false; } if (exists_in_NDB == exists_in_DD) { // Mismatch doesn't exist any more, return success return true; } if (exists_in_DD) { // Logfile group exists in DD but not in NDB. Correct this by removing the // logfile group from DD if (!dd_client.drop_logfile_group(lfg_name.c_str())) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to drop logfile group '%s' in DD", lfg_name.c_str()); return false; } dd_client.commit(); ndb_log_info("Logfile group '%s' dropped from DD", lfg_name.c_str()); return true; } // Logfile group exists in NDB but not in DD. Correct this by installing the // logfile group in the DD std::vector undofile_names; if (!ndb_get_undofile_names(dict, lfg_name, &undofile_names)) { ndb_log_error("Failed to get undofiles assigned to logfile group '%s'", lfg_name.c_str()); return false; } int ndb_id, ndb_version; if (!ndb_get_logfile_group_id_and_version(dict, lfg_name, ndb_id, ndb_version)) { ndb_log_error("Failed to get id and version of logfile group '%s'", lfg_name.c_str()); return false; } if (!dd_client.install_logfile_group(lfg_name.c_str(), undofile_names, ndb_id, ndb_version, false /* force_overwrite */)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to install logfile group '%s' in DD", lfg_name.c_str()); return false; } dd_client.commit(); ndb_log_info("Logfile group '%s' installed in DD", lfg_name.c_str()); return true; } bool Ndb_metadata_sync::sync_tablespace(THD *thd, const std::string &ts_name, bool &temp_error) const { Ndb_dd_client dd_client(thd); if (!dd_client.mdl_lock_tablespace_exclusive(ts_name.c_str(), true, 10)) { ndb_log_info("Failed to acquire MDL on tablespace '%s'", ts_name.c_str()); temp_error = true; // Since it's a temporary error, the THD conditions should be cleared but // not logged clear_thd_conditions(thd); return false; } ndb_log_info("Synchronizing tablespace '%s'", ts_name.c_str()); // Errors detected in the remainder of the function are not temporary temp_error = false; Thd_ndb *thd_ndb = get_thd_ndb(thd); NdbDictionary::Dictionary *dict = thd_ndb->ndb->getDictionary(); bool exists_in_NDB; if (!ndb_tablespace_exists(dict, ts_name, exists_in_NDB)) { ndb_log_warning("Failed to determine if tablespace '%s' exists in NDB", ts_name.c_str()); return false; } bool exists_in_DD; if (!dd_client.tablespace_exists(ts_name.c_str(), exists_in_DD)) { log_and_clear_thd_conditions(thd, condition_logging_level::WARNING); ndb_log_warning("Failed to determine if tablespace '%s' exists in DD", ts_name.c_str()); return false; } if (exists_in_NDB == exists_in_DD) { // Mismatch doesn't exist any more, return success return true; } if (exists_in_DD) { // Tablespace exists in DD but not in NDB. Correct this by removing the // tablespace from DD if (!dd_client.drop_tablespace(ts_name.c_str())) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to drop tablespace '%s' in DD", ts_name.c_str()); return false; } dd_client.commit(); ndb_log_info("Tablespace '%s' dropped from DD", ts_name.c_str()); return true; } // Tablespace exists in NDB but not in DD. Correct this by installing the // tablespace in the DD std::vector datafile_names; if (!ndb_get_datafile_names(dict, ts_name, &datafile_names)) { ndb_log_error("Failed to get datafiles assigned to tablespace '%s'", ts_name.c_str()); return false; } int ndb_id, ndb_version; if (!ndb_get_tablespace_id_and_version(dict, ts_name, ndb_id, ndb_version)) { ndb_log_error("Failed to get id and version of tablespace '%s'", ts_name.c_str()); return false; } if (!dd_client.install_tablespace(ts_name.c_str(), datafile_names, ndb_id, ndb_version, false /* force_overwrite */)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to install tablespace '%s' in DD", ts_name.c_str()); return false; } dd_client.commit(); ndb_log_info("Tablespace '%s' installed in DD", ts_name.c_str()); return true; } class Mutex_guard { public: Mutex_guard(mysql_mutex_t &mutex) : m_mutex(mutex) { mysql_mutex_lock(&m_mutex); } Mutex_guard(const Mutex_guard &) = delete; ~Mutex_guard() { mysql_mutex_unlock(&m_mutex); } private: mysql_mutex_t &m_mutex; }; extern mysql_mutex_t ndbcluster_mutex; void Ndb_metadata_sync::drop_ndb_share(const char *db_name, const char *table_name) const { char key[FN_REFLEN + 1]; build_table_filename(key, sizeof(key) - 1, db_name, table_name, "", 0); NDB_SHARE *share = NDB_SHARE::acquire_reference_by_key(key, "table_sync"); // temporary ref if (share) { Mutex_guard ndbcluster_mutex_guard(ndbcluster_mutex); NDB_SHARE::mark_share_dropped(&share); DBUG_ASSERT(share); NDB_SHARE::release_reference_have_lock(share, "table_sync"); // temporary ref } } bool Ndb_metadata_sync::sync_table(THD *thd, const std::string &db_name, const std::string &table_name, bool &temp_error) { if (DBUG_EVALUATE_IF("ndb_metadata_sync_fail", true, false)) { temp_error = false; return false; } Ndb_dd_client dd_client(thd); if (!dd_client.mdl_locks_acquire_exclusive(db_name.c_str(), table_name.c_str(), true, 10)) { ndb_log_info("Failed to acquire MDL on table '%s.%s'", db_name.c_str(), table_name.c_str()); temp_error = true; // Since it's a temporary error, the THD conditions should be cleared but // not logged clear_thd_conditions(thd); return false; } ndb_log_info("Synchronizing table '%s.%s'", db_name.c_str(), table_name.c_str()); // Most of the errors detected after this are not temporary temp_error = false; Thd_ndb *thd_ndb = get_thd_ndb(thd); Ndb *ndb = thd_ndb->ndb; NdbDictionary::Dictionary *dict = ndb->getDictionary(); bool exists_in_NDB; if (!ndb_table_exists(dict, db_name, table_name, exists_in_NDB)) { ndb_log_warning("Failed to determine if table '%s.%s' exists in NDB", db_name.c_str(), table_name.c_str()); return false; } bool exists_in_DD; if (!dd_client.table_exists(db_name.c_str(), table_name.c_str(), exists_in_DD)) { log_and_clear_thd_conditions(thd, condition_logging_level::WARNING); ndb_log_warning("Failed to determine if table '%s.%s' exists in DD", db_name.c_str(), table_name.c_str()); return false; } if (exists_in_NDB == exists_in_DD) { // Mismatch doesn't exist any more, return success return true; } if (exists_in_DD) { // Table exists in DD but not in NDB // Check if it's a local table bool local_table; if (!dd_client.is_local_table(db_name.c_str(), table_name.c_str(), local_table)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to determine if table '%s.%s' was a local table", db_name.c_str(), table_name.c_str()); return false; } if (local_table) { // Local table, the mismatch is expected return true; } // Remove the table from DD Ndb_referenced_tables_invalidator invalidator(thd, dd_client); if (!dd_client.remove_table(db_name.c_str(), table_name.c_str(), &invalidator)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to drop table '%s.%s' in DD", db_name.c_str(), table_name.c_str()); return false; } if (!invalidator.invalidate()) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error( "Failed to invalidate tables referencing table '%s.%s' in " "DD", db_name.c_str(), table_name.c_str()); return false; } // Drop share if it exists drop_ndb_share(db_name.c_str(), table_name.c_str()); ndb_tdc_close_cached_table(thd, db_name.c_str(), table_name.c_str()); // Invalidate the table in NdbApi if (ndb->setDatabaseName(db_name.c_str())) { ndb_log_error("Failed to set database name of NDB object"); return false; } dd_client.commit(); ndb_log_info("Table '%s.%s' dropped from DD", db_name.c_str(), table_name.c_str()); Ndb_table_guard ndbtab_guard(dict, table_name.c_str()); ndbtab_guard.invalidate(); return true; } // Table exists in NDB but not in DD. Correct this by installing the table in // the DD if (ndb->setDatabaseName(db_name.c_str())) { ndb_log_error("Failed to set database name of NDB object"); return false; } Ndb_table_guard ndbtab_guard(dict, table_name.c_str()); const NdbDictionary::Table *tab = ndbtab_guard.get_table(); if (tab == nullptr) { // Mismatch doesn't exist any more, return success return true; } Uint32 extra_metadata_version, unpacked_len; void *unpacked_data; const int get_result = tab->getExtraMetadata(extra_metadata_version, &unpacked_data, &unpacked_len); if (get_result != 0) { ndb_log_info("Failed to get extra metadata of table '%s.%s'", db_name.c_str(), table_name.c_str()); return false; } if (extra_metadata_version == 1) { // Table with "old" metadata found if (!dd_client.migrate_table( db_name.c_str(), table_name.c_str(), static_cast(unpacked_data), unpacked_len, false)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error( "Failed to migrate table '%s.%s' with extra metadata " "version 1", db_name.c_str(), table_name.c_str()); free(unpacked_data); return false; } free(unpacked_data); const dd::Table *dd_table; if (!dd_client.get_table(db_name.c_str(), table_name.c_str(), &dd_table)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error( "Failed to get table '%s.%s' from DD after it was installed", db_name.c_str(), table_name.c_str()); return false; } if (ndbcluster_binlog_setup_table(thd, ndb, db_name.c_str(), table_name.c_str(), dd_table) != 0) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to setup binlogging for table '%s.%s'", db_name.c_str(), table_name.c_str()); return false; } dd_client.commit(); ndb_log_info("Table '%s.%s' installed in DD", db_name.c_str(), table_name.c_str()); return true; } dd::sdi_t sdi; sdi.assign(static_cast(unpacked_data), unpacked_len); free(unpacked_data); const std::string tablespace_name = ndb_table_tablespace_name(dict, tab); if (!tablespace_name.empty()) { // Acquire IX MDL on tablespace if (!dd_client.mdl_lock_tablespace(tablespace_name.c_str(), true)) { ndb_log_info("Failed to acquire MDL on tablespace '%s'", tablespace_name.c_str()); temp_error = true; // Since it's a temporary error, the THD conditions should be cleared but // not logged clear_thd_conditions(thd); return false; } bool tablespace_exists; if (!dd_client.tablespace_exists(tablespace_name.c_str(), tablespace_exists)) { log_and_clear_thd_conditions(thd, condition_logging_level::WARNING); ndb_log_warning("Failed to determine if tablespace '%s' exists in DD", tablespace_name.c_str()); return false; } if (!tablespace_exists) { const Detected_object obj("", tablespace_name, TABLESPACE_OBJECT); if (object_blacklisted(obj)) { // The tablespace was detected but its sync failed. Such errors // shouldn't be treated as temporary errors and the table is added to // the blacklist ndb_log_error("Tablespace '%s' is currently blacklisted", tablespace_name.c_str()); ndb_log_error("Failed to install disk data table '%s.%s'", db_name.c_str(), table_name.c_str()); return false; } else { // There's a possibility (especially when ndb_restore is used) that a // disk data table is being synchronized before the tablespace has been // synchronized which is a temporary error since the next detection // cycle will detect and attempt to sync the tablespace before the table ndb_log_info( "Disk data table '%s.%s' not synced since tablespace '%s' " "hasn't been synced yet", db_name.c_str(), table_name.c_str(), tablespace_name.c_str()); temp_error = true; // Since it's a temporary error, the THD conditions should be cleared // but not logged clear_thd_conditions(thd); return false; } } } Ndb_referenced_tables_invalidator invalidator(thd, dd_client); if (!dd_client.install_table(db_name.c_str(), table_name.c_str(), sdi, tab->getObjectId(), tab->getObjectVersion(), tab->getPartitionCount(), tablespace_name, false, &invalidator)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to install table '%s.%s' in DD", db_name.c_str(), table_name.c_str()); return false; } if (!invalidator.invalidate()) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to invalidate tables referencing table '%s.%s' in DD", db_name.c_str(), table_name.c_str()); return false; } const dd::Table *dd_table; if (!dd_client.get_table(db_name.c_str(), table_name.c_str(), &dd_table)) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to get table '%s.%s' from DD after it was installed", db_name.c_str(), table_name.c_str()); return false; } if (ndbcluster_binlog_setup_table(thd, ndb, db_name.c_str(), table_name.c_str(), dd_table) != 0) { log_and_clear_thd_conditions(thd, condition_logging_level::ERROR); ndb_log_error("Failed to setup binlogging for table '%s.%s'", db_name.c_str(), table_name.c_str()); return false; } dd_client.commit(); ndb_log_info("Table '%s.%s' installed in DD", db_name.c_str(), table_name.c_str()); return true; }