backend/wbprivate/sqlide/wb_live_schema_tree.cpp (1,817 lines of code) (raw):

/* * Copyright (c) 2009, 2022, 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 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 "wb_live_schema_tree.h" #include "grtdb/charset_utils.h" #include "grt/icon_manager.h" #include "grts/structs.db.query.h" #include "base/string_utilities.h" #include "base/log.h" #include "mforms/app.h" #include <boost/make_shared.hpp> using namespace wb; using namespace bec; using namespace base; using namespace grt; DEFAULT_LOG_DOMAIN("SqlEditorSchemaTree"); const short LiveSchemaTree::COLUMN_DATA = 0x01; const short LiveSchemaTree::TRIGGER_DATA = 0x02; const short LiveSchemaTree::INDEX_DATA = 0x04; const short LiveSchemaTree::FK_DATA = 0x08; const std::string LiveSchemaTree::SCHEMA_TAG = "_SCHEMA_"; const std::string LiveSchemaTree::TABLES_TAG = "_TABLES_"; const std::string LiveSchemaTree::VIEWS_TAG = "_VIEWS_"; const std::string LiveSchemaTree::PROCEDURES_TAG = "_PROCEDURES_"; const std::string LiveSchemaTree::FUNCTIONS_TAG = "_FUNCTIONS_"; const std::string LiveSchemaTree::TABLE_TAG = "_TABLE_"; const std::string LiveSchemaTree::VIEW_TAG = "_VIEW_"; const std::string LiveSchemaTree::ROUTINE_TAG = "_ROUTINE_"; const std::string LiveSchemaTree::COLUMNS_TAG = "_COLUMNS_"; const std::string LiveSchemaTree::INDEXES_TAG = "_INDEXES_"; const std::string LiveSchemaTree::TRIGGERS_TAG = "_TRIGGERS_"; const std::string LiveSchemaTree::FOREIGN_KEYS_TAG = "_FOREIGN_KEYS_"; const std::string LiveSchemaTree::COLUMN_TAG = "_COLUMN_"; const std::string LiveSchemaTree::INDEX_TAG = "_INDEX_"; const std::string LiveSchemaTree::TRIGGER_TAG = "_TRIGGER_"; const std::string LiveSchemaTree::FOREIGN_KEY_TAG = "_FOREIGN_KEY_"; const std::string LiveSchemaTree::FETCHING_CAPTION = _("fetching..."); const std::string LiveSchemaTree::ERROR_FETCHING_CAPTION = _("could not be fetched"); const std::string LiveSchemaTree::TABLES_CAPTION = _("Tables"); const std::string LiveSchemaTree::VIEWS_CAPTION = _("Views"); const std::string LiveSchemaTree::PROCEDURES_CAPTION = _("Stored Procedures"); const std::string LiveSchemaTree::FUNCTIONS_CAPTION = _("Functions"); const std::string LiveSchemaTree::COLUMNS_CAPTION = _("Columns"); const std::string LiveSchemaTree::INDEXES_CAPTION = _("Indexes"); const std::string LiveSchemaTree::TRIGGERS_CAPTION = _("Triggers"); const std::string LiveSchemaTree::FOREIGN_KEYS_CAPTION = _("Foreign Keys"); const std::string LiveSchemaTree::LST_INFO_BOX_DETAIL_ROW = "<tr>" "<td style=\"border:none; padding-left: 15px;\">%s</td>" "<td style=\"border:none; padding-left: 15px;\"><font color='#717171'>%s</font></td>" "</tr>"; const int LiveSchemaTree::TABLES_NODE_INDEX = 0; const int LiveSchemaTree::VIEWS_NODE_INDEX = 1; const int LiveSchemaTree::PROCEDURES_NODE_INDEX = 2; const int LiveSchemaTree::FUNCTIONS_NODE_INDEX = 3; const int LiveSchemaTree::TABLE_COLUMNS_NODE_INDEX = 0; const int LiveSchemaTree::TABLE_INDEXES_NODE_INDEX = 1; const int LiveSchemaTree::TABLE_FOREIGN_KEYS_NODE_INDEX = 2; const int LiveSchemaTree::TABLE_TRIGGERS_NODE_INDEX = 3; const char* LiveSchemaTree::_schema_tokens[16] = { "", // Empty item to use 0 as not found "CASCADE", "SET NULL", "SET DEFAULT", "RESTRICT", "NO ACTION", // The update/delete rules on foreign keys "BTREE", "FULLTEXT", "HASH", "RTREE", "SPATIAL", // The index types "INSERT", "UPDATE", "DELETE", // Trigger events "BEFORE", "AFTER"}; // Trigger timing LiveSchemaTree::LSTData::LSTData() : details(""){}; void LiveSchemaTree::LSTData::copy(LSTData* other) { this->details = other->details; } std::string LiveSchemaTree::LSTData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val(""); if (full) { std::string owner_name = node->get_string(0); ret_val = strfmt(_("<b>%s:</b> <font color='#148814'><b>%s</b></font><br><br>"), get_object_name().c_str(), owner_name.c_str()); } else ret_val = details; return ret_val; } void LiveSchemaTree::ColumnData::copy(LSTData* other) { LSTData::copy(other); ColumnData* pother = dynamic_cast<ColumnData*>(other); if (pother) { this->name = pother->name; this->type = pother->type; this->default_value = pother->default_value; this->charset_collation = pother->charset_collation; this->is_id = pother->is_id; this->is_pk = pother->is_pk; this->is_fk = pother->is_fk; this->is_idx = pother->is_idx; } } std::string LiveSchemaTree::ColumnData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val(""); if (details.empty()) { std::string html_name = name; if (is_pk || is_idx) { if (is_pk) html_name = "<u>" + html_name + "</u>"; html_name = "<b>" + html_name + "</b>"; } std::string html_type = type; if (is_pk) html_type += " PK"; details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), html_name.c_str(), html_type.c_str()); } if (full) { ret_val = LSTData::get_details(full, node); if (!charset_collation.empty()) { ret_val += _("<b>Collation:</b> "); ret_val += charset_collation; ret_val += "<br><br>"; } ret_val += _("<b>Definition:</b><table style=\"border: none; border-collapse: collapse;\">"); ret_val += details; ret_val += "</table><br><br>"; } else ret_val = details; return ret_val; } void LiveSchemaTree::FKData::copy(LSTData* other) { LSTData::copy(other); FKData* pother = dynamic_cast<FKData*>(other); if (pother) { this->referenced_table = pother->referenced_table; this->from_cols = pother->from_cols; this->to_cols = pother->to_cols; this->update_rule = pother->update_rule; this->delete_rule = pother->delete_rule; } } std::string LiveSchemaTree::FKData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val(""); if (details.empty()) { std::string target = strfmt("%s (%s \xE2\x86\x92 %s)", referenced_table.c_str(), from_cols.c_str(), to_cols.c_str()); details = "<table style=\"border: none; border-collapse: collapse;\">"; details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Target", target.c_str()); details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "On Update", externalize_token(update_rule).c_str()); details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "On Delete", externalize_token(delete_rule).c_str()); details += "</table>"; } if (full) { ret_val = LSTData::get_details(full, node); ret_val += _("<b>Definition:</b><br>"); ret_val += details; } else ret_val = details; return ret_val; } void LiveSchemaTree::IndexData::copy(LSTData* other) { LSTData::copy(other); IndexData* pother = dynamic_cast<IndexData*>(other); if (pother) { this->columns.assign(pother->columns.begin(), pother->columns.end()); this->type = pother->type; this->unique = pother->unique; this->visible = pother->visible; } } std::string LiveSchemaTree::IndexData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val(""); if (details.empty()) { details = "<table style=\"border: none; border-collapse: collapse;\">"; details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Type", externalize_token(type).c_str()); details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Unique", unique ? "Yes" : "No"); details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Visible", visible ? "Yes" : "No"); details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Columns", columns[0].c_str()); for (std::size_t index = 1; index < columns.size(); index++) details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "", columns[index].c_str()); details += "</table>"; } if (full) { ret_val = LSTData::get_details(full, node); ret_val += _("<b>Definition:</b><br>"); ret_val += details; } else ret_val = details; return ret_val; } void LiveSchemaTree::TriggerData::copy(LSTData* other) { LSTData::copy(other); TriggerData* pother = dynamic_cast<TriggerData*>(other); if (pother) { this->event_manipulation = pother->event_manipulation; this->timing = pother->timing; } } std::string LiveSchemaTree::TriggerData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val(""); if (details.empty()) { details = "<table style=\"border: none; border-collapse: collapse;\">"; details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Event", externalize_token(event_manipulation).c_str()); details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Timing", externalize_token(timing).c_str()); details += "</table>"; } if (full) { ret_val = LSTData::get_details(full, node); ret_val += _("<b>Definition:</b><br>"); ret_val += details; } else ret_val = details; return ret_val; } void LiveSchemaTree::ObjectData::copy(LSTData* other) { LSTData::copy(other); ObjectData* pother = dynamic_cast<ObjectData*>(other); if (pother) { this->fetched = pother->fetched; this->fetching = pother->fetching; } } void LiveSchemaTree::ViewData::copy(LSTData* other) { ObjectData::copy(other); ViewData* pother = dynamic_cast<ViewData*>(other); if (pother) { this->_loaded_mask = pother->_loaded_mask; this->_loading_mask = pother->_loading_mask; this->columns_load_error = pother->columns_load_error; } } std::string LiveSchemaTree::ViewData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val; if (full) ret_val = LSTData::get_details(full, node); if (_loaded_mask & COLUMN_DATA) { int count = (get_type() == Table) ? node->get_child(TABLE_COLUMNS_NODE_INDEX)->count() : node->count(); if (count) { ret_val += _("<b>Columns:</b><table style=\"border: none; border-collapse: collapse;\">"); for (int index = 0; index < count; index++) { ColumnData* pdata; if (get_type() == Table) pdata = dynamic_cast<ColumnData*>(node->get_child(TABLE_COLUMNS_NODE_INDEX)->get_child(index)->get_data()); else pdata = dynamic_cast<ColumnData*>(node->get_child(index)->get_data()); ret_val += pdata->get_details(false, node); } ret_val += "</table><br><br>"; } } // Appends the error message if needed if (columns_load_error) ret_val += details; return ret_val; } std::string LiveSchemaTree::TableData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val = ViewData::get_details(full, node); if (_loaded_mask & FK_DATA) { int count = node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->count(); if (count) { ret_val += _("<div><b>Related Tables:</b></div>"); for (int index = 0; index < count; index++) { FKData* pdata = dynamic_cast<FKData*>(node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->get_child(index)->get_data()); ret_val += pdata->get_details(false, node); } } } return ret_val; } void LiveSchemaTree::SchemaData::copy(LSTData* other) { LSTData::copy(other); SchemaData* pother = dynamic_cast<SchemaData*>(other); if (pother) { this->fetched = pother->fetched; this->fetching = pother->fetching; } } //-------------------------------------------------------------------------------------------------- short LiveSchemaTree::ViewData::get_loaded_mask() { return _loaded_mask; } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::ViewData::set_unloaded_data(short mask) { // Gets the data that is loaded and needs to be marked as unloaded short filter = _loaded_mask & mask; _loaded_mask -= filter; } void LiveSchemaTree::ViewData::set_loaded_data(short mask) { // Gets the data that is being marked as loaded and was loading short filter = mask & _loading_mask; _loading_mask -= filter; // Now marks the data as loaded _loaded_mask |= mask; } short LiveSchemaTree::ViewData::get_loading_mask() { return _loading_mask; } void LiveSchemaTree::ViewData::set_loading_mask(short mask) { _loading_mask = mask; } bool LiveSchemaTree::ViewData::is_data_loaded(short mask) { return (_loaded_mask & mask) == mask; } bool LiveSchemaTree::ViewData::is_update_complete() { bool ret_val = false; if (_reload_mask) { short loaded_mask = get_loaded_mask(); if ((loaded_mask & _reload_mask) == _reload_mask) { _reload_mask = 0; ret_val = true; } } return ret_val; } std::string LiveSchemaTree::ProcedureData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val = ObjectData::get_details(true, node); ret_val += ObjectData::get_details(false, node); return ret_val; } std::string LiveSchemaTree::FunctionData::get_details(bool full, const mforms::TreeNodeRef& node) { std::string ret_val = ObjectData::get_details(true, node); ret_val += ObjectData::get_details(false, node); return ret_val; } //-------------------------------------------------------------------------------------------------- LiveSchemaTree::LiveSchemaTree(MySQLVersion version) : _model_view(nullptr), _schema_pattern(0), _object_pattern(0), _case_sensitive_identifiers(false), _is_schema_contents_enabled(true), _enabled_events(false), _version(version), _base(0), _filter(), _filter_type(Any) { fill_node_icons(); // Setup the schema node collection skeleton mforms::TreeNodeCollectionSkeleton schema_nodes(_icon_paths[Schema]); mforms::TreeNodeSkeleton tables(TABLES_CAPTION, _icon_paths[TableCollection], TABLES_TAG); schema_nodes.children.push_back(tables); mforms::TreeNodeSkeleton views(VIEWS_CAPTION, _icon_paths[ViewCollection], VIEWS_TAG); schema_nodes.children.push_back(views); mforms::TreeNodeSkeleton procedures(PROCEDURES_CAPTION, _icon_paths[ProcedureCollection], PROCEDURES_TAG); schema_nodes.children.push_back(procedures); mforms::TreeNodeSkeleton functions(FUNCTIONS_CAPTION, _icon_paths[FunctionCollection], FUNCTIONS_TAG); schema_nodes.children.push_back(functions); _node_collections[Schema] = schema_nodes; // Setup the table node collection skeleton mforms::TreeNodeCollectionSkeleton table_nodes(_icon_paths[Table]); mforms::TreeNodeSkeleton columns(COLUMNS_CAPTION, _icon_paths[ColumnCollection], COLUMNS_TAG); table_nodes.children.push_back(columns); mforms::TreeNodeSkeleton indexes(INDEXES_CAPTION, _icon_paths[IndexCollection], INDEXES_TAG); table_nodes.children.push_back(indexes); mforms::TreeNodeSkeleton foreign_keys(FOREIGN_KEYS_CAPTION, _icon_paths[ForeignKeyCollection], FOREIGN_KEYS_TAG); mforms::TreeNodeSkeleton fetching_fk(FETCHING_CAPTION, _icon_paths[ForeignKey], ""); foreign_keys.children.push_back(fetching_fk); table_nodes.children.push_back(foreign_keys); mforms::TreeNodeSkeleton triggers(TRIGGERS_CAPTION, _icon_paths[TriggerCollection], TRIGGERS_TAG); mforms::TreeNodeSkeleton fetching_trigger(FETCHING_CAPTION, _icon_paths[Trigger], ""); triggers.children.push_back(fetching_trigger); table_nodes.children.push_back(triggers); _node_collections[Table] = table_nodes; // Setup the view node collection skeleton mforms::TreeNodeCollectionSkeleton view_nodes(_icon_paths[View]); mforms::TreeNodeSkeleton fetching_view(FETCHING_CAPTION, _icon_paths[View], ""); view_nodes.children.push_back(fetching_view); _node_collections[View] = view_nodes; mforms::TreeNodeCollectionSkeleton procedure_nodes(_icon_paths[Procedure]); _node_collections[Procedure] = procedure_nodes; mforms::TreeNodeCollectionSkeleton function_nodes(_icon_paths[Function]); _node_collections[Function] = function_nodes; mforms::TreeNodeCollectionSkeleton table_column_nodes(_icon_paths[TableColumn]); _node_collections[TableColumn] = table_column_nodes; mforms::TreeNodeCollectionSkeleton view_column_nodes(_icon_paths[ViewColumn]); _node_collections[ViewColumn] = view_column_nodes; mforms::TreeNodeCollectionSkeleton index_nodes(_icon_paths[Index]); _node_collections[Index] = index_nodes; mforms::TreeNodeCollectionSkeleton trigger_nodes(_icon_paths[Trigger]); _node_collections[Trigger] = trigger_nodes; mforms::TreeNodeCollectionSkeleton fk_nodes(_icon_paths[ForeignKey]); _node_collections[ForeignKey] = fk_nodes; } LiveSchemaTree::~LiveSchemaTree() { clean_filter(); } void LiveSchemaTree::set_fetch_delegate(std::shared_ptr<FetchDelegate> delegate) { _fetch_delegate = delegate; } void LiveSchemaTree::set_delegate(std::shared_ptr<Delegate> delegate) { _delegate = delegate; } unsigned char LiveSchemaTree::internalize_token(const std::string& token) { unsigned char found_index = 0; for (unsigned char index = 1; !found_index && (index < (sizeof(_schema_tokens) / sizeof(char*))); index++) { if (token == _schema_tokens[index]) found_index = index; } return found_index; } std::string LiveSchemaTree::externalize_token(unsigned char c) { return (c > 0 && c < (sizeof(_schema_tokens) / sizeof(char*))) ? _schema_tokens[c] : ""; } void LiveSchemaTree::set_case_sensitive_identifiers(bool flag) { _case_sensitive_identifiers = flag; } bool LiveSchemaTree::identifiers_equal(const std::string& a, const std::string& b) { return base::string_compare(a, b, _case_sensitive_identifiers) == 0; } void LiveSchemaTree::setup_node(mforms::TreeNodeRef node, ObjectType type, mforms::TreeNodeData* pdata, bool ignore_null_data) { switch (type) { case Schema: node->set_data(pdata ? pdata : new SchemaData()); break; case Table: node->set_data(pdata ? pdata : new TableData()); break; case View: node->set_data(pdata ? pdata : new ViewData()); break; case Procedure: node->set_data(pdata ? pdata : new ProcedureData()); break; case Function: node->set_data(pdata ? pdata : new FunctionData()); break; case ViewColumn: if (pdata || !ignore_null_data) node->set_data(pdata ? pdata : new ColumnData(type)); break; case TableColumn: node->set_data(pdata ? pdata : new ColumnData(type)); break; case Index: node->set_data(pdata ? pdata : new IndexData()); break; case Trigger: if (pdata || !ignore_null_data) node->set_data(pdata ? pdata : new TriggerData()); break; case ForeignKey: if (pdata || !ignore_null_data) node->set_data(pdata ? pdata : new FKData()); break; default: break; } } void LiveSchemaTree::fill_node_icons() { _icon_paths[Schema] = get_node_icon_path(Schema); _icon_paths[TableCollection] = get_node_icon_path(TableCollection); _icon_paths[ViewCollection] = get_node_icon_path(ViewCollection); _icon_paths[ProcedureCollection] = get_node_icon_path(ProcedureCollection); _icon_paths[FunctionCollection] = get_node_icon_path(FunctionCollection); _icon_paths[Table] = get_node_icon_path(Table); _icon_paths[View] = get_node_icon_path(View); _icon_paths[Procedure] = get_node_icon_path(Procedure); _icon_paths[Function] = get_node_icon_path(Function); _icon_paths[ColumnCollection] = get_node_icon_path(ColumnCollection); _icon_paths[IndexCollection] = get_node_icon_path(IndexCollection); _icon_paths[ForeignKeyCollection] = get_node_icon_path(ForeignKeyCollection); _icon_paths[TriggerCollection] = get_node_icon_path(TriggerCollection); _icon_paths[ViewColumn] = get_node_icon_path(ViewColumn); _icon_paths[TableColumn] = get_node_icon_path(TableColumn); _icon_paths[Index] = get_node_icon_path(Index); _icon_paths[ForeignKey] = get_node_icon_path(ForeignKey); _icon_paths[Trigger] = get_node_icon_path(Trigger); } std::string LiveSchemaTree::get_node_icon_path(ObjectType type) { bec::IconId icon = get_node_icon(type); return bec::IconManager::get_instance()->get_icon_file(icon); } bec::IconId LiveSchemaTree::get_node_icon(ObjectType type) { bec::IconId icon; switch (type) { case Schema: icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.unloaded.side.$.png", bec::Icon16); break; case TableCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.Table.many.side.$.png", bec::Icon16); break; case ViewCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.View.many.side.$.png", bec::Icon16); break; // TODO: Update the Procedure and Function collection icons to the correct value case ProcedureCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.many.side.$.png", bec::Icon16); break; case FunctionCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.many.side.$.png", bec::Icon16); break; case Table: icon = bec::IconManager::get_instance()->get_icon_id("db.Table.side.$.png", bec::Icon16); break; case View: icon = bec::IconManager::get_instance()->get_icon_id("db.View.side.$.png", bec::Icon16); break; case Procedure: icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.side.$.png", bec::Icon16); break; case Function: icon = bec::IconManager::get_instance()->get_icon_id("grt_function.png", bec::Icon16); break; case ColumnCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.Column.many.side.$.png", bec::Icon16); break; case IndexCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.Index.many.side.$.png", bec::Icon16); break; case ForeignKeyCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.many.side.$.png", bec::Icon16); break; case TriggerCollection: icon = bec::IconManager::get_instance()->get_icon_id("db.Trigger.many.side.$.png", bec::Icon16); break; case ViewColumn: icon = bec::IconManager::get_instance()->get_icon_id("db.Column.side.$.png", bec::Icon16); break; case TableColumn: icon = bec::IconManager::get_instance()->get_icon_id("db.Column.side.$.png", bec::Icon16); break; case Index: icon = bec::IconManager::get_instance()->get_icon_id("db.Index.side.$.png", bec::Icon16); break; case ForeignKey: icon = bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.side.$.png", bec::Icon16); break; case Trigger: icon = bec::IconManager::get_instance()->get_icon_id("db.Trigger.side.$.png", bec::Icon16); break; default: icon = -1; break; } return icon; } void LiveSchemaTree::update_node_icon(mforms::TreeNodeRef node) { bec::IconId icon = 0; LSTData* pnode_data = dynamic_cast<LSTData*>(node->get_data()); if (pnode_data) { switch (pnode_data->get_type()) { case Schema: { SchemaData* pdata = dynamic_cast<SchemaData*>(node->get_data()); if (pdata->fetched) icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.side.$.png", bec::Icon16); else if (pdata->fetching) icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.loading.side.$.png", bec::Icon16); else icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.unloaded.side.$.png", bec::Icon16); } break; case View: { ViewData* pdata = dynamic_cast<ViewData*>(node->get_data()); if (pdata->columns_load_error) icon = bec::IconManager::get_instance()->get_icon_id("db.View.broken.side.$.png", bec::Icon16); else icon = bec::IconManager::get_instance()->get_icon_id("db.View.side.$.png", bec::Icon16); } break; case TableColumn: { ColumnData* pdata = dynamic_cast<ColumnData*>(node->get_data()); if (pdata->is_pk) icon = bec::IconManager::get_instance()->get_icon_id("db.Column.pk.side.$.png", bec::Icon16); else if (pdata->is_fk) icon = bec::IconManager::get_instance()->get_icon_id("db.Column.fk.side.$.png", bec::Icon16); } break; // No other item changes icon default: break; } if (icon) node->set_icon_path(0, bec::IconManager::get_instance()->get_icon_file(icon)); } } /* * Function: update_change_data * Description: Uses the received information to * - Generate the list of nodes to be removed * - Purge children to let there only the items to be added * Parameters: * parent: the tree node for which the children list will be updated * children: the list of names that will be used on the process * type: the type of children that will be affected (some nodes may have children of different types) * to_remove: a vector containing the nodes to be removed from the parent node */ void LiveSchemaTree::update_change_data(mforms::TreeNodeRef parent, base::StringListPtr children, ObjectType type, std::vector<mforms::TreeNodeRef>& to_remove) { mforms::TreeNodeRef node; int total_nodes = parent->count(); if (total_nodes == 1 && parent->get_child(0)->get_string(0) == FETCHING_CAPTION) to_remove.push_back(parent->get_child(0)); else { for (int index = 0; index < total_nodes; index++) { node = parent->get_child(index); LSTData* pchild_data = dynamic_cast<LSTData*>(node->get_data()); // Ensure only items of the same type are analyzed if (pchild_data && pchild_data->get_type() == type) { std::list<std::string>::iterator found_child = std::find(children->begin(), children->end(), node->get_string(0)); // If not found, the item will be removed if (found_child == children->end()) to_remove.push_back(node); // If found, the item is removed from the incoming list // to let only nodes to be added else children->erase(found_child); } } } } /* * Function: update_node_children * Description: Updates the children of a node based on a received children name list: * Removes children nodes not contained on the received name list (if not just appending) * Adds children nodes for names on the list for which a node is missing * Parameters: * parent: the tree node for which the children list will be updated * children: the list of names that will be used on the process * type: the type of children that will be affected (some nodes may have children of different types) * sorted: used to determine whether the nodes should be inserted in the proper position to keep the objects * sorted or if they just need to be appended the way they come. * just_append: suppresses the logic of removing nodes (i.e. when searching nodes with different filters * they are only appended on the base tree) * Return Value: a boolean value indicating whether the children list changed (nodes were added or removed) * * NOTE : That children may change, so if the original list is needed, the caller needs to have a copy */ bool LiveSchemaTree::update_node_children(mforms::TreeNodeRef parent, base::StringListPtr children, ObjectType type, bool sorted, bool just_append) { bool ret_val = false; if (_base) { std::vector<std::string> path = get_node_path(parent); mforms::TreeNodeRef base_parent = _base->get_node_from_path(path); ret_val = _base->update_node_children(base_parent, children, type, sorted, just_append); // Filters the arrived data GPatternSpec* pattern = NULL; if (type == Schema) pattern = _schema_pattern; else if (is_object_type(SchemaObject, type)) pattern = _object_pattern; filter_children(type, base_parent, parent, pattern); } else { mforms::TreeNodeRef node; bool added = false; bool removed = false; std::vector<mforms::TreeNodeRef> childs_to_remove; //_model_view->freeze_refresh(); cannot be called from a background thread. // Calculates the nodes to be removed and the new nodes to be created update_change_data(parent, children, type, childs_to_remove); // Removes deleted children if needed... if (!just_append && !childs_to_remove.empty()) { for (std::size_t index = 0; index < childs_to_remove.size(); index++) childs_to_remove[index]->remove_from_parent(); removed = true; } // If at this point there are nodes on the parent, then the new nodes // need to be inserted into the right position. // If not, as they are sorted, they just get added. std::vector<mforms::TreeNodeRef> added_nodes; std::vector<mforms::TreeNodeRef> group_added_nodes; std::string icon_path = get_node_icon_path(type); if (sorted) children->sort( std::bind(base::stl_string_compare, std::placeholders::_1, std::placeholders::_2, _case_sensitive_identifiers)); if (!children->empty()) { // it and it_end are used on the iteration process // start and end are used to determine a group to be inserted // which by default is the whole list std::list<std::string>::const_iterator it = children->begin(), start = it, it_end = children->end(), end = it_end; // final_group will be used to prevent searching for the position // of the children once one has been found to be at the end. bool final_group = false; int last_position = -1; _node_collections[type].captions.clear(); if (parent->count() > 0 && sorted) { // Initializes last_position with the position of the first // Item to be inserted find_child_position(parent, *it, type, last_position); end = start; do { it++; if (it != it_end) { int position = 0; if (!find_child_position(parent, *it, type, position)) { if (position != last_position) { // Assigns the captions for the group to be inserted _node_collections[type].captions.assign(start, it); group_added_nodes = parent->add_node_collection(_node_collections[type], last_position); added_nodes.insert(added_nodes.end(), group_added_nodes.begin(), group_added_nodes.end()); // Start is reset to the iterator position start = it; last_position = position == -1 ? position : position + (int)_node_collections[type].captions.size(); // Shortcut to quit once an item has been found to be located // At the end of the list final_group = (last_position == -1); } } } // All the time end will advance with the iterator end = it; } while (it != it_end && !final_group); } // Inserts the last group of nodes... end = children->end(); _node_collections[type].captions.assign(start, end); if (!_node_collections[type].captions.empty()) { // positions the index on the previous position (last in group) group_added_nodes = parent->add_node_collection(_node_collections[type], last_position); added_nodes.insert(added_nodes.end(), group_added_nodes.begin(), group_added_nodes.end()); } for (std::size_t index = 0; index < added_nodes.size(); index++) { setup_node(added_nodes[index], type); added = true; } } ret_val = (added || removed); std::string icon = get_node_icon_path(type); //_model_view->thaw_refresh(); Cannot be called from a background thread. } return ret_val; } std::string LiveSchemaTree::get_field_description(const mforms::TreeNodeRef& node) { std::string text; mforms::TreeNodeRef temp_node = node; try { while (temp_node && !text.length()) { LSTData* pdata = dynamic_cast<LSTData*>(temp_node->get_data()); if (pdata) { ObjectType type = pdata->get_type(); if (is_object_type(TableOrView, type)) { short fetch_mask = (type == Table) ? COLUMN_DATA | INDEX_DATA : COLUMN_DATA; load_table_details(type, get_schema_name(node), temp_node->get_string(0), fetch_mask); } else if (is_object_type(RoutineObject, type)) { load_routine_details(temp_node); } text = pdata->get_details(true, temp_node); } else temp_node = temp_node->get_parent(); } } catch (std::exception& e) { text = _("Unable to retrieve node description."); logError("Exception retrieving node description : %s\n", strfmt("%s", e.what()).c_str()); } return text; } void LiveSchemaTree::set_notify_on_reload(const mforms::TreeNodeRef& node) { mforms::TreeNodeRef temp_node = node; LSTData* pdata = NULL; while (temp_node && !pdata) { pdata = dynamic_cast<LSTData*>(temp_node->get_data()); if (pdata) notify_on_reload_data = pdata; else temp_node = temp_node->get_parent(); } } void LiveSchemaTree::notify_on_reload(const mforms::TreeNodeRef& node) { mforms::TreeNodeRef temp_node = node; LSTData* pdata = NULL; while (temp_node && !pdata) { pdata = dynamic_cast<LSTData*>(temp_node->get_data()); if (pdata && pdata == notify_on_reload_data && pdata->is_update_complete()) _model_view->changed(); else temp_node = temp_node->get_parent(); } } void LiveSchemaTree::set_active_schema(const std::string& schema) { mforms::TreeNodeTextAttributes attrs; if (_model_view) { mforms::TreeNodeRef current_active = get_child_node(_model_view->root_node(), _active_schema); mforms::TreeNodeRef new_active = get_child_node(_model_view->root_node(), schema); if (current_active) { std::string name = current_active->get_string(0); current_active->set_string(0, name); current_active->set_attributes(0, attrs); } if (new_active) { std::string name = new_active->get_string(0); attrs.bold = true; new_active->set_string(0, name); new_active->set_attributes(0, attrs); new_active->expand(); } } _active_schema = schema; if (_base) _base->set_active_schema(schema); } void LiveSchemaTree::update_live_object_state(ObjectType type, const std::string& schema_name, const std::string& old_obj_name, const std::string& new_obj_name) { if (_model_view) { mforms::TreeNodeRef schema_node; bool created = old_obj_name.empty() && !new_obj_name.empty(); bool deleted = !old_obj_name.empty() && new_obj_name.empty(); bool changed = !old_obj_name.empty() && !new_obj_name.empty(); if (type == Schema) { if (created) insert_node(_model_view->root_node(), new_obj_name, type); else if (deleted) { schema_node = get_child_node(_model_view->root_node(), old_obj_name); if (schema_node) schema_node->remove_from_parent(); } } else { schema_node = get_child_node(_model_view->root_node(), schema_name); if (schema_node) { mforms::TreeNodeRef object_node; if (!created) { switch (type) { case Table: object_node = this->get_child_node(schema_node->get_child(TABLES_NODE_INDEX), old_obj_name); break; case View: object_node = this->get_child_node(schema_node->get_child(VIEWS_NODE_INDEX), old_obj_name); break; case Procedure: object_node = this->get_child_node(schema_node->get_child(PROCEDURES_NODE_INDEX), old_obj_name, type); break; case Function: object_node = this->get_child_node(schema_node->get_child(FUNCTIONS_NODE_INDEX), old_obj_name, type); break; default: break; } } if (changed && object_node) { if (old_obj_name != new_obj_name) object_node->set_string(0, new_obj_name); // As the object has changed we trigger a reload on // Its content reload_object_data(object_node); } else { if (created) { mforms::TreeNodeRef parent_node; int target_position = 0; switch (type) { case Table: if (!find_child_position(schema_node->get_child(TABLES_NODE_INDEX), new_obj_name, type, target_position)) parent_node = schema_node->get_child(TABLES_NODE_INDEX); break; case View: if (!find_child_position(schema_node->get_child(VIEWS_NODE_INDEX), new_obj_name, type, target_position)) parent_node = schema_node->get_child(VIEWS_NODE_INDEX); break; case Procedure: if (!find_child_position(schema_node->get_child(PROCEDURES_NODE_INDEX), new_obj_name, type, target_position)) parent_node = schema_node->get_child(PROCEDURES_NODE_INDEX); break; case Function: if (!find_child_position(schema_node->get_child(FUNCTIONS_NODE_INDEX), new_obj_name, type, target_position)) parent_node = schema_node->get_child(FUNCTIONS_NODE_INDEX); break; default: break; } if (parent_node) insert_node(parent_node, new_obj_name, type); } else { if (object_node) object_node->remove_from_parent(); } } } } } } void LiveSchemaTree::schema_contents_arrived(const std::string& schema_name, base::StringListPtr tables, base::StringListPtr views, base::StringListPtr procedures, base::StringListPtr functions, bool just_append) { if (_base) { _base->schema_contents_arrived(schema_name, tables, views, procedures, functions, just_append); filter_data(); } else { if (_model_view) { mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name); if (schema_node) { mforms::TreeNodeRef tables_node = schema_node->get_child(TABLES_NODE_INDEX); mforms::TreeNodeRef views_node = schema_node->get_child(VIEWS_NODE_INDEX); mforms::TreeNodeRef procedures_node = schema_node->get_child(PROCEDURES_NODE_INDEX); mforms::TreeNodeRef functions_node = schema_node->get_child(FUNCTIONS_NODE_INDEX); SchemaData* pdata = dynamic_cast<SchemaData*>(schema_node->get_data()); // When an error occurred all the incoming lists are NULL if (tables && views && procedures && functions) { int old_table_count = tables_node->count(); int old_view_count = tables_node->count(); // We need to duplicate the data, because it's being changed inside update_node_children // and we can't do this because it's shared between threads update_node_children(tables_node, std::make_shared<StringList>(*tables), Table, true, just_append); update_node_children(views_node, std::make_shared<StringList>(*views), View, true, just_append); update_node_children(procedures_node, std::make_shared<StringList>(*procedures), Procedure, true, just_append); update_node_children(functions_node, std::make_shared<StringList>(*functions), Function, true, just_append); // If there were nodes that means this is a refresh, in such case loaded tables // must be reloaded so the changes are displayed if (old_table_count) { for (std::size_t index = 0; index < (std::size_t)tables_node->count(); index++) { mforms::TreeNodeRef pnode = tables_node->get_child((int)index); reload_object_data(pnode); } } // If there were nodes that means this is a refresh, in such case loaded tables // must be reloaded so the changes are displayed if (old_view_count) { for (std::size_t index = 0; index < (std::size_t)views_node->count(); index++) { mforms::TreeNodeRef pnode = views_node->get_child((int)index); reload_object_data(pnode); } } if (!just_append) pdata->fetched = true; tables_node->set_string(0, TABLES_CAPTION); views_node->set_string(0, VIEWS_CAPTION); procedures_node->set_string(0, PROCEDURES_CAPTION); functions_node->set_string(0, FUNCTIONS_CAPTION); } // This section will be reached whenever there´s an exception loading the schema data else { tables_node->set_string(0, TABLES_CAPTION + " " + ERROR_FETCHING_CAPTION); views_node->set_string(0, VIEWS_CAPTION + " " + ERROR_FETCHING_CAPTION); procedures_node->set_string(0, PROCEDURES_CAPTION + " " + ERROR_FETCHING_CAPTION); functions_node->set_string(0, FUNCTIONS_CAPTION + " " + ERROR_FETCHING_CAPTION); } pdata->fetching = false; update_node_icon(schema_node); } } } } void LiveSchemaTree::load_table_details(ObjectType object_type, const std::string schema_name, const std::string object_name, int fetch_mask) { if (_model_view) { mforms::TreeNodeRef node; // If object type is unknown there's no need to pull the // object from the tree if (object_type != Any) node = get_node_for_object(schema_name, object_type, object_name); if (node) load_table_details(node, fetch_mask); else fetch_table_details(object_type, schema_name, object_name, fetch_mask); } } void LiveSchemaTree::load_table_details(mforms::TreeNodeRef& node, int fetch_mask) { ViewData* pdata = dynamic_cast<ViewData*>(node->get_data()); if (pdata) { short loaded_mask = pdata->get_loaded_mask(); short loading_mask = pdata->get_loading_mask(); // Calculates what needs to be loaded based on what was requested and what is already loaded short data_load_flags = (fetch_mask ^ loaded_mask) & fetch_mask; // Calculates what needs to be loaded based on what was left on the previous check and what is already being loaded data_load_flags = (data_load_flags ^ loading_mask) & data_load_flags; if (data_load_flags) { pdata->set_loading_mask(data_load_flags); std::string schema_name = get_schema_name(node); fetch_table_details(pdata->get_type(), schema_name, node->get_string(0), data_load_flags); } } } void LiveSchemaTree::fetch_table_details(ObjectType object_type, const std::string schema_name, const std::string object_name, int fetch_mask) { std::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock(); if (delegate) { delegate->fetch_object_details( schema_name, object_name, object_type, fetch_mask, std::bind(&LiveSchemaTree::update_node_children, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); } } void LiveSchemaTree::load_routine_details(mforms::TreeNodeRef& node) { ObjectData* pdata = dynamic_cast<ObjectData*>(node->get_data()); if (pdata && !pdata->fetched && !pdata->fetching) { pdata->fetching = true; std::string schema_name = get_schema_name(node); std::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock(); if (delegate) delegate->fetch_routine_details(schema_name, node->get_string(0), pdata->get_type()); } } void LiveSchemaTree::load_data_for_filter(const std::string& schema_filter, const std::string& object_filter) { if (std::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock()) { std::string remote_schema_filter = get_filter_wildcard(schema_filter, RemoteLike); std::string remote_object_filter = get_filter_wildcard(object_filter, RemoteLike); delegate->fetch_data_for_filter( remote_schema_filter, remote_object_filter, std::bind(&LiveSchemaTree::schema_contents_arrived, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); } } void LiveSchemaTree::load_schema_content(mforms::TreeNodeRef& schema_node) { SchemaData* data = dynamic_cast<SchemaData*>(schema_node->get_data()); if (!data->fetched && !data->fetching) { data->fetching = true; std::string name = schema_node->get_string(0); if (_base) { mforms::TreeNodeRef base_schema_node = _base->get_node_from_path(get_node_path(schema_node)); base_schema_node->get_child(TABLES_NODE_INDEX)->set_string(0, TABLES_CAPTION + " " + FETCHING_CAPTION); base_schema_node->get_child(VIEWS_NODE_INDEX)->set_string(0, VIEWS_CAPTION + " " + FETCHING_CAPTION); base_schema_node->get_child(PROCEDURES_NODE_INDEX)->set_string(0, PROCEDURES_CAPTION + " " + FETCHING_CAPTION); base_schema_node->get_child(FUNCTIONS_NODE_INDEX)->set_string(0, FUNCTIONS_CAPTION + " " + FETCHING_CAPTION); } schema_node->get_child(TABLES_NODE_INDEX)->set_string(0, TABLES_CAPTION + " " + FETCHING_CAPTION); schema_node->get_child(VIEWS_NODE_INDEX)->set_string(0, VIEWS_CAPTION + " " + FETCHING_CAPTION); schema_node->get_child(PROCEDURES_NODE_INDEX)->set_string(0, PROCEDURES_CAPTION + " " + FETCHING_CAPTION); schema_node->get_child(FUNCTIONS_NODE_INDEX)->set_string(0, FUNCTIONS_CAPTION + " " + FETCHING_CAPTION); update_node_icon(schema_node); if (std::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock()) { delegate->fetch_schema_contents( name, std::bind(&LiveSchemaTree::schema_contents_arrived, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); } } } mforms::TreeNodeRef LiveSchemaTree::get_node_for_object(const std::string& schema_name, ObjectType type, const std::string& name) { mforms::TreeNodeRef object_node = mforms::TreeNodeRef(); if (_model_view) { mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name); if (schema_node) { if (type == Schema) object_node = schema_node; else { switch (type) { case Table: object_node = get_child_node(schema_node->get_child(TABLES_NODE_INDEX), name); break; case View: object_node = get_child_node(schema_node->get_child(VIEWS_NODE_INDEX), name); break; case Procedure: object_node = get_child_node(schema_node->get_child(PROCEDURES_NODE_INDEX), name, type); break; case Function: object_node = get_child_node(schema_node->get_child(FUNCTIONS_NODE_INDEX), name, type); break; default: break; } } } } return object_node; } mforms::TreeNodeRef LiveSchemaTree::create_node_for_object(const std::string& schema_name, ObjectType type, const std::string& name) { bool created_schema = false; mforms::TreeNodeRef object_node = mforms::TreeNodeRef(); mforms::TreeNodeRef parent_node = mforms::TreeNodeRef(); if (_model_view) { mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name); // Creates the schema if doesnt exist... if (!schema_node) { schema_node = insert_node(_model_view->root_node(), schema_name, Schema); created_schema = true; } switch (type) { case Table: parent_node = schema_node->get_child(TABLES_NODE_INDEX); break; case View: parent_node = schema_node->get_child(VIEWS_NODE_INDEX); break; case Procedure: parent_node = schema_node->get_child(PROCEDURES_NODE_INDEX); break; case Function: parent_node = schema_node->get_child(FUNCTIONS_NODE_INDEX); break; default: break; } if (parent_node) object_node = insert_node(parent_node, name, type); else if (created_schema) schema_node->remove_from_parent(); } return object_node; } grt::BaseListRef LiveSchemaTree::get_selected_objects() { grt::ListRef<db_query_LiveDBObject> selection(true); if (_model_view) { std::list<mforms::TreeNodeRef> selnodes(_model_view->get_selection()); db_query_LiveDBObjectRef table_object; std::list<mforms::TreeNodeRef>::const_iterator index, end = selnodes.end(); for (index = selnodes.begin(); index != end; index++) { db_query_LiveDBObjectRef obj(grt::Initialized); mforms::TreeNodeRef node = *index; LSTData* pdata = dynamic_cast<LSTData*>(node->get_data()); ObjectType current_type = Any; if (pdata) current_type = pdata->get_type(); else { if (node->get_tag() == TABLES_TAG) current_type = TableCollection; else if (node->get_tag() == VIEWS_TAG) current_type = ViewCollection; else if (node->get_tag() == PROCEDURES_TAG) current_type = ProcedureCollection; else if (node->get_tag() == FUNCTIONS_TAG) current_type = FunctionCollection; else if (node->get_tag() == COLUMNS_TAG) current_type = ColumnCollection; else if (node->get_tag() == INDEXES_TAG) current_type = IndexCollection; else if (node->get_tag() == TRIGGERS_TAG) current_type = TriggerCollection; else if (node->get_tag() == FOREIGN_KEYS_TAG) current_type = ForeignKeyCollection; } // the selection list is always sorted top elements first, so we now for sure that // if a node and one of its subnodes are selected, the parent node will always come // first in the list switch (current_type) { case Any: case NoneType: break; case Schema: obj->type("db.Schema"); obj->schemaName(node->get_string(0)); obj->name(node->get_string(0)); break; case Table: obj->type("db.Table"); obj->schemaName(node->get_parent()->get_parent()->get_string(0)); obj->name(node->get_string(0)); table_object = obj; break; case TableColumn: case Trigger: case Index: case ForeignKey: obj->schemaName(node->get_parent()->get_parent()->get_parent()->get_parent()->get_string(0)); if (!table_object.is_valid() || table_object->schemaName() != obj->schemaName() || table_object->name() != node->get_parent()->get_parent()->get_string(0)) { table_object = db_query_LiveDBObjectRef(grt::Initialized); table_object->type("db.Table"); table_object->schemaName(obj->schemaName()); table_object->name(node->get_parent()->get_parent()->get_string(0)); } if (current_type == TableColumn) obj->type("db.Column"); else if (current_type == Trigger) obj->type("db.Trigger"); else if (current_type == Index) obj->type("db.Index"); else if (current_type == ForeignKey) obj->type("db.ForeignKey"); obj->owner(table_object); obj->name(node->get_string(0)); break; case View: obj->type("db.View"); obj->schemaName(node->get_parent()->get_parent()->get_string(0)); obj->name(node->get_string(0)); table_object = obj; break; case ViewColumn: obj->schemaName(node->get_parent()->get_parent()->get_parent()->get_string(0)); if (!table_object.is_valid() || table_object->schemaName() != obj->schemaName() || table_object->name() != node->get_parent()->get_string(0)) { table_object = db_query_LiveDBObjectRef(grt::Initialized); table_object->type("db.View"); table_object->schemaName(obj->schemaName()); table_object->name(node->get_parent()->get_string(0)); } obj->type("db.Column"); obj->owner(table_object); obj->name(node->get_string(0)); break; case Procedure: obj->type("db.StoredProcedure"); obj->schemaName(node->get_parent()->get_parent()->get_string(0)); obj->name(node->get_string(0)); break; case Function: obj->type("db.Function"); obj->schemaName(node->get_parent()->get_parent()->get_string(0)); obj->name(node->get_string(0)); break; case TableCollection: obj->schemaName(node->get_parent()->get_string(0)); obj->type("tables"); obj->name(""); break; case ViewCollection: obj->schemaName(node->get_parent()->get_string(0)); obj->type("views"); obj->name(""); break; case ProcedureCollection: obj->schemaName(node->get_parent()->get_string(0)); obj->type("storedProcedures"); obj->name(""); break; case FunctionCollection: obj->schemaName(node->get_parent()->get_string(0)); obj->type("functions"); obj->name(""); break; case ColumnCollection: case IndexCollection: case TriggerCollection: case ForeignKeyCollection: obj->schemaName(node->get_parent()->get_parent()->get_parent()->get_string(0)); if (!table_object.is_valid() || table_object->schemaName() != obj->schemaName() || table_object->name() != node->get_parent()->get_parent()->get_string(0)) { table_object = db_query_LiveDBObjectRef(grt::Initialized); table_object->type("db.Table"); table_object->schemaName(obj->schemaName()); table_object->name(node->get_parent()->get_string(0)); } obj->owner(table_object); if (current_type == ForeignKeyCollection) obj->type("foreignKeys"); else if (current_type == TriggerCollection) obj->type("triggers"); else if (current_type == IndexCollection) obj->type("indexes"); else if (current_type == ColumnCollection) obj->type("columns"); break; case ForeignKeyColumn: case IndexColumn: break; } if (obj->type() != "") selection.insert(obj); } } return selection; } bec::MenuItemList LiveSchemaTree::get_popup_items_for_nodes(const std::list<mforms::TreeNodeRef>& nodes) { bec::MenuItemList items; { mforms::TreeNodeRef node; if (nodes.size()) node = nodes.front(); int type = -1; if (node) { LSTData* pdata = dynamic_cast<LSTData*>(node->get_data()); if (pdata) type = pdata->get_type(); } if (type == Schema) { bec::MenuItem active_schema_item; active_schema_item.caption = _("Set as Default Schema"); active_schema_item.internalName = "set_active_schema"; active_schema_item.accessibilityName = "Set Active Schema"; active_schema_item.enabled = nodes.size() == 1; bec::MenuItem filter_schema_item; filter_schema_item.caption = _("Filter to This Schema"); filter_schema_item.internalName = "filter_schema"; filter_schema_item.accessibilityName = "Filter Schema"; filter_schema_item.enabled = nodes.size() == 1; items.push_back(active_schema_item); items.push_back(filter_schema_item); bec::MenuItem item; item.type = MenuSeparator; item.internalName = "builtins_separator"; // this indicates where plugins should start adding their menu items item.accessibilityName = "Separator"; items.push_back(item); } else if (type == Table || type == View || type == TableColumn || type == ViewColumn || type == ViewCollection || type == ColumnCollection) { bec::MenuItem view_item; { std::string caption = _("Select Rows"); { DictRef options = DictRef::cast_from(grt::GRT::get()->get("/wb/options/options")); bool limit_rows = (0 != options.get_int("SqlEditor:LimitRows")); ssize_t limit_rows_count = options.get_int("SqlEditor:LimitRowsCount"); if (limit_rows && (limit_rows_count <= 0)) limit_rows = false; if (limit_rows) caption += _(" - Limit ") + std::to_string(limit_rows_count); } view_item.caption = caption; } view_item.internalName = "select_data"; view_item.accessibilityName = "Select Data"; view_item.enabled = !nodes.empty() && (nodes.size() == 1 || (type == TableColumn || type == ViewColumn)); items.push_back(view_item); bec::MenuItem item; item.type = MenuSeparator; item.internalName = "builtins_separator"; // this indicates where plugins should start adding their menu items item.accessibilityName = "Separator"; items.push_back(item); } } { bec::MenuItem item; item.type = MenuSeparator; item.internalName = "bottom_plugins_separator"; item.accessibilityName = "Separator"; // this indicates where plugins should start adding their menu items items.push_back(item); item.type = MenuAction; item.caption = _("Refresh All"); item.internalName = "refresh"; item.accessibilityName = "Refresh"; items.push_back(item); } return items; } bool LiveSchemaTree::activate_popup_item_for_nodes(const std::string& name, const std::list<mforms::TreeNodeRef>& unsorted_nodes) { std::vector<ChangeRecord> changes; mforms::TreeNodeRef pnode; std::string schema_name = ""; std::string object_name = ""; std::string object_detail = ""; if (std::shared_ptr<Delegate> delegate = _delegate.lock()) { if (name == "refresh") { delegate->tree_refresh(); return true; } else if (name == "set_active_schema") { pnode = unsorted_nodes.front(); ChangeRecord record = {Schema, "", pnode->get_string(0), ""}; changes.push_back(record); delegate->tree_activate_objects("activate", changes); return true; } else if (name == "filter_schema") { pnode = unsorted_nodes.front(); ChangeRecord record = {Schema, "", pnode->get_string(0), ""}; changes.push_back(record); delegate->tree_activate_objects("filter", changes); return true; } else if (name == "select_data") { std::list<mforms::TreeNodeRef>::const_iterator index, end = unsorted_nodes.end(); mforms::TreeNodeRef pnode; LSTData* pdata; std::string schema_name = ""; std::string object_name = ""; std::string object_detail = ""; ObjectType type = Any; bool use_columns = false; for (index = unsorted_nodes.begin(); index != end; index++) { pnode = (*index); pdata = dynamic_cast<LSTData*>(pnode->get_data()); if (pdata) { schema_name = ""; object_name = ""; object_detail = ""; type = pdata->get_type(); schema_name = get_schema_name(pnode); if (is_object_type(SchemaObject, type)) object_name = pnode->get_string(0); else { if (pdata->get_type() == TableColumn) { object_name = pnode->get_parent()->get_parent()->get_string(0); object_detail = pnode->get_string(0); type = Table; use_columns = true; } else if (pdata->get_type() == ViewColumn) { object_name = pnode->get_parent()->get_string(0); object_detail = pnode->get_string(0); type = View; use_columns = true; } } if (object_name.length() > 0) { ChangeRecord record = {type, schema_name, object_name, object_detail}; changes.push_back(record); } } else { if (pnode->get_tag() == COLUMNS_TAG) { int size = pnode->count(); mforms::TreeNodeRef child; schema_name = get_schema_name(pnode); object_name = pnode->get_parent()->get_string(0); for (int index = 0; index < size; index++) { child = pnode->get_child(index); ChangeRecord record = {Table, schema_name, object_name, child->get_string(0)}; changes.push_back(record); } } } } if (use_columns) delegate->tree_activate_objects(name + "_columns", changes); else delegate->tree_activate_objects(name, changes); return true; } else return delegate->sidebar_action(name); } return false; } bool LiveSchemaTree::is_schema_contents_enabled() const { return _is_schema_contents_enabled; } void LiveSchemaTree::is_schema_contents_enabled(bool value) { _is_schema_contents_enabled = value; } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::set_no_connection() { _model_view->clear(); mforms::TreeNodeRef node = _model_view->add_node(); node->set_string(0, "Not connected"); } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::set_enabled(bool enabled) { _model_view->set_enabled(enabled); } //-------------------------------------------------------------------------------------------------- std::string LiveSchemaTree::get_filter_wildcard(const std::string& filter, FilterType type) { std::string wildcard = filter; if (filter.length() == 0) wildcard = "*"; switch (type) { case LocalRegexp: case LocalLike: case RemoteRegexp: if ('*' != wildcard.at(wildcard.length() - 1)) wildcard += "*"; break; case RemoteLike: base::replaceStringInplace(wildcard, "%", "\\%"); base::replaceStringInplace(wildcard, "_", "\\_"); base::replaceStringInplace(wildcard, "?", "_"); base::replaceStringInplace(wildcard, "*", "%"); if ('%' != wildcard.at(wildcard.length() - 1)) wildcard += "%"; break; } return wildcard; } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::filter_data() { _enabled_events = false; // Removes all the objects on the target tree _model_view->clear(); mforms::TreeNodeRef base_root = _base->_model_view->root_node(); mforms::TreeNodeRef this_root = _model_view->root_node(); filter_children(Schema, base_root, this_root, _schema_pattern); // To keep the active schema on the filtered tree set_active_schema(_base->_active_schema); _enabled_events = true; } //-------------------------------------------------------------------------------------------------- /* * filter_children_collection: will trigger a children copy for the collection nodes of the given source * right now the pattern is only used for nodes on schema collections */ void LiveSchemaTree::filter_children_collection(mforms::TreeNodeRef& source, mforms::TreeNodeRef& target) { LSTData* pdata = dynamic_cast<LSTData*>(source->get_data()); if (pdata) { mforms::TreeNodeRef source_collection; mforms::TreeNodeRef target_collection; switch (pdata->get_type()) { case Schema: { source_collection = source->get_child(TABLES_NODE_INDEX); target_collection = target->get_child(TABLES_NODE_INDEX); bool found_tables = filter_children(Table, source_collection, target_collection, _object_pattern); source_collection = source->get_child(VIEWS_NODE_INDEX); target_collection = target->get_child(VIEWS_NODE_INDEX); bool found_views = filter_children(View, source_collection, target_collection, _object_pattern); source_collection = source->get_child(PROCEDURES_NODE_INDEX); target_collection = target->get_child(PROCEDURES_NODE_INDEX); bool found_procedures = filter_children(Procedure, source_collection, target_collection, _object_pattern); source_collection = source->get_child(FUNCTIONS_NODE_INDEX); target_collection = target->get_child(FUNCTIONS_NODE_INDEX); bool found_functions = filter_children(Function, source_collection, target_collection, _object_pattern); if (_object_pattern && !(found_tables || found_views || found_procedures || found_functions)) target->remove_from_parent(); } break; case Table: source_collection = source->get_child(TABLE_COLUMNS_NODE_INDEX); target_collection = target->get_child(TABLE_COLUMNS_NODE_INDEX); filter_children(TableColumn, source_collection, target_collection); source_collection = source->get_child(TABLE_INDEXES_NODE_INDEX); target_collection = target->get_child(TABLE_INDEXES_NODE_INDEX); filter_children(Index, source_collection, target_collection); source_collection = source->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX); target_collection = target->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX); filter_children(ForeignKey, source_collection, target_collection); source_collection = source->get_child(TABLE_TRIGGERS_NODE_INDEX); target_collection = target->get_child(TABLE_TRIGGERS_NODE_INDEX); filter_children(Trigger, source_collection, target_collection); break; case View: filter_children(ViewColumn, source, target); break; default: break; } } } /* * filter_children: will create duplicate objects in target for the children in source matching the given pattern * if no pattern is specified, all the children will be cuplicated */ bool LiveSchemaTree::filter_children(ObjectType type, mforms::TreeNodeRef& source, mforms::TreeNodeRef& target, GPatternSpec* pattern) { // Validation to occur only on schema child objects if a pattern is set bool validate = is_object_type(DatabaseObject, type) && pattern; // Clears the collection... target->remove_children(); int count = source->count(); for (int index = 0; index < count; index++) { mforms::TreeNodeRef source_node = source->get_child(index); // #ifdef GLIB_VERSION_2_70 won't work in RHEL9/OL9 because the glib-2.68 package already contains this definition #ifdef g_pattern_spec_match_string bool match_string = g_pattern_spec_match_string(pattern, base::toupper(source_node->get_string(0)).c_str()); #else bool match_string = g_pattern_match_string(pattern, base::toupper(source_node->get_string(0)).c_str()); #endif if (!validate || match_string) { std::vector<mforms::TreeNodeRef> group_added_nodes; _node_collections[type].captions.clear(); _node_collections[type].captions.push_back(source_node->get_string(0)); group_added_nodes = target->add_node_collection(_node_collections[type]); setup_node(group_added_nodes[0], type, source_node->get_data(), true); // For each found node, continues with their children... if (type == Schema || type == Table || type == View) filter_children_collection(source_node, group_added_nodes[0]); if (source_node->is_expanded()) group_added_nodes[0]->expand(); else group_added_nodes[0]->collapse(); } } if (source->is_expanded() != target->is_expanded()) { if (source->is_expanded()) target->expand(); else target->collapse(); } return target->count() > 0; } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::clean_filter() { if (_filter.length() > 0) { _filter_type = Any; _filter = ""; g_pattern_spec_free(_schema_pattern); _schema_pattern = NULL; if (_object_pattern) { g_pattern_spec_free(_object_pattern); _object_pattern = NULL; } } } void LiveSchemaTree::set_filter(std::string filter) { // Cleans the previous filter if any... clean_filter(); if (filter.length() > 0) { _filter = filter; std::vector<std::string> filters = base::split(_filter, ".", 2); // Gets the filter wildcard strings std::string schema_filter = base::toupper(get_filter_wildcard(filters[0], LocalLike)); std::string object_filter = base::toupper(get_filter_wildcard(filters.size() > 1 ? filters[1] : "", LocalLike)); _schema_pattern = g_pattern_spec_new(schema_filter.c_str()); if (filters.size() > 1 && object_filter != "*") _object_pattern = g_pattern_spec_new(object_filter.c_str()); } } void LiveSchemaTree::set_model_view(mforms::TreeView* target) { _model_view = target; if (_model_view) { scoped_connect(_model_view->signal_expand_toggle(), std::bind(&LiveSchemaTree::expand_toggled, this, std::placeholders::_1, std::placeholders::_2)); scoped_connect(_model_view->signal_node_activated(), std::bind(&LiveSchemaTree::node_activated, this, std::placeholders::_1, std::placeholders::_2)); _model_view->set_row_overlay_handler( std::bind(&LiveSchemaTree::overlay_icons_for_tree_node, this, std::placeholders::_1)); } } /* Function : binary_search_node * Description : Search a child on a given node based on it's name, assumes the children * are sorted * Parameters : parent, the node where the search is being performed * min, max : usual parameters on the binary search * name : the name of the child node being searched * type : the type of the child node to be searched (not being used) * position : output parameter to store: * - If the node is found, the position in parent's list * - If not found, the position where it should be located if it is going to be added * Return Value : if found, the child node */ mforms::TreeNodeRef LiveSchemaTree::binary_search_node(const mforms::TreeNodeRef& parent, int min, int max, const std::string& name, ObjectType type, int& position) { if (max < min) return mforms::TreeNodeRef(); else { int middle = (max + min) / 2; position = middle; mforms::TreeNodeRef node = parent->get_child(middle); int comparison = base::string_compare(node->get_string(0), name, _case_sensitive_identifiers); if (comparison < 0) return binary_search_node(parent, middle + 1, max, name, type, ++position); else if (comparison > 0) return binary_search_node(parent, min, middle - 1, name, type, position); else return node; } } /* Function : get_child_node * Description : Searches a specific child on a given node, supports both sequential search * and binary search. Binary search should be used when searching for schemas, * tables, views and routines which are sorted. Sequential search is there for * the non sorted nodes. */ mforms::TreeNodeRef LiveSchemaTree::get_child_node(const mforms::TreeNodeRef& parent, const std::string& name, ObjectType type, bool binary_search) { int last_position = 0; bool found = false; mforms::TreeNodeRef child; if (binary_search) { if (parent && parent->count()) child = binary_search_node(parent, 0, parent->count() - 1, name, type, last_position); if (child) found = true; } else { if (parent && parent->count()) { for (int index = 0; !found && index < parent->count(); index++) { child = parent->get_child(index); found = (base::string_compare(child->get_string(0), name, _case_sensitive_identifiers) == 0); if (found && type != Any) { LSTData* pdata = dynamic_cast<LSTData*>(child->get_data()); found = (pdata && type == pdata->get_type()); } } } } return found ? child : mforms::TreeNodeRef(); } /* Function : find_child_position * Description : given a child name, searches it on the received parent. This function is * intended for usage on sorted collections of nodes * Parameters : parent, the node where the search will occur * type, the type of the object being searched * position, output parameter containing: * - If found, the index of the found node * - If not found, the position where the node should be (i.e. to add it there) * Return value : boolean value indicating whether the node was found or not */ bool LiveSchemaTree::find_child_position(const mforms::TreeNodeRef& parent, const std::string& name, ObjectType type, int& position) { mforms::TreeNodeRef child; position = 0; if (parent && parent->count()) child = binary_search_node(parent, 0, parent->count() - 1, name, type, position); if (parent->count() == position) position = -1; return child ? true : false; } void LiveSchemaTree::update_schemata(base::StringListPtr schema_list) { mforms::TreeNodeRef schema_node; if (_model_view) { mforms::TreeNodeRef root = _model_view->root_node(); if (root && root->count() > 0 && !root->get_child(0)->get_data()) { // the tree was in no-connection mode _model_view->clear(); root = _model_view->root_node(); } schema_list->sort( std::bind(base::stl_string_compare, std::placeholders::_1, std::placeholders::_2, _case_sensitive_identifiers)); update_node_children(root, schema_list, Schema, true); // Re-sets the active schema at view level if (_active_schema.length()) set_active_schema(_active_schema); int total_schemas = root->count(); for (int index = 0; index < total_schemas; index++) { schema_node = root->get_child(index); SchemaData* data = dynamic_cast<SchemaData*>(schema_node->get_data()); if (data->fetched) { data->fetched = false; if (schema_node->is_expanded()) load_schema_content(schema_node); } } } } void LiveSchemaTree::expand_toggled(mforms::TreeNodeRef node, bool value) { if (_enabled_events) { LSTData* node_data = dynamic_cast<LSTData*>(node->get_data()); if (value) { if (node_data) { switch (node_data->get_type()) { case Schema: load_schema_content(node); break; case Table: load_table_details(node, COLUMN_DATA | INDEX_DATA); break; case View: { load_table_details(node, COLUMN_DATA); ViewData* pdata = dynamic_cast<ViewData*>(node->get_data()); if (pdata->columns_load_error) { node->remove_children(); update_node_icon(node); } } break; default: break; } } else { std::string node_tag = node->get_tag(); mforms::TreeNodeRef parent = node->get_parent(); if (node_tag == TRIGGERS_TAG) load_table_details(parent, TRIGGER_DATA); else if (node_tag == FOREIGN_KEYS_TAG) load_table_details(parent, FK_DATA); } } // If there's a base tree the expansion state needs to be propagated to that tree // Events should be disabled there so only the expand state will be propagated if (_base) { std::vector<std::string> path = get_node_path(node); mforms::TreeNodeRef base_node = _base->get_node_from_path(path); if (value) base_node->expand(); else base_node->collapse(); } } } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::node_activated(mforms::TreeNodeRef node, int column) { LSTData* node_data = dynamic_cast<LSTData*>(node->get_data()); if (node_data) { std::string node_name = node->get_string(0); switch (node_data->get_type()) { case Schema: { std::vector<ChangeRecord> changes; ChangeRecord record = {Schema, "", node_name, ""}; changes.push_back(record); if (std::shared_ptr<Delegate> delegate = _delegate.lock()) { switch (column) { case -1: delegate->tree_activate_objects("inspect", changes); break; case -2: delegate->tree_activate_objects("alter", changes); break; default: delegate->tree_activate_objects("activate", changes); #ifndef _MSC_VER node->toggle(); #endif break; } } } break; case Table: /* fall-thru */ case View: { if (column < 0) { std::vector<ChangeRecord> changes; ChangeRecord record = {node_data->get_type(), get_schema_name(node), node_name, ""}; changes.push_back(record); if (std::shared_ptr<Delegate> delegate = _delegate.lock()) { switch (column) { case -1: delegate->tree_activate_objects("inspect", changes); break; case -2: delegate->tree_activate_objects("alter", changes); break; case -3: delegate->tree_activate_objects("select_data", changes); break; #ifndef _MSC_VER default: node->toggle(); break; #endif } } break; } } /* fall-thru */ case Procedure: /* fall-thru */ case Function: { if (column < 0) { std::vector<ChangeRecord> changes; ChangeRecord record = {node_data->get_type(), get_schema_name(node), node_name, ""}; changes.push_back(record); if (std::shared_ptr<Delegate> delegate = _delegate.lock()) { switch (column) { case -1: delegate->tree_activate_objects("alter", changes); break; case -2: delegate->tree_activate_objects("execute", changes); break; } } break; } } /* fall-thru */ default: node_name = base::quoteIdentifierIfNeeded(node_name, '`', _version); sql_editor_text_insert_signal(node_name); break; } } #ifndef _MSC_VER else node->toggle(); #endif } //-------------------------------------------------------------------------------------------------- /** * Finds the parent schema for a specific node in the tree. */ std::string LiveSchemaTree::get_schema_name(const mforms::TreeNodeRef& node) { std::string ret_val; mforms::TreeNodeRef temp_node = node; mforms::TreeNodeRef parent = temp_node->get_parent(); // Safety validation, all nodes in the LST should have a parent // Even schema nodes which it's parent is root if (parent) { while (parent->get_parent()) { temp_node = parent; parent = parent->get_parent(); } ret_val = temp_node->get_string(0); } return ret_val; } /* * get_node_path: Gets the name path to a node from root */ std::vector<std::string> LiveSchemaTree::get_node_path(const mforms::TreeNodeRef& node) { std::vector<std::string> path; mforms::TreeNodeRef temp_node = node; mforms::TreeNodeRef parent = temp_node->get_parent(); // Safety validation, all nodes in the LST should have a parent // Even schema nodes which it's parent is root if (parent) { path.insert(path.begin(), temp_node->get_string(0)); while (parent->get_parent()) { temp_node = parent; path.insert(path.begin(), temp_node->get_string(0)); parent = parent->get_parent(); } } return path; } /* * get_node_from_path: Finds a node in a tree following a name path * TODO: This will not work in the case of the routines, as there's no way to know if the path is * for a procedure or for a function, on this case a sequential search will be done so * procedures will be searched first all the time */ mforms::TreeNodeRef LiveSchemaTree::get_node_from_path(std::vector<std::string> path) { mforms::TreeNodeRef temp_node = _model_view->root_node(); std::size_t index = 0; bool error = false; bool use_binary_search = true; while (!error && index < path.size()) { temp_node = get_child_node(temp_node, path[index], Any, use_binary_search); if (temp_node && temp_node->is_valid()) { index++; // Uses binary search only on the db object collection nodes std::string tag = temp_node->get_tag(); use_binary_search = (tag == TABLES_TAG || tag == VIEWS_TAG); } else error = true; } return error ? mforms::TreeNodeRef() : temp_node; } /* * is_object_type: Validates that the type of a given object is in a specific group */ bool LiveSchemaTree::is_object_type(ObjectTypeValidation validation, ObjectType type) { switch (validation) { case DatabaseObject: return (type == Schema || type == Table || type == View || type == Procedure || type == Function); break; case SchemaObject: return (type == Table || type == View || type == Procedure || type == Function); break; case TableOrView: return (type == Table || type == View); break; case ColumnObject: return (type == TableColumn || type == ViewColumn); break; case RoutineObject: return (type == Procedure || type == Function); break; } return false; } mforms::TreeNodeRef LiveSchemaTree::insert_node(mforms::TreeNodeRef parent, const std::string& name, ObjectType type) { mforms::TreeNodeRef node; int target_position = 0; if (!find_child_position(parent, name, type, target_position)) { std::vector<mforms::TreeNodeRef> group_added_nodes; _node_collections[type].captions.clear(); _node_collections[type].captions.push_back(name); group_added_nodes = parent->add_node_collection(_node_collections[type], target_position); node = group_added_nodes[0]; setup_node(node, type); } return node; } void LiveSchemaTree::reload_object_data(mforms::TreeNodeRef& node) { ViewData* pdata = dynamic_cast<ViewData*>(node->get_data()); if (pdata) { short loaded_mask = pdata->get_loaded_mask(); if (loaded_mask) { // This was a successful update so in case the error icon was loaded // It needs to be reset if (pdata->columns_load_error) { pdata->columns_load_error = false; update_node_icon(node); } // Marks the data that is expected to be loaded on the node pdata->set_reload_mask(loaded_mask); // Identifies the node expansion state bool is_expanded = node->is_expanded(); int expanded_mask = 0; if (is_expanded && pdata->get_type() == Table) { expanded_mask |= node->get_child(TABLE_COLUMNS_NODE_INDEX)->is_expanded() ? COLUMN_DATA : 0; expanded_mask |= node->get_child(TABLE_INDEXES_NODE_INDEX)->is_expanded() ? INDEX_DATA : 0; expanded_mask |= node->get_child(TABLE_TRIGGERS_NODE_INDEX)->is_expanded() ? TRIGGER_DATA : 0; expanded_mask |= node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->is_expanded() ? FK_DATA : 0; } // Invalidates any loaded data on the object pdata->set_unloaded_data(loaded_mask); // Removes any loaded information to allow reload discard_object_data(node, loaded_mask); // Reloads the previously loaded information if the node is // expanded, if not, it will be loaded on expansion if (loaded_mask) { load_table_details(node, loaded_mask); // Triggers the expansion of the subnodes if (is_expanded) { node->expand(); if (expanded_mask) { if (expanded_mask & COLUMN_DATA) node->get_child(TABLE_COLUMNS_NODE_INDEX)->expand(); if (expanded_mask & INDEX_DATA) node->get_child(TABLE_INDEXES_NODE_INDEX)->expand(); if (expanded_mask & TRIGGER_DATA) node->get_child(TABLE_TRIGGERS_NODE_INDEX)->expand(); if (expanded_mask & FK_DATA) node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->expand(); } } } } } } //-------------------------------------------------------------------------------------------------- void LiveSchemaTree::discard_object_data(mforms::TreeNodeRef& node, int data_mask) { mforms::TreeNodeRef parent_node; if (data_mask & COLUMN_DATA) { LSTData* pdata = dynamic_cast<LSTData*>(node->get_data()); if (pdata->get_type() == Table) parent_node = node->get_child(TABLE_COLUMNS_NODE_INDEX); else parent_node = node; parent_node->remove_children(); } if (data_mask & INDEX_DATA) { parent_node = node->get_child(TABLE_INDEXES_NODE_INDEX); parent_node->remove_children(); } if (data_mask & TRIGGER_DATA) { parent_node = node->get_child(TABLE_TRIGGERS_NODE_INDEX); parent_node->remove_children(); } if (data_mask & FK_DATA) { parent_node = node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX); parent_node->remove_children(); } } //-------------------------------------------------------------------------------------------------- std::vector<std::string> LiveSchemaTree::overlay_icons_for_tree_node(mforms::TreeNodeRef node) { LSTData* data = dynamic_cast<LSTData*>(node->get_data()); std::vector<std::string> icons; if (data) { switch (data->get_type()) { case Schema: icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_inspector.png")); icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_editor.png")); break; case Table: case View: icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_inspector.png")); icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_editor.png")); icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_result.png")); break; case Procedure: case Function: icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_editor.png")); icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_execute.png")); break; default: break; } } return icons; } //--------------------------------------------------------------------------------------------------