backend/wbpublic/grtdb/editor_dbobject.cpp (353 lines of code) (raw):

/* * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is 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 "editor_dbobject.h" #include "base/util_functions.h" #include "grt/validation_manager.h" #include "grtpp_notifications.h" #include "base/string_utilities.h" #include "base/notifications.h" #include "mforms/utilities.h" #include "mforms/code_editor.h" #include "sqlide/sql_editor_be.h" #include "db_helpers.h" #include "mysql/MySQLRecognizerCommon.h" const char *DEFAULT_CHARSET_CAPTION = "Default Charset"; const char *DEFAULT_COLLATION_CAPTION = "Default Collation"; using namespace bec; using namespace parsers; //-------------------------------------------------------------------------------------------------- DBObjectEditorBE::DBObjectEditorBE(const db_DatabaseObjectRef &object) : BaseEditor(object) { _ignored_object_fields_for_ui_refresh.insert("lastChangeDate"); // Get the owning catalog. GrtObjectRef run = object; while (run.is_valid() && !run.is_instance(db_Catalog::static_class_name())) run = run->owner(); _catalog = db_CatalogRef::cast_from(run); _parserServices = MySQLParserServices::get(); bool case_sensitive = true; if (object->customData().get_int("CaseSensitive", 1) == 0) case_sensitive = false; // Assume a default version if the given catalog is incomplete. GrtVersionRef version = get_catalog()->version(); if (!version.is_valid()) version = bec::parse_version("8.0.16"); _globalSymbols = parsers::functionSymbolsForVersion(bec::versionToEnum(version)); std::string sqlMode; if (object->customData().has_key("sqlMode")) sqlMode = object->customData().get_string("sqlMode"); _parserContext = _parserServices->createParserContext(get_catalog()->characterSets(), version, sqlMode, case_sensitive); // Because syntax checks and auto completion are done in different threads we need 2 different parser contexts. _autocompletionContext = _parserServices->createParserContext(get_catalog()->characterSets(), version, sqlMode, case_sensitive); _val_notify_conn = ValidationManager::signal_notify()->connect( std::bind(&DBObjectEditorBE::notify_from_validation, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); // Get notified about version number changes. grt::GRTNotificationCenter::get()->add_grt_observer(this, "GRNPreferencesDidClose"); grt::DictRef info(true); info.gset("form", form_id()); info.set("object", object); // Must be delayed, because observer will probably need the form to be finished constructing grt::GRTNotificationCenter::get()->send_grt("GRNDBObjectEditorCreated", grt::ObjectRef(), info); } //-------------------------------------------------------------------------------------------------- DBObjectEditorBE::~DBObjectEditorBE() { grt::GRTNotificationCenter::get()->remove_grt_observer(this); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::handle_grt_notification(const std::string &name, grt::ObjectRef sender, grt::DictRef info) { if (info.get_int("saved") == 1) { if (name == "GRNPreferencesDidClose") { // We want to see changes for the server version. GrtVersionRef version = get_catalog()->version(); _parserContext->updateServerVersion(version); get_sql_editor()->setServerVersion(version); } } } //-------------------------------------------------------------------------------------------------- bool DBObjectEditorBE::is_editing_live_object() { return get_dbobject()->customData().get("liveRdbms").is_valid(); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::apply_changes_to_live_object() { BaseEditor::apply_changes_to_live_object(); if (on_apply_changes_to_live_object(this, false)) refresh_live_object(); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::refresh_live_object() { BaseEditor::refresh_live_object(); on_refresh_live_object(this); } //-------------------------------------------------------------------------------------------------- bool DBObjectEditorBE::can_close() { // Editing in a model always allows to close the editor. Save checks are done when the model // is closed. if (!is_editing_live_object()) return true; bool res = BaseEditor::can_close(); // Note: the result of the BaseEditor::can_close() is only used if there's no apply callback set. // Otherwise we always use the callback for checks (because there can be other changes than // just the code editor which is checked in the BaseEditor). if (on_apply_changes_to_live_object) { bool is_object_modified = on_apply_changes_to_live_object(this, true); if (is_object_modified) { int user_choice = mforms::Utilities::show_warning(base::strfmt(_("Object %s was changed"), get_name().c_str()), base::strfmt(_("Do you want to save changes made to %s?"), get_name().c_str()), _("Save"), _("Cancel"), _("Don't Save")); if (mforms::ResultOk == user_choice) res = on_apply_changes_to_live_object(this, false); else if (mforms::ResultCancel == user_choice) res = false; else res = true; } else res = true; } return res; } //-------------------------------------------------------------------------------------------------- bool DBObjectEditorBE::should_close_on_delete_of(const std::string &oid) { if (get_object().id() == oid) return true; db_SchemaRef schema(get_schema()); if (schema.is_valid() && schema.id() == oid) return true; return false; } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::update_change_date() { get_object().set_member("lastChangeDate", grt::StringRef(base::fmttime(0, DATETIME_FMT))); } //-------------------------------------------------------------------------------------------------- std::string DBObjectEditorBE::get_name() { return get_object()->name(); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::set_name(const std::string &name) { if (get_object()->name() != name) { RefreshUI::Blocker refresh_block(*this); AutoUndoEdit undo(this, get_dbobject(), "name"); std::string name_ = base::trim(name); get_dbobject()->name(name_); update_change_date(); undo.end(base::strfmt(_("Rename to '%s'"), name_.c_str())); } } //-------------------------------------------------------------------------------------------------- std::string DBObjectEditorBE::get_comment() { return get_dbobject()->comment(); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::set_comment(const std::string &descr) { if (get_dbobject()->comment() != descr) { RefreshUI::Blocker blocker(*this); AutoUndoEdit undo(this, get_dbobject(), "comment"); get_dbobject()->comment(descr); update_change_date(); undo.end(_("Edit Comment")); } } //-------------------------------------------------------------------------------------------------- std::string DBObjectEditorBE::get_sql() { if (db_DatabaseDdlObjectRef::can_wrap(get_object())) { db_DatabaseDdlObjectRef object = db_DatabaseDdlObjectRef::cast_from(get_object()); return object->sqlDefinition(); } return ""; } //-------------------------------------------------------------------------------------------------- /** * Called from outside to set new sql text in our editor. */ void DBObjectEditorBE::set_sql(const std::string &sql) { get_sql_editor()->sql(sql.c_str()); commit_changes(); send_refresh(); } //-------------------------------------------------------------------------------------------------- bool DBObjectEditorBE::is_sql_commented() { return (*get_dbobject()->commentedOut() != 0); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::set_sql_commented(bool flag) { RefreshUI::Blocker blocker(*this); AutoUndoEdit undo(this, get_dbobject(), "commentedOut"); get_dbobject()->commentedOut(flag ? 1 : 0); update_change_date(); undo.end(_("Comment Out SQL")); } //-------------------------------------------------------------------------------------------------- db_CatalogRef DBObjectEditorBE::get_catalog() { return _catalog; } //-------------------------------------------------------------------------------------------------- db_SchemaRef DBObjectEditorBE::get_schema() { GrtObjectRef object = get_dbobject(); while (object.is_valid() && !object.is_instance(db_Schema::static_class_name())) object = object->owner(); return db_SchemaRef::cast_from(object); } //-------------------------------------------------------------------------------------------------- std::string DBObjectEditorBE::get_schema_name() { return get_schema()->name(); } //-------------------------------------------------------------------------------------------------- db_SchemaRef DBObjectEditorBE::get_schema_with_name(const std::string &schema_name) { return grt::find_named_object_in_list(_catalog->schemata(), schema_name); } //-------------------------------------------------------------------------------------------------- std::vector<std::string> DBObjectEditorBE::get_all_schema_names() { std::vector<std::string> names; if (is_editing_live_object()) { names.push_back(get_schema()->name()); return names; } grt::ListRef<db_Schema> schema_list = _catalog->schemata(); for (size_t sc = schema_list.count(), s = 0; s < sc; s++) names.push_back(schema_list[s]->name()); return names; } //-------------------------------------------------------------------------------------------------- /** * Collects the FQN for all tables not in our own schema. */ std::vector<std::string> DBObjectEditorBE::get_all_table_names() { if (is_editing_live_object()) on_create_live_table_stubs(this); grt::ListRef<db_Schema> schema_list = _catalog->schemata(); db_SchemaRef myschema = get_schema(); std::vector<std::string> table_list; // Construct FQN for all tables. This will sort all tables within the same schema together. table_list = get_schema_table_names(); for (size_t i = 0; i < schema_list.count(); ++i) { if (schema_list[i] == myschema) continue; db_SchemaRef schema = schema_list[i]; std::string schema_name = schema_list[i]->name(); for (size_t j = 0; j < schema->tables().count(); ++j) table_list.push_back("`" + schema_name + "`.`" + *schema->tables()[j]->name() + "`"); } std::sort(table_list.begin(), table_list.end()); table_list.push_back("Specify Manually..."); return table_list; } //-------------------------------------------------------------------------------------------------- /** * Collects the FQN for all tables in our schema. */ std::vector<std::string> DBObjectEditorBE::get_schema_table_names() { db_SchemaRef schema = get_schema(); std::vector<std::string> table_list; std::string schema_name = schema->name(); if (schema.is_valid()) { for (size_t i = 0; i < schema->tables().count(); ++i) table_list.push_back("`" + schema_name + "`.`" + *schema->tables()[i]->name() + "`"); } std::sort(table_list.begin(), table_list.end()); return table_list; } //-------------------------------------------------------------------------------------------------- std::vector<std::string> DBObjectEditorBE::get_table_column_names(const std::string &fq_table_name) { db_SchemaRef schema; std::vector<std::string> columns; if (fq_table_name.empty()) return columns; std::vector<std::string> parts = base::split_qualified_identifier(fq_table_name); std::string table_name; if (parts.size() == 1) { table_name = parts[0]; schema = get_schema(); } else if (!parts.empty()) { schema = get_schema_with_name(parts[0]); table_name = parts[1]; } if (schema.is_valid()) { db_TableRef table(grt::find_named_object_in_list(schema->tables(), table_name)); if (table.is_valid()) { for (size_t c = table->columns().count(), i = 0; i < c; i++) columns.push_back(*table->columns()[i]->name()); } } return columns; } //-------------------------------------------------------------------------------------------------- std::vector<std::string> DBObjectEditorBE::get_table_column_names(const db_TableRef &table) { std::vector<std::string> columns; if (table.is_valid()) for (size_t c = table->columns().count(), i = 0; i < c; i++) columns.push_back(*table->columns()[i]->name()); return columns; } //-------------------------------------------------------------------------------------------------- std::vector<std::string> DBObjectEditorBE::get_charset_list() { std::vector<std::string> result; grt::ListRef<db_CharacterSet> charsets = _catalog->characterSets(); for (size_t j = 0; j < charsets.count(); ++j) { db_CharacterSetRef cs = charsets.get(j); std::string cs_name(cs->name().c_str()); result.push_back(cs_name); } result.push_back(DEFAULT_CHARSET_CAPTION); std::sort(result.begin(), result.end()); return result; } //-------------------------------------------------------------------------------------------------- std::vector<std::string> DBObjectEditorBE::get_charset_collation_list(const std::string &charset) { std::vector<std::string> result; grt::ListRef<db_CharacterSet> charsets = _catalog->characterSets(); for (size_t j = 0; j < charsets.count(); ++j) { db_CharacterSetRef cs = charsets.get(j); if (cs->name() != charset) continue; grt::StringListRef collations(cs->collations()); for (size_t k = 0; k < collations.count(); ++k) { result.push_back(collations.get(k)); } } result.push_back(DEFAULT_COLLATION_CAPTION); std::sort(result.begin(), result.end()); return result; } //-------------------------------------------------------------------------------------------------- std::vector<std::string> DBObjectEditorBE::get_charset_collation_list() { std::vector<std::string> collation_list; grt::ListRef<db_CharacterSet> charsets = _catalog->characterSets(); for (size_t j = 0; j < charsets.count(); ++j) { db_CharacterSetRef cs = charsets.get(j); grt::StringListRef collations(cs->collations()); std::string cs_name(cs->name().c_str()); collation_list.push_back(format_charset_collation(cs_name, "")); for (size_t k = 0; k < collations.count(); ++k) collation_list.push_back(format_charset_collation(cs_name, collations.get(k))); } return collation_list; } //-------------------------------------------------------------------------------------------------- std::string DBObjectEditorBE::format_charset_collation(const std::string &charset, const std::string &collation) { if (collation.empty()) { if (charset.empty()) return " - "; else return charset + " - " + DEFAULT_COLLATION_CAPTION; } else return charset + " - " + collation; } //-------------------------------------------------------------------------------------------------- bool DBObjectEditorBE::parse_charset_collation(const std::string &str, std::string &charset, std::string &collation) { std::string::size_type pos; if ((pos = str.find(" - ")) != std::string::npos) { charset = str.substr(0, pos); collation = str.substr(pos + 3); if (collation == DEFAULT_COLLATION_CAPTION) collation = ""; return true; } charset = ""; collation = ""; return false; } //-------------------------------------------------------------------------------------------------- bool DBObjectEditorBE::has_editor() { if (_sql_editor) return true; return false; } //-------------------------------------------------------------------------------------------------- MySQLEditor::Ref DBObjectEditorBE::get_sql_editor() { if (!_sql_editor) { _sql_editor = MySQLEditor::create(_parserContext, _autocompletionContext, { _globalSymbols }); grt::DictRef obj_options = get_dbobject()->customData(); if (obj_options.has_key("sqlMode")) _sql_editor->set_sql_mode(obj_options.get_string("sqlMode")); } return _sql_editor; } //-------------------------------------------------------------------------------------------------- void bec::DBObjectEditorBE::reset_editor_undo_stack() { // Don't create an editor control if we don't need one (e.g. for the schema editor). if (_sql_editor) _sql_editor->get_editor_control()->reset_dirty(); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::notify_from_validation(const grt::Validator::Tag &tag, const grt::ObjectRef &obj, const std::string &msg, const int level) { bool notify_is_for_us = false; if (obj.is_valid()) { // Get edited object const GrtObjectRef our_obj = get_object(); const GrtObjectRef val_obj = GrtObjectRef::cast_from(obj); // Check if passed object is ours if (our_obj == val_obj) notify_is_for_us = true; else { GrtObjectRef parent = val_obj->owner(); while (parent.is_valid()) { if (parent == our_obj) { notify_is_for_us = true; break; } parent = parent->owner(); } // Scan owners upwards } } else { if (tag == "*") notify_is_for_us = true; } if (notify_is_for_us) { _last_validation_check_status = (grt::MessageType)level; _last_validation_message = msg; } } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::send_refresh() { db_DatabaseObjectRef db_object = get_dbobject(); (*db_object->signal_changed())("", grt::ValueRef()); } //-------------------------------------------------------------------------------------------------- void DBObjectEditorBE::set_sql_mode(const std::string &value) { MySQLEditor::Ref sql_editor = get_sql_editor(); if (sql_editor) sql_editor->set_sql_mode(value); } //--------------------------------------------------------------------------------------------------