modules/util/dump/instance_cache.cc (959 lines of code) (raw):

/* * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * * 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 designed to work 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 either included with * the program or referenced in the documentation. * * 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 */ #include "modules/util/dump/instance_cache.h" #include <mysqld_error.h> #include <algorithm> #include <iterator> #include <stdexcept> #include <utility> #include "mysqlshdk/include/shellcore/console.h" #include "mysqlshdk/libs/utils/debug.h" #include "mysqlshdk/libs/utils/logger.h" #include "mysqlshdk/libs/utils/profiling.h" #include "mysqlshdk/libs/utils/utils_net.h" #include "mysqlshdk/libs/utils/utils_sqlstring.h" #include "mysqlshdk/libs/utils/utils_string.h" #include "modules/util/dump/dump_errors.h" #include "modules/util/dump/schema_dumper.h" namespace mysqlsh { namespace dump { namespace { // ASCII NUL (U+0000) is not permitted in quoted or unquoted identifiers const std::string k_template_marker{"\0", 1}; const std::string k_schema_var{"s"}; const std::string k_schema_template = k_template_marker + k_schema_var + k_template_marker; const std::string k_table_var{"t"}; const std::string k_table_template = k_template_marker + k_table_var + k_template_marker; class Profiler final { public: Profiler() = delete; explicit Profiler(const char *msg) : m_msg(msg) { log_debug("-> %s", m_msg); m_duration.start(); } Profiler(const Profiler &) = delete; Profiler(Profiler &&) = delete; Profiler &operator=(const Profiler &) = delete; Profiler &operator=(Profiler &&) = delete; ~Profiler() { m_duration.finish(); log_debug("<- %s took %f seconds", m_msg, m_duration.seconds_elapsed()); } private: const char *m_msg = nullptr; mysqlshdk::utils::Duration m_duration; }; } // namespace void Instance_cache::Index::add_column(const Column *column) { m_columns.emplace_back(column); if (!m_columns_sql.empty()) { m_columns_sql += ','; } m_columns_sql += column->quoted_name; } struct Instance_cache_builder::Iterate_schema { std::string schema_column; std::vector<std::string> extra_columns; std::string table_name; std::string where; }; struct Instance_cache_builder::Iterate_table : public Instance_cache_builder::Iterate_schema { std::string table_column; }; struct Instance_cache_builder::Query_helper { static std::string case_sensitive_compare(const std::string &column, const std::string &value) { // we're forcing case-sensitive collation on all platforms (effectively // ignoring lower_case_table_names system variable) // STRCMP is used to overcome problems with comparison in 5.7 return shcore::sqlstring("STRCMP(" + column + " COLLATE utf8_bin,?)", 0) << value; } template <typename It> static std::string case_sensitive_compare(const std::string &column, It begin, It end, bool equals) { return "(" + shcore::str_join(begin, end, "&", [&column](const auto &v) { return case_sensitive_compare(column, v); }) + ")" + (!equals ? "!" : "") + "=0"; } template <typename C> static std::string case_sensitive_compare(const std::string &column, const C &c, bool equals) { return case_sensitive_compare(column, c.begin(), c.end(), equals); } static std::string compare(const std::string &column, const std::string &value) { return shcore::sqlstring(column + "=?", 0) << value; } template <typename It> static std::string compare(const std::string &column, It begin, It end, bool equals) { return "(" + column + (equals ? "" : " NOT") + " IN(" + shcore::str_join( begin, end, ",", [](const auto &v) { return shcore::sqlstring("?", 0) << v; }) + "))"; } template <typename C> static std::string compare(const std::string &column, const C &c, bool equals) { return compare(column, c.begin(), c.end(), equals); } static std::string build_query(const Iterate_schema &info, const std::string &filter) { std::string query = "SELECT " + info.schema_column; build_query(info, filter, &query); return query; } static std::string build_query(const Iterate_table &info, const std::string &filter) { std::string query = "SELECT " + info.schema_column + "," + info.table_column; build_query(info, filter, &query); return query; } static std::string case_sensitive_match(const Iterate_table &info, const Object &table) { return "(" + case_sensitive_compare(info.schema_column, table.schema) + "=0 AND " + case_sensitive_compare(info.table_column, table.name) + "=0)"; } template <typename It> static std::string case_sensitive_match(const Iterate_table &info, It begin, It end) { return "(" + shcore::str_join(begin, end, "OR", [&info](const auto &v) { return case_sensitive_match(info, v); }) + ")"; } template <typename T> static std::string case_sensitive_match(const std::string &schema, const T &objects, const std::string &schema_column, const std::string &object_column) { return "(" + case_sensitive_compare(schema_column, schema) + "=0 AND " + case_sensitive_compare(object_column, objects, true) + ")"; } template <typename T> static std::string match(const std::string &schema, const T &objects, const std::string &schema_column, const std::string &object_column) { return "(" + case_sensitive_compare(schema_column, schema) + "=0 AND " + compare(object_column, objects, true) + ")"; } template <typename C> static std::string build_case_sensitive_filter( const C &map, const std::string &schema_column, const std::string &object_column) { return build_objects_filter<C, typename C::mapped_type>( map, schema_column, object_column, case_sensitive_match); } template <typename C> static std::string build_filter(const C &map, const std::string &schema_column, const std::string &object_column) { return build_objects_filter<C, typename C::mapped_type>( map, schema_column, object_column, match); } private: static void build_query(const Iterate_schema &info, const std::string &filter, std::string *query) { if (!info.extra_columns.empty()) { *query += "," + shcore::str_join(info.extra_columns, ","); } *query += " FROM information_schema." + info.table_name; if (!info.where.empty() || !filter.empty()) { *query += " WHERE " + info.where; if (!info.where.empty() && !filter.empty()) { *query += " AND "; } *query += filter; } } template <typename C, typename T> static std::string build_objects_filter( const C &map, const std::string &schema_column, const std::string &object_column, std::string (*fun)(const std::string &, const T &, const std::string &, const std::string &)) { std::string filter; for (const auto &schema : map) { if (!schema.second.empty()) { filter += fun(schema.first, schema.second, schema_column, object_column) + "OR"; } } if (!filter.empty()) { // remove trailing OR filter.pop_back(); filter.pop_back(); } return filter; } }; Instance_cache_builder::Instance_cache_builder( const std::shared_ptr<mysqlshdk::db::ISession> &session, const common::Filtering_options &filters, Instance_cache &&cache) : m_session(session), m_cache(std::move(cache)), m_filters(filters) { set_schema_filter(); set_table_filter(); for (const auto &schema : m_cache.schemas) { if (!schema.second.tables.empty()) { set_has_tables(); } if (!schema.second.views.empty()) { set_has_views(); } if (has_tables() && has_views()) { break; } } if (m_cache.schemas.empty()) { fetch_version(); fetch_explain_select_rows_index(); filter_schemas(); filter_tables(); } } Instance_cache_builder &Instance_cache_builder::metadata( const Partition_filters &partitions) { fetch_metadata(partitions); return *this; } Instance_cache_builder &Instance_cache_builder::users() { Profiler profiler{"fetching users"}; Schema_dumper sd{m_session}; const auto &users = m_filters.users(); m_cache.users = sd.get_users(users); m_cache.roles = sd.get_roles(users); m_cache.filtered.users = m_cache.users.size(); m_cache.total.users = count("user_privileges", {}, "DISTINCT grantee"); return *this; } Instance_cache_builder &Instance_cache_builder::events() { Profiler profiler{"fetching events"}; Iterate_schema info; info.schema_column = "EVENT_SCHEMA"; // NOT NULL info.extra_columns = { "EVENT_NAME" // NOT NULL }; info.table_name = "events"; // event names are case insensitive info.where = object_filter(info, m_filters.events().included(), m_filters.events().excluded()); iterate_schemas(info, [this](const std::string &, Instance_cache::Schema *schema, const mysqlshdk::db::IRow *row) { schema->events.emplace(row->get_string(1)); // EVENT_NAME ++m_cache.filtered.events; }); // the total number of events within the filtered schemas m_cache.total.events = count(info); return *this; } Instance_cache_builder &Instance_cache_builder::routines() { Profiler profiler{"fetching routines"}; Iterate_schema info; info.schema_column = "ROUTINE_SCHEMA"; // NOT NULL info.extra_columns = { "ROUTINE_NAME", // NOT NULL "ROUTINE_TYPE", // NOT NULL }; info.table_name = "routines"; // routine names are case insensitive info.where = object_filter(info, m_filters.routines().included(), m_filters.routines().excluded()); const std::string procedure = "PROCEDURE"; iterate_schemas(info, [&procedure, this](const std::string &, Instance_cache::Schema *schema, const mysqlshdk::db::IRow *row) { auto &target = row->get_string(2) == procedure ? schema->procedures : schema->functions; // ROUTINE_TYPE target.emplace(row->get_string(1)); // ROUTINE_NAME ++m_cache.filtered.routines; }); // the total number of routines within the filtered schemas m_cache.total.routines = count(info); return *this; } Instance_cache_builder &Instance_cache_builder::triggers() { Profiler profiler{"fetching triggers"}; if (has_tables()) { Iterate_table info; info.schema_column = "TRIGGER_SCHEMA"; // NOT NULL info.table_column = "EVENT_OBJECT_TABLE"; // NOT NULL info.extra_columns = { "TRIGGER_NAME", // NOT NULL "ACTION_ORDER" // NOT NULL }; info.table_name = "triggers"; // trigger names are case sensitive info.where = trigger_filter(info, m_filters.triggers().included(), m_filters.triggers().excluded()); // schema -> table -> triggers std::unordered_map< std::string, std::unordered_map<std::string, std::multimap<uint64_t, std::string>>> triggers; iterate_tables(info, [&triggers, this](const std::string &schema_name, const std::string &table_name, Instance_cache::Table *, const mysqlshdk::db::IRow *row) { triggers[schema_name][table_name].emplace( row->get_uint(3), row->get_string(2)); // ACTION_ORDER, TRIGGER_NAME ++m_cache.filtered.triggers; }); for (auto &schema : triggers) { auto &s = m_cache.schemas.at(schema.first); for (auto &table : schema.second) { auto &t = s.tables.at(table.first); for (auto &trigger : table.second) { t.triggers.emplace_back(std::move(trigger.second)); } } } // the total number of triggers within the filtered tables m_cache.total.triggers = count(info); } return *this; } Instance_cache_builder &Instance_cache_builder::binlog_info() { Profiler profiler{"fetching binlog info"}; m_cache.binlog = Schema_dumper{m_session}.binlog(); return *this; } Instance_cache Instance_cache_builder::build() { return std::move(m_cache); } void Instance_cache_builder::filter_schemas() { Profiler profiler{"filtering schemas"}; std::string sql = "SELECT " "SCHEMA_NAME," // NOT NULL "DEFAULT_COLLATION_NAME " // NOT NULL "FROM information_schema.schemata"; const auto filter = schema_filter("SCHEMA_NAME"); if (!filter.empty()) { sql += " WHERE " + filter; } { const auto result = query(sql); while (const auto row = result->fetch_one()) { const auto schema = row->get_string(0); // SCHEMA_NAME m_cache.schemas[schema].collation = row->get_string(1); // DEFAULT_COLLATION_NAME } } m_cache.filtered.schemas = m_cache.schemas.size(); // the total number of schemas in an instance m_cache.total.schemas = count("schemata"); } void Instance_cache_builder::filter_tables() { Profiler profiler{"filtering tables"}; const std::string schema_column = "TABLE_SCHEMA"; const std::string table_column = "TABLE_NAME"; Iterate_schema info; info.schema_column = schema_column; // NOT NULL info.extra_columns = { table_column, // NOT NULL "TABLE_TYPE", // NOT NULL "TABLE_ROWS", // can be NULL "AVG_ROW_LENGTH", // can be NULL "ENGINE", // can be NULL "CREATE_OPTIONS", // can be NULL "TABLE_COMMENT" // can be NULL in 8.0 }; info.table_name = "tables"; info.where = table_filter(schema_column, table_column); iterate_schemas( info, [this](const std::string &, Instance_cache::Schema *schema, const mysqlshdk::db::IRow *row) { const auto table_type = row->get_string(2); // TABLE_TYPE if ("SYSTEM VIEW" == table_type) { return; } const auto table_name = row->get_string(1); // TABLE_NAME const auto is_table = "BASE TABLE" == table_type; Instance_cache::Table &target = is_table ? schema->tables[table_name] : schema->views[table_name]; target.row_count = row->get_uint(3, 0); // TABLE_ROWS target.average_row_length = row->get_uint(4, 0); // AVG_ROW_LENGTH target.engine = row->get_string(5, ""); // ENGINE target.create_options = row->get_string(6, ""); // CREATE_OPTIONS target.comment = row->get_string(7, ""); // TABLE_COMMENT if (is_table) { set_has_tables(); ++m_cache.filtered.tables; DBUG_EXECUTE_IF("dumper_average_row_length_0", { target.average_row_length = 0; }); } else { set_has_views(); ++m_cache.filtered.views; } }); // the total number of tables and views within the filtered schemas m_cache.total.tables = count(info, "'BASE TABLE'=TABLE_TYPE"); m_cache.total.views = count(info, "'VIEW'=TABLE_TYPE"); } void Instance_cache_builder::fetch_metadata( const Partition_filters &partitions) { Profiler profiler{"fetching metadata"}; fetch_ndbinfo(); fetch_server_metadata(); fetch_view_metadata(); fetch_columns(); fetch_table_indexes(); fetch_table_histograms(); fetch_table_partitions(partitions); } void Instance_cache_builder::fetch_version() { Profiler profiler{"fetching version"}; m_cache.server_version = Schema_dumper{m_session}.server_version(); } void Instance_cache_builder::fetch_explain_select_rows_index() { m_cache.explain_rows_idx = query("EXPLAIN SELECT 1")->field_names()->field_index("rows"); } void Instance_cache_builder::fetch_server_metadata() { Profiler profiler{"fetching server metadata"}; const auto &co = m_session->get_connection_options(); m_cache.user = co.get_user(); { const auto result = query("SELECT @@GLOBAL.HOSTNAME;"); if (const auto row = result->fetch_one()) { m_cache.server = row->get_string(0); } else { m_cache.server = co.has_host() ? co.get_host() : "localhost"; } } m_cache.hostname = mysqlshdk::utils::Net::get_hostname(); Schema_dumper dumper{m_session}; m_cache.gtid_executed = dumper.gtid_executed(); if (m_cache.server_version.version >= mysqlshdk::utils::Version(8, 0, 16)) { m_cache.partial_revokes = dumper.partial_revokes(); } } void Instance_cache_builder::fetch_ndbinfo() { Profiler profiler{"fetching ndbinfo"}; try { const auto result = query("SHOW VARIABLES LIKE 'ndbinfo\\_version'"); m_cache.has_ndbinfo = nullptr != result->fetch_one(); } catch (const mysqlshdk::db::Error &) { log_error("Failed to check if instance is a part of NDB cluster."); } } void Instance_cache_builder::fetch_view_metadata() { Profiler profiler{"fetching view metadata"}; if (!has_views()) { return; } Iterate_table info; info.schema_column = "TABLE_SCHEMA"; // NOT NULL info.table_column = "TABLE_NAME"; // NOT NULL info.extra_columns = { "CHARACTER_SET_CLIENT", // NOT NULL "COLLATION_CONNECTION" // NOT NULL }; info.table_name = "views"; iterate_views(info, [](const std::string &, const std::string &, Instance_cache::View *view, const mysqlshdk::db::IRow *row) { view->character_set_client = row->get_string(2); // CHARACTER_SET_CLIENT view->collation_connection = row->get_string(3); // COLLATION_CONNECTION }); } void Instance_cache_builder::fetch_columns() { Profiler profiler{"fetching columns"}; if (!has_tables() && !has_views()) { return; } Iterate_table info; info.schema_column = "TABLE_SCHEMA"; // NOT NULL info.table_column = "TABLE_NAME"; // NOT NULL info.extra_columns = { "COLUMN_NAME", // can be NULL in 8.0 "DATA_TYPE", // can be NULL in 8.0 "ORDINAL_POSITION", // NOT NULL "IS_NULLABLE", // NOT NULL "COLUMN_TYPE", // NOT NULL "EXTRA", // can be NULL in 8.0 }; info.table_name = "columns"; // schema -> table -> columns std::unordered_map< std::string, std::unordered_map< std::string, std::map<uint64_t, Instance_cache::Column>>> table_columns; // schema -> view -> columns std::unordered_map< std::string, std::unordered_map< std::string, std::map<uint64_t, Instance_cache::Column>>> view_columns; const auto create_column = [](const mysqlshdk::db::IRow *row) { Instance_cache::Column column; // these can be NULL in 8.0, as per output of 'SHOW COLUMNS', but it's // not likely, as they're NOT NULL in definition of mysql.columns hidden // table column.name = row->get_string(2, ""); // COLUMN_NAME column.quoted_name = shcore::quote_identifier(column.name); const auto data_type = row->get_string(3, ""); // DATA_TYPE column.type = mysqlshdk::db::dbstring_to_type( data_type, row->get_string(6)); // COLUMN_TYPE column.csv_unsafe = shcore::str_iendswith(data_type, "binary", "blob") || mysqlshdk::db::Type::Bit == column.type || mysqlshdk::db::Type::Geometry == column.type; const auto extra = row->get_string(7, ""); // EXTRA column.generated = extra.find(" GENERATED") != std::string::npos; column.auto_increment = extra.find("auto_increment") != std::string::npos; column.nullable = shcore::str_caseeq(row->get_string(5), "YES"); // IS_NULLABLE return column; }; iterate_tables_and_views( info, [&table_columns, &create_column]( const std::string &schema_name, const std::string &table_name, Instance_cache::Table *, const mysqlshdk::db::IRow *row) { table_columns[schema_name][table_name].emplace( row->get_uint(4), // ORDINAL_POSITION create_column(row)); }, [&view_columns, &create_column]( const std::string &schema_name, const std::string &view_name, Instance_cache::View *, const mysqlshdk::db::IRow *row) { view_columns[schema_name][view_name].emplace( row->get_uint(4), // ORDINAL_POSITION create_column(row)); }); for (auto &schema : table_columns) { auto &s = m_cache.schemas.at(schema.first); for (auto &table : schema.second) { auto &t = s.tables.at(table.first); t.all_columns.reserve(table.second.size()); for (auto &column : table.second) { t.all_columns.emplace_back(std::move(column.second)); if (!t.all_columns.back().generated) { t.columns.emplace_back(&t.all_columns.back()); } } } } for (auto &schema : view_columns) { auto &s = m_cache.schemas.at(schema.first); for (auto &view : schema.second) { auto &v = s.views.at(view.first); v.all_columns.reserve(view.second.size()); for (auto &column : view.second) { v.all_columns.emplace_back(std::move(column.second)); if (!v.all_columns.back().generated) { v.columns.emplace_back(&v.all_columns.back()); } } } } } void Instance_cache_builder::fetch_table_indexes() { Profiler profiler{"fetching table indexes"}; if (!has_tables()) { return; } Iterate_table info; info.schema_column = "TABLE_SCHEMA"; // NOT NULL info.table_column = "TABLE_NAME"; info.extra_columns = { "INDEX_NAME", // can be NULL in 8.0 "COLUMN_NAME", // can be NULL in 8.0 "SEQ_IN_INDEX" // NOT NULL }; info.table_name = "statistics"; info.where = "COLUMN_NAME IS NOT NULL AND NON_UNIQUE=0"; constexpr std::string_view k_primary_index = "PRIMARY"; struct Index_info { std::vector<Instance_cache::Column *> columns; }; // schema -> table -> index name -> index info // indexes are ordered to ensure repeatability of the selection algorithm std::unordered_map< std::string, std::unordered_map<std::string, std::map<std::string, Index_info>>> indexes; iterate_tables( info, [&indexes](const std::string &schema_name, const std::string &table_name, Instance_cache::Table *t, const mysqlshdk::db::IRow *row) { // INDEX_NAME can be NULL in 8.0, as per output of 'SHOW COLUMNS', but // it's not likely, as it's NOT NULL in definition of mysql.indexes // hidden table // // NULL values in COLUMN_NAME are filtered out const auto column = std::find_if(t->all_columns.begin(), t->all_columns.end(), [name = row->get_string(3)](const auto &c) { return name == c.name; }); // COLUMN_NAME // column will not be found if user is missing SELECT privilege and it // was not possible to fetch column information if (t->all_columns.end() != column) { auto &index_info = indexes[schema_name][table_name] [row->get_string(2, {})]; // INDEX_NAME const auto seq = row->get_uint(4); // SEQ_IN_INDEX // SEQ_IN_INDEX is 1-based if (seq > index_info.columns.size()) { index_info.columns.resize(seq); } index_info.columns[seq - 1] = &(*column); } else { assert(t->all_columns.empty()); } }); for (const auto &schema : indexes) { for (const auto &table : schema.second) { auto &t = m_cache.schemas.at(schema.first).tables.at(table.first); for (const auto &index : table.second) { Instance_cache::Index new_index; bool nullable = false; for (const auto &column : index.second.columns) { new_index.add_column(column); nullable |= column->nullable; } auto ptr = &t.indexes.emplace(index.first, std::move(new_index)).first->second; if (k_primary_index == index.first) { t.primary_key = ptr; } else if (!nullable) { t.primary_key_equivalents.emplace_back(ptr); } else { t.unique_keys.emplace_back(ptr); } } } } } void Instance_cache_builder::fetch_table_histograms() { Profiler profiler{"fetching table histograms"}; if (!has_tables() || !m_cache.server_version.is_8_0) { return; } try { Iterate_table info; info.schema_column = "SCHEMA_NAME"; // NOT NULL info.table_column = "TABLE_NAME"; // NOT NULL info.extra_columns = { "COLUMN_NAME", // NOT NULL "JSON_EXTRACT(HISTOGRAM,'$.\"number-of-buckets-specified\"')" // NOT // NULL }; info.table_name = "column_statistics"; iterate_tables( info, [](const std::string &, const std::string &, Instance_cache::Table *table, const mysqlshdk::db::IRow *row) { Instance_cache::Histogram histogram; histogram.column = row->get_string(2); // COLUMN_NAME histogram.buckets = shcore::lexical_cast<std::size_t>( row->get_string(3)); // number-of-buckets-specified table->histograms.emplace_back(std::move(histogram)); }); for (auto &schema : m_cache.schemas) { for (auto &table : schema.second.tables) { std::sort( table.second.histograms.begin(), table.second.histograms.end(), [](const auto &l, const auto &r) { return l.column < r.column; }); } } } catch (const mysqlshdk::db::Error &e) { log_error("Failed to fetch table histograms: %s.", e.format().c_str()); current_console()->print_warning("Failed to fetch table histograms."); } } void Instance_cache_builder::fetch_table_partitions( const Partition_filters &partitions) { Profiler profiler{"fetching table partitions"}; if (!has_tables()) { return; } Iterate_table info; info.schema_column = "TABLE_SCHEMA"; // NOT NULL info.table_column = "TABLE_NAME"; // NOT NULL info.extra_columns = { "PARTITION_NAME", // NOT NULL due to condition "SUBPARTITION_NAME", // can be NULL "TABLE_ROWS", // NOT NULL "AVG_ROW_LENGTH", // NOT NULL }; info.table_name = "partitions"; info.where = "PARTITION_NAME IS NOT NULL"; const auto include_partition = [&partitions](const std::string &schema, const std::string &table, const std::string &partition, const std::string &subpartition) { // if table is not on the list, all partitions from that table are // included const auto s = partitions.find(schema); if (partitions.end() == s) { return true; } const auto t = s->second.find(table); if (s->second.end() == t || t->second.empty()) { return true; } // we have a list of partitions for that table, include partition only // if it's on the list for (const auto &p : t->second) { if (partition == p || subpartition == p) { return true; } } return false; }; iterate_tables( info, [&include_partition](const std::string &s, const std::string &t, Instance_cache::Table *table, const mysqlshdk::db::IRow *row) { if (shcore::str_caseeq(table->engine, "NDB", "NDBCLUSTER")) { // Partition selection is disabled for tables employing a storage // engine that supplies automatic partitioning, such as NDB. Ignore // such tables. return; } auto partition = row->get_string(2); // PARTITION_NAME auto subpartition = row->get_string(3, {}); // SUBPARTITION_NAME if (!include_partition(s, t, partition, subpartition)) { return; } Instance_cache::Partition p; p.name = std::move(subpartition.empty() ? partition : subpartition); p.quoted_name = shcore::quote_identifier(p.name); p.row_count = row->get_uint(4, 0); // TABLE_ROWS p.average_row_length = row->get_uint(5, 0); // AVG_ROW_LENGTH table->partitions.emplace_back(std::move(p)); }); } void Instance_cache_builder::iterate_schemas( const Iterate_schema &info, const std::function<void(const std::string &, Instance_cache::Schema *, const mysqlshdk::db::IRow *)> &callback) { Profiler profiler{"iterating schemas"}; const auto result = query(QH::build_query(info, schema_filter(info))); std::string current_schema; Instance_cache::Schema *schema = nullptr; while (const auto row = result->fetch_one()) { { auto schema_name = row->get_string(0); if (schema_name != current_schema) { current_schema = std::move(schema_name); const auto it = m_cache.schemas.find(current_schema); if (it != m_cache.schemas.end()) { schema = &it->second; } else { schema = nullptr; } } } if (schema) { callback(current_schema, schema, row); } } } void Instance_cache_builder::iterate_tables_and_views( const Iterate_table &info, const std::function<void(const std::string &, const std::string &, Instance_cache::Table *, const mysqlshdk::db::IRow *)> &table_callback, const std::function<void(const std::string &, const std::string &, Instance_cache::View *, const mysqlshdk::db::IRow *)> &view_callback) { Profiler profiler{"iterating tables and views"}; const auto result = query(QH::build_query(info, schema_and_table_filter(info))); std::string current_schema; Instance_cache::Schema *schema = nullptr; std::string current_object; Instance_cache::Table *table = nullptr; Instance_cache::View *view = nullptr; while (const auto row = result->fetch_one()) { { auto schema_name = row->get_string(0); if (schema_name != current_schema) { current_schema = std::move(schema_name); current_object.clear(); const auto it = m_cache.schemas.find(current_schema); if (it != m_cache.schemas.end()) { schema = &it->second; } else { schema = nullptr; table = nullptr; view = nullptr; } } } if (schema) { auto object_name = row->get_string(1); if (object_name != current_object) { current_object = std::move(object_name); const auto table_it = schema->tables.find(current_object); if (table_it != schema->tables.end()) { table = &table_it->second; view = nullptr; } else { table = nullptr; const auto view_it = schema->views.find(current_object); if (view_it != schema->views.end()) { view = &view_it->second; } else { view = nullptr; } } } } if (table && table_callback) { table_callback(current_schema, current_object, table, row); } if (view && view_callback) { view_callback(current_schema, current_object, view, row); } } } void Instance_cache_builder::iterate_tables( const Iterate_table &info, const std::function<void(const std::string &, const std::string &, Instance_cache::Table *, const mysqlshdk::db::IRow *)> &callback) { Profiler profiler{"iterating tables"}; iterate_tables_and_views(info, callback, {}); } void Instance_cache_builder::iterate_views( const Iterate_table &info, const std::function<void(const std::string &, const std::string &, Instance_cache::View *, const mysqlshdk::db::IRow *)> &callback) { Profiler profiler{"iterating views"}; iterate_tables_and_views(info, {}, callback); } void Instance_cache_builder::set_schema_filter() { m_schema_filter = ""; const auto &included = m_filters.schemas().included(); const auto &excluded = m_filters.schemas().excluded(); if (!included.empty()) { m_schema_filter += QH::case_sensitive_compare(k_schema_template, included, true); } if (!excluded.empty()) { if (!included.empty()) { m_schema_filter += " AND "; } m_schema_filter += QH::case_sensitive_compare(k_schema_template, excluded, false); } } void Instance_cache_builder::set_table_filter() { m_table_filter = ""; const auto &included = m_filters.tables().included(); const auto &excluded = m_filters.tables().excluded(); if (!included.empty()) { m_table_filter += "(" + QH::build_case_sensitive_filter( included, k_schema_template, k_table_template) + ")"; } for (const auto &schema : excluded) { if (!m_table_filter.empty()) { m_table_filter += " AND "; } m_table_filter += "NOT " + QH::case_sensitive_match(schema.first, schema.second, k_schema_template, k_table_template); } } std::string Instance_cache_builder::schema_filter( const std::string &schema_column) const { return shcore::str_subvars( m_schema_filter, [&schema_column](std::string_view) { return schema_column; }, k_template_marker, k_template_marker); } std::string Instance_cache_builder::schema_filter( const Iterate_schema &info) const { return schema_filter(info.schema_column); } std::string Instance_cache_builder::table_filter( const std::string &schema_column, const std::string &table_column) const { return shcore::str_subvars( m_table_filter, [&schema_column, &table_column](std::string_view var) { if (var == k_schema_var) return schema_column; if (var == k_table_var) return table_column; throw std::logic_error("Unknown variable: " + std::string{var}); }, k_template_marker, k_template_marker); } std::string Instance_cache_builder::schema_and_table_filter( const Iterate_table &info) const { auto result = schema_filter(info.schema_column); auto filter = table_filter(info.schema_column, info.table_column); if (!result.empty() && !filter.empty()) { result += " AND "; } result += filter; return result; } std::string Instance_cache_builder::object_filter( const Iterate_schema &info, const Object_filters &included, const Object_filters &excluded) const { std::string filter; const auto &object_column = info.extra_columns[0]; if (!included.empty()) { filter += "(" + QH::build_filter(included, info.schema_column, object_column) + ")"; } for (const auto &schema : excluded) { if (!filter.empty()) { filter += " AND "; } filter += "NOT " + QH::match(schema.first, schema.second, info.schema_column, object_column); } return filter; } std::string Instance_cache_builder::trigger_filter( const Iterate_table &info, const Trigger_filters &included, const Trigger_filters &excluded) const { const auto filter_triggers = [&info](const std::string &schema, const Object_filters &tables) { const auto &trigger_column = info.extra_columns[0]; const auto schema_filter = QH::case_sensitive_compare(info.schema_column, schema) + "=0"; std::string filter; for (const auto &table : tables) { filter += "(" + schema_filter + " AND " + QH::case_sensitive_compare(info.table_column, table.first) + "=0"; if (!table.second.empty()) { // only the specified triggers are used filter += " AND" + QH::case_sensitive_compare(trigger_column, table.second, true); } filter += ")OR"; } if (!filter.empty()) { // remove trailing OR filter.pop_back(); filter.pop_back(); } return filter; }; std::string filter; if (!included.empty()) { filter += "(" + shcore::str_join(included.begin(), included.end(), "OR", [&filter_triggers](const auto &v) { return filter_triggers(v.first, v.second); }) + ")"; } for (const auto &schema : excluded) { if (!filter.empty()) { filter += " AND "; } filter += "NOT(" + filter_triggers(schema.first, schema.second) + ")"; } return filter; } uint64_t Instance_cache_builder::count(const std::string &table, const std::string &where, const std::string &column) const { std::string sql = "SELECT COUNT(" + column + ") FROM information_schema." + table; if (!where.empty()) { sql += " WHERE " + where; } return query(sql)->fetch_one()->get_uint(0); } uint64_t Instance_cache_builder::count(const Iterate_schema &info, const std::string &where) const { auto filter = schema_filter(info); if (!filter.empty() && !where.empty()) { filter += " AND "; } filter += where; return count(info.table_name, filter); } uint64_t Instance_cache_builder::count(const Iterate_table &info, const std::string &where) const { auto filter = schema_and_table_filter(info); if (!filter.empty() && !where.empty()) { filter += " AND "; } filter += where; return count(info.table_name, filter); } } // namespace dump } // namespace mysqlsh