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;
}
//--------------------------------------------------------------------------------------------------