backend/wbprivate/sqlide/wb_context_sqlide.cpp (875 lines of code) (raw):
/*
* Copyright (c) 2009, 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 "python_context.h"
#include "base/file_utilities.h"
#include "base/string_utilities.h"
#include "base/sqlstring.h"
#include "base/trackable.h"
#include "base/log.h"
#include "base/notifications.h"
#include "base/ui_form.h"
#include "grt.h"
#include "grts/structs.h"
#include "grts/structs.app.h"
#include "grt/editor_base.h"
#include "grtui/grtdb_connect_dialog.h"
#include "workbench/wb_context.h"
#include "workbench/wb_context_ui.h"
#include "workbench/wb_command_ui.h"
#include "workbench/SSHSessionWrapper.h"
#include "sqlide/wb_context_sqlide.h"
#include "sqlide/wb_sql_editor_form.h"
#include "sqlide/wb_sql_editor_panel.h"
#include "sqlide/wb_sql_editor_snippets.h"
#include "sqlide/wb_sql_editor_help.h"
#include "sqlide/wb_sql_editor_result_panel.h"
#include "sqlide/wb_sql_editor_tree_controller.h"
#include "objimpl/db.query/db_query_Editor.h"
#include "objimpl/db.query/db_query_Resultset.h"
#include "objimpl/db.query/db_query_EditableResultset.h"
#include "objimpl/db.query/db_query_QueryBuffer.h"
#include "objimpl/wrapper/grt_PyObject_impl.h"
#include "grts/structs.db.query.h"
#include "grtdb/db_helpers.h"
#include "mforms/code_editor.h"
#include "mforms/menubar.h"
#include "mforms/toolbar.h"
#include "mforms/filechooser.h"
using namespace wb;
using namespace bec;
using namespace base;
DEFAULT_LOG_DOMAIN(DOMAIN_WQE_BE)
static std::map<std::string, std::string> auto_save_sessions;
//------------------------------------------------------------------------------------------------
class MYSQLWBBACKEND_PUBLIC_FUNC db_query_EditorConcreteImplData : public db_query_Editor::ImplData,
public base::trackable {
void sql_editor_list_changed(MySQLEditor::Ref editor, bool added) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
if (added) {
editor->grtobj()->owner(_self);
_self->queryEditors().insert(db_query_QueryEditorRef::cast_from(editor->grtobj()));
} else {
_self->queryEditors().remove_value(db_query_QueryEditorRef::cast_from(editor->grtobj()));
editor->grtobj()->reset_references();
}
}
}
public:
db_query_EditorConcreteImplData(std::shared_ptr<SqlEditorForm> editor, const db_query_EditorRef &self)
: _self(dynamic_cast<db_query_Editor *>(self.valueptr())), _editor(editor) {
for (int c = editor->sql_editor_count(), i = 0; i < c; i++) {
SqlEditorPanel *panel = editor->sql_editor_panel(i);
if (panel) {
db_query_QueryEditorRef qb(panel->grtobj());
qb->owner(self);
_self->queryEditors().insert(qb);
}
}
editor->sql_editor_list_changed.connect(std::bind(&db_query_EditorConcreteImplData::sql_editor_list_changed, this,
std::placeholders::_1, std::placeholders::_2));
}
std::shared_ptr<SqlEditorForm> editor_object() const {
return _editor;
}
virtual db_mgmt_ConnectionRef connection() const {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
return _editor->connection_descriptor();
return db_mgmt_ConnectionRef();
}
virtual db_mgmt_SSHConnectionRef sshConnection() const {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
return _editor->getSSHConnection();
return db_mgmt_SSHConnectionRef();
}
virtual grt::IntegerRef getSSHTunnelPort() const {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
return _editor->getTunnelPort();
return -1;
}
virtual grt::IntegerRef isConnected() const {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
if (_editor->offline())
return grt::IntegerRef(-1);
else {
if (_editor->connected())
return grt::IntegerRef(1);
else
return grt::IntegerRef(0);
}
}
return grt::IntegerRef(0);
}
virtual db_query_QueryEditorRef addQueryEditor() {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
_editor->new_sql_script_file();
return _editor->active_sql_editor_panel()->grtobj();
}
return db_query_QueryEditorRef();
}
virtual grt::IntegerRef addToOutput(const std::string &text, long bringToFront) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
ref->output_text_slot(text, bringToFront != 0);
return grt::IntegerRef(0);
}
virtual grt::ListRef<db_query_Resultset> executeScript(const std::string &sql) {
grt::ListRef<db_query_Resultset> result(true);
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
bec::GRTManager::get()->replace_status_text("Executing query...");
try {
RecordsetsRef rsets(ref->exec_sql_returning_results(sql, true));
for (std::vector<Recordset::Ref>::const_iterator iter = rsets->begin(); iter != rsets->end(); ++iter)
result.insert(grtwrap_recordset(_self, *iter));
bec::GRTManager::get()->replace_status_text("Query finished.");
} catch (sql::SQLException &exc) {
logError("Exception executing SQL code from GRT interface: %s\n", exc.what());
}
}
return result;
}
virtual grt::IntegerRef executeScriptAndOutputToGrid(const std::string &sql) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
ref->exec_sql_retaining_editor_contents(sql, NULL, true);
return grt::IntegerRef(0);
}
virtual db_query_ResultsetRef executeManagementQuery(const std::string &sql, bool log) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
return ref->exec_management_query(sql, log);
}
return db_query_ResultsetRef();
}
virtual void executeManagementCommand(const std::string &sql, bool log) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
ref->exec_management_sql(sql, log);
}
virtual db_query_ResultsetRef executeQuery(const std::string &sql, bool log) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
return ref->exec_main_query(sql, log);
}
return db_query_ResultsetRef();
}
virtual void executeCommand(const std::string &sql, bool log, bool background) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
if (background)
ref->exec_sql_retaining_editor_contents(sql, NULL, false);
else
ref->exec_main_sql(sql, log);
}
}
virtual db_query_EditableResultsetRef createTableEditResultset(const std::string &schema, const std::string &table,
const std::string &where, bool showGrid) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
std::string query;
query = base::sqlstring("SELECT * FROM !.!", 0) << schema << table;
if (!where.empty())
query.append(" ").append(where);
if (showGrid) {
executeScriptAndOutputToGrid(query);
} else {
RecordsetsRef rsets(ref->exec_sql_returning_results(query, true));
if (rsets->size() == 1 && !(*rsets)[0]->is_readonly())
return grtwrap_editablerecordset(_self, (*rsets)[0]);
}
}
return db_query_EditableResultsetRef();
}
virtual void activeSchema(const std::string &schema) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
ref->active_schema(schema);
}
virtual std::string activeSchema() {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
return ref->active_schema();
return "";
}
virtual db_query_QueryEditorRef activeQueryEditor() {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
SqlEditorPanel *panel = ref->active_sql_editor_panel();
if (panel)
return panel->grtobj();
}
return db_query_QueryEditorRef();
}
virtual void editLiveObject(const db_DatabaseObjectRef &object, const db_CatalogRef &catalog) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
ref->get_live_tree()->open_alter_object_editor(object, catalog);
}
}
virtual void alterLiveObject(const std::string &type, const std::string &schemaName, const std::string &objectName) {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref) {
wb::LiveSchemaTree::ObjectType otype;
if (type == "db.Schema")
otype = wb::LiveSchemaTree::Schema;
else if (type == "db.Table")
otype = wb::LiveSchemaTree::Table;
else if (type == "db.View")
otype = wb::LiveSchemaTree::View;
else if (type == "db.StoredProcedure")
otype = wb::LiveSchemaTree::Procedure;
else if (type == "db.Function")
otype = wb::LiveSchemaTree::Function;
else
return;
ref->get_live_tree()->do_alter_live_object(otype, schemaName, objectName);
}
}
virtual grt::ListRef<db_query_LiveDBObject> schemaTreeSelection() const {
std::shared_ptr<SqlEditorForm> ref(_editor);
if (ref)
return grt::ListRef<db_query_LiveDBObject>::cast_from(
ref->get_live_tree()->get_schema_tree()->get_selected_objects());
return grt::ListRef<db_query_LiveDBObject>();
}
void detach() {
_editor.reset();
}
protected:
db_query_Editor *_self;
std::shared_ptr<SqlEditorForm> _editor;
};
//------------------------------------------------------------------------------------------------
void WBContextSQLIDE::call_in_editor(void (SqlEditorForm::*method)()) {
SqlEditorForm *form = get_active_sql_editor();
if (form)
(form->*method)();
}
void WBContextSQLIDE::call_in_editor_panel(void (SqlEditorPanel::*method)()) {
SqlEditorForm *form = get_active_sql_editor();
if (form) {
SqlEditorPanel *panel = form->active_sql_editor_panel();
if (panel)
(panel->*method)();
}
}
void WBContextSQLIDE::call_in_editor_str(void (SqlEditorForm::*method)(const std::string &arg),
const std::string &arg) {
SqlEditorForm *form = get_active_sql_editor();
if (form)
(form->*method)(arg);
}
void WBContextSQLIDE::call_in_editor_str2(void (SqlEditorForm::*method)(const std::string &arg1, bool arg2, bool arg3),
const std::string &arg1, bool arg2, bool arg3) {
SqlEditorForm *form = get_active_sql_editor();
if (form)
(form->*method)(arg1, arg2, arg3);
}
void WBContextSQLIDE::call_in_editor_bool(void (SqlEditorForm::*method)(bool arg), bool arg) {
SqlEditorForm *form = get_active_sql_editor();
if (form)
(form->*method)(arg);
}
static void call_export(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
SqlEditorPanel *panel = form->active_sql_editor_panel();
if (panel && panel->active_result_panel())
panel->active_result_panel()->show_export_recordset();
}
}
inline bool has_active_resultset(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
SqlEditorPanel *panel = form->active_sql_editor_panel();
if (panel)
return panel->active_result_panel() != NULL;
}
return false;
}
static bool validate_export(wb::WBContextSQLIDE *sqlide) {
return has_active_resultset(sqlide);
}
static void call_save_file(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *editor = sqlide->get_active_sql_editor();
if (editor) {
SqlEditorPanel *panel = editor->active_sql_editor_panel();
if (panel) {
panel->save();
}
}
}
static void call_save_file_as(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *editor = sqlide->get_active_sql_editor();
if (editor) {
SqlEditorPanel *panel = editor->active_sql_editor_panel();
if (panel) {
panel->save_as("");
}
}
}
static void call_revert(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *editor = sqlide->get_active_sql_editor();
if (editor) {
SqlEditorPanel *panel = editor->active_sql_editor_panel();
if (panel) {
if (panel->is_dirty()) {
int rc = mforms::Utilities::show_message(
_("Revert to Saved"), base::strfmt(_("Do you want to revert to the most recently saved version of '%s'?\nAny "
"changes since them will be lost."),
panel->filename().c_str()),
_("Revert"), _("Cancel"), "");
if (rc != mforms::ResultOk)
return;
panel->revert_to_saved();
}
}
}
}
static bool validate_revert(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *editor = sqlide->get_active_sql_editor();
if (editor) {
SqlEditorPanel *panel = editor->active_sql_editor_panel();
if (panel)
return !panel->is_scratch() && !panel->filename().empty();
}
return false;
}
static void call_continue_on_error(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form)
form->continue_on_error(!form->continue_on_error());
}
static void call_reconnect(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
sqlide->reconnect_editor(form);
}
static void call_new_connection(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
db_mgmt_ConnectionRef conn(form->connection_descriptor());
wb::WBContextUI::get()->get_wb()->add_new_query_window(conn);
}
}
static bool validate_has_connection(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
return form && form->connection_descriptor().is_valid();
}
static void call_open_script(wb::WBContextSQLIDE *sqlide) {
mforms::FileChooser chooser(mforms::OpenFile);
chooser.set_title("Open SQL Script");
chooser.set_extensions("SQL Files (*.sql)|*.sql|Query Browser Files (*.qbquery)|*.qbquery", "sql");
if (chooser.run_modal()) {
std::shared_ptr<SqlEditorForm> form = wb::WBContextUI::get()->get_wb()->add_new_query_window();
if (form) {
form->open_file(chooser.get_path());
}
}
}
static void call_no_connection_empty_tab(wb::WBContextSQLIDE *sqlide) {
std::shared_ptr<SqlEditorForm> form = wb::WBContextUI::get()->get_wb()->add_new_query_window();
if (form)
form->open_file();
}
static void call_exec_sql(wb::WBContextSQLIDE *sqlide, bool current_statement_only) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form)
form->run_editor_contents(current_statement_only);
}
static bool validate_exec_sql(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
return (form && !form->is_running_query() && form->connected());
}
static void call_save_edits(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
SqlEditorPanel *panel = form->active_sql_editor_panel();
if (panel) {
SqlEditorResult *result = panel->active_result_panel();
if (result)
result->apply_changes();
}
}
}
static void call_discard_edits(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
SqlEditorPanel *panel = form->active_sql_editor_panel();
if (panel) {
SqlEditorResult *result = panel->active_result_panel();
if (result)
result->discard_changes();
}
}
}
//--------------------------------------------------------------------------------------------------
static bool validate_save_edits(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
SqlEditorPanel *panel = form->active_sql_editor_panel();
if (panel) {
SqlEditorResult *result = panel->active_result_panel();
if (result)
return result->has_pending_changes();
}
}
return false;
}
//--------------------------------------------------------------------------------------------------
static bool validate_list_members(wb::WBContextSQLIDE *sqlide) {
return bec::GRTManager::get()->get_app_option_int("DbSqlEditor:CodeCompletionEnabled") != 0;
}
//--------------------------------------------------------------------------------------------------
static void new_script_tab(wb::WBContextSQLIDE *sqlide) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
if (bec::GRTManager::get()->get_app_option_int("DbSqlEditor:DiscardUnsavedQueryTabs", 0))
form->new_sql_scratch_area();
else
form->new_sql_script_file();
}
}
//--------------------------------------------------------------------------------------------------
static bool validate_toolbar_alias_toggle(wb::WBContextSQLIDE *sqlide, const std::string &item_name);
static void call_toolbar_alias_toggle(wb::WBContextSQLIDE *sqlide, const std::string &item_name) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
mforms::ToolBarItem *item = form->get_toolbar()->find_item(item_name);
if (item) {
item->set_checked(!item->get_checked());
item->callback();
validate_toolbar_alias_toggle(sqlide, item_name); // update menu item title
}
}
}
static bool validate_toolbar_alias_toggle(wb::WBContextSQLIDE *sqlide, const std::string &item_name) {
SqlEditorForm *form = sqlide->get_active_sql_editor();
if (form) {
mforms::ToolBarItem *item = form->get_toolbar()->find_item(item_name);
mforms::MenuItem *mitem = NULL;
mitem = form->get_menubar()->find_item("view");
if (mitem)
mitem = mitem->find_item("sidebars");
if (mitem)
mitem = mitem->find_item("alias." + item_name);
if (item && mitem) {
std::string title = mitem->get_title();
if (item->get_checked())
base::replaceStringInplace(title, "Show", "Hide");
else
base::replaceStringInplace(title, "Hide", "Show");
mitem->set_title(title);
}
}
return true;
}
//--------------------------------------------------------------------------------------------------
WBContextSQLIDE::WBContextSQLIDE()
: _auto_save_handle(0), _auto_save_interval(0), _auto_save_active(false), _option_change_signal_connected(false) {
}
//--------------------------------------------------------------------------------------------------
WBContextSQLIDE::~WBContextSQLIDE() {
if (_auto_save_handle)
mforms::Utilities::cancel_timeout(_auto_save_handle);
base::NotificationCenter::get()->remove_observer(this);
}
//--------------------------------------------------------------------------------------------------
void WBContextSQLIDE::option_changed(grt::internal::OwnedDict *dict, bool, const std::string &key) {
if (key == "workbench:AutoSaveSQLEditorInterval" &&
dict == WBContextUI::get()->get_wb()->get_wb_options().valueptr()) {
auto_save_workspaces();
}
}
//--------------------------------------------------------------------------------------------------
bool WBContextSQLIDE::auto_save_workspaces() {
WBContext *wb = WBContextUI::get()->get_wb();
ssize_t interval = wb->get_root()->options()->options().get_int("workbench:AutoSaveSQLEditorInterval", 60);
if (interval <= 0 || !_auto_save_active) {
_auto_save_handle = static_cast<mforms::TimeoutHandle>(NULL);
_auto_save_active = false;
return false;
}
for (std::list<std::weak_ptr<SqlEditorForm>>::const_iterator iter = _open_editors.begin();
iter != _open_editors.end(); ++iter) {
SqlEditorForm::Ref editor((*iter).lock());
try {
if (editor)
editor->auto_save();
} catch (const std::exception &exception) {
logWarning("Exception during auto-save of SQL Editors: %s\n", exception.what());
bec::GRTManager::get()->replace_status_text(
base::strfmt("Error during auto-save of SQL Editors: %s", exception.what()));
}
}
if (interval != _auto_save_interval) {
_auto_save_interval = interval;
if (_auto_save_handle)
mforms::Utilities::cancel_timeout(_auto_save_handle);
// schedule new interval
_auto_save_handle =
mforms::Utilities::add_timeout((float)interval, std::bind(&WBContextSQLIDE::auto_save_workspaces, this));
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
void WBContextSQLIDE::detect_auto_save_files(const std::string &autosave_dir) {
// look for SQLEditor autosave workspace folders
std::list<std::string> autosaves;
try {
autosaves = base::scan_for_files_matching(base::makePath(autosave_dir, "sql_workspaces/*.autosave"));
} catch (const std::runtime_error &e) {
logError("Error while scanning for sql workspaces: %s\n", e.what());
}
for (std::list<std::string>::const_iterator d = autosaves.begin(); d != autosaves.end(); ++d) {
gchar *conn_id;
gsize length;
if (g_file_get_contents(base::makePath(*d, "connection_id").c_str(), &conn_id, &length, NULL)) {
::auto_save_sessions[std::string(conn_id, length)] = *d;
g_free(conn_id);
logInfo("Found auto-save workspace %s\n", d->c_str());
} else
logWarning("Found incomplete auto-save workspace %s\n", d->c_str());
}
}
std::map<std::string, std::string> WBContextSQLIDE::auto_save_sessions() {
return ::auto_save_sessions;
}
//----------------------------------------------------------------------------------------------------------------------
CommandUI *WBContextSQLIDE::get_cmdui() {
return wb::WBContextUI::get()->get_command_ui();
}
//----------------------------------------------------------------------------------------------------------------------
void WBContextSQLIDE::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info) {
if (name == "GNAppClosing")
finalize();
}
//----------------------------------------------------------------------------------------------------------------------
void WBContextSQLIDE::init() {
// Access the context help once to start its initial loading.
help::DbSqlEditorContextHelp::get();
DbSqlEditorSnippets::setup(this, base::makePath(bec::GRTManager::get()->get_user_datadir(), "snippets"));
// scoped_connect(wb::WBContextUI::get()->get_wb()->signal_app_closing(),std::bind(&WBContextSQLIDE::finalize, this));
base::NotificationCenter::get()->add_observer(this, "GNAppClosing");
// Setup some builtin commands handled by ourselves for the SQL IDE.
wb::CommandUI *cmdui = wb::WBContextUI::get()->get_command_ui();
cmdui->add_builtin_command("alias.wb.toggleSidebar", std::bind(call_toolbar_alias_toggle, this, "wb.toggleSidebar"),
std::bind(validate_toolbar_alias_toggle, this, "wb.toggleSidebar"));
cmdui->add_builtin_command("alias.wb.toggleSecondarySidebar",
std::bind(call_toolbar_alias_toggle, this, "wb.toggleSecondarySidebar"),
std::bind(validate_toolbar_alias_toggle, this, "wb.toggleSecondarySidebar"));
cmdui->add_builtin_command("alias.wb.toggleOutputArea",
std::bind(call_toolbar_alias_toggle, this, "wb.toggleOutputArea"),
std::bind(validate_toolbar_alias_toggle, this, "wb.toggleOutputArea"));
cmdui->add_builtin_command("query.execute", std::bind(call_exec_sql, this, false),
std::bind(validate_exec_sql, this));
cmdui->add_builtin_command("query.execute_current_statement", std::bind(call_exec_sql, this, true),
std::bind(validate_exec_sql, this));
cmdui->add_builtin_command("query.explain_current_statement", std::bind(&WBContextSQLIDE::call_in_editor, this,
&SqlEditorForm::explain_current_statement),
std::bind(validate_exec_sql, this));
cmdui->add_builtin_command("query.save_edits", std::bind(call_save_edits, this),
std::bind(validate_save_edits, this));
cmdui->add_builtin_command("query.discard_edits", std::bind(call_discard_edits, this),
std::bind(validate_save_edits, this));
cmdui->add_builtin_command("query.commit", std::bind(&WBContextSQLIDE::call_in_editor, this, &SqlEditorForm::commit));
cmdui->add_builtin_command("query.rollback",
std::bind(&WBContextSQLIDE::call_in_editor, this, &SqlEditorForm::rollback));
cmdui->add_builtin_command("query.autocommit",
std::bind(&WBContextSQLIDE::call_in_editor, this, &SqlEditorForm::toggle_autocommit));
cmdui->add_builtin_command("query.gatherPSInfo", std::bind(&WBContextSQLIDE::call_in_editor, this,
&SqlEditorForm::toggle_collect_ps_statement_events));
cmdui->add_builtin_command("query.new_schema", std::bind(&WBContextSQLIDE::call_in_editor_str, this,
&SqlEditorForm::toolbar_command, "query.new_schema"));
cmdui->add_builtin_command(
"query.show_inspector",
std::bind(&WBContextSQLIDE::call_in_editor_str, this, &SqlEditorForm::toolbar_command, "query.show_inspector"));
cmdui->add_builtin_command("query.new_table", std::bind(&WBContextSQLIDE::call_in_editor_str, this,
&SqlEditorForm::toolbar_command, "query.new_table"));
cmdui->add_builtin_command("query.new_view", std::bind(&WBContextSQLIDE::call_in_editor_str, this,
&SqlEditorForm::toolbar_command, "query.new_view"));
cmdui->add_builtin_command("query.new_routine", std::bind(&WBContextSQLIDE::call_in_editor_str, this,
&SqlEditorForm::toolbar_command, "query.new_routine"));
cmdui->add_builtin_command("query.new_function", std::bind(&WBContextSQLIDE::call_in_editor_str, this,
&SqlEditorForm::toolbar_command, "query.new_function"));
cmdui->add_builtin_command("query.new_connection", std::bind(call_new_connection, this),
std::bind(validate_has_connection, this));
cmdui->add_builtin_command("query.openScriptNoConnection", std::bind(call_open_script, this));
cmdui->add_builtin_command("query.newQueryNoconnection", std::bind(call_no_connection_empty_tab, this));
cmdui->add_builtin_command("query.newQuery",
std::bind(&WBContextSQLIDE::call_in_editor, this, &SqlEditorForm::new_scratch_area));
cmdui->add_builtin_command("query.newFile", std::bind(new_script_tab, this));
cmdui->add_builtin_command(
"query.openFile",
std::bind(&WBContextSQLIDE::call_in_editor_str2, this,
(void (SqlEditorForm::*)(const std::string &, bool, bool)) & SqlEditorForm::open_file, "", true, true));
cmdui->add_builtin_command("query.saveFile", std::bind(call_save_file, this));
cmdui->add_builtin_command("query.saveFileAs", std::bind(call_save_file_as, this));
cmdui->add_builtin_command("query.revert", std::bind(call_revert, this), std::bind(validate_revert, this));
cmdui->add_builtin_command("query.export", std::bind(call_export, this), std::bind(validate_export, this));
cmdui->add_builtin_command("query.cancel",
std::bind(&WBContextSQLIDE::call_in_editor, this, &SqlEditorForm::cancel_query));
cmdui->add_builtin_command("query.reconnect", std::bind(call_reconnect, this));
cmdui->add_builtin_command("query.continueOnError", std::bind(call_continue_on_error, this));
cmdui->add_builtin_command("query.jump_to_placeholder", std::bind(&WBContextSQLIDE::call_in_editor_panel, this,
&SqlEditorPanel::jump_to_placeholder));
cmdui->add_builtin_command("list-members",
std::bind(&WBContextSQLIDE::call_in_editor_panel, this, &SqlEditorPanel::list_members),
std::bind(validate_list_members, this));
}
//----------------------------------------------------------------------------------------------------------------------
void WBContextSQLIDE::finalize() {
if (_auto_save_handle) {
mforms::Utilities::cancel_timeout(_auto_save_handle);
_auto_save_handle = static_cast<mforms::TimeoutHandle>(NULL);
}
std::list<SqlEditorForm::Ptr>::iterator next, ed = _open_editors.begin();
while (ed != _open_editors.end()) {
next = ed;
++next;
if (!ed->expired()) {
ed->lock()->close();
}
ed = next;
}
}
//----------------------------------------------------------------------------------------------------------------------
void WBContextSQLIDE::reconnect_editor(SqlEditorForm *editor) {
std::shared_ptr<wb::SSHTunnel> tunnel;
if (!editor->connection_descriptor().is_valid()) {
grtui::DbConnectionDialog dialog(wb::WBContextUI::get()->get_wb()->get_root()->rdbmsMgmt());
logDebug("No connection associated with editor on reconnect, showing connection selection dialog...\n");
db_mgmt_ConnectionRef target = dialog.run();
if (!target.is_valid())
return;
editor->set_connection(target);
}
// open tunnel, if needed
try {
tunnel = sql::DriverManager::getDriverManager()->getTunnel(editor->connection_descriptor());
} catch (grt::user_cancelled &) {
bec::GRTManager::get()->replace_status_text("Tunnel connection cancelled.");
return;
}
try {
if (editor && !editor->is_running_query()) {
bec::GRTManager::get()->replace_status_text("Reconnecting...");
if (editor->connect(tunnel))
bec::GRTManager::get()->replace_status_text("Connection reopened.");
else {
bec::GRTManager::get()->replace_status_text("Could not reconnect.");
}
}
} catch (std::exception &exc) {
SqlEditorForm::report_connection_failure(exc.what(), editor->connection_descriptor());
return;
}
}
static void *connect_editor(SqlEditorForm::Ref editor, std::shared_ptr<wb::SSHTunnel> tunnel) {
try {
logDebug3("Connecting SQL editor...\n");
editor->connect(tunnel);
} catch (sql::AuthenticationError &exc) {
logError("Got an authentication error during connection: %s\n", exc.what());
return new std::string(exc.what());
} catch (grt::server_denied &sd) {
if (sd.errNo == 3159)
return new std::string(":SSL_ONLY");
if (sd.errNo == 3032)
return new std::string(":OFFLINE_MODE");
} catch (grt::user_cancelled &) {
logInfo("User cancelled connection\n");
return new std::string(":CANCELLED");
}
catch (std::exception &exc)
{
logError("Got an exception during connection: %s\n", exc.what());
return new std::string(exc.what());
}
logDebug3("Connection to SQL editor succeeded\n");
return new std::string();
}
static bool cancel_connect_editor(SqlEditorForm::Ref editor) {
logDebug3("Cancelling connection...\n");
editor->cancel_connect();
return true;
}
SqlEditorForm::Ref WBContextSQLIDE::create_connected_editor(const db_mgmt_ConnectionRef &conn) {
// start by opening the tunnel, if needed
std::shared_ptr<wb::SSHTunnel> tunnel;
if (conn.is_valid())
tunnel = sql::DriverManager::getDriverManager()->getTunnel(conn);
SqlEditorForm::Ref editor(SqlEditorForm::create(this, conn));
if (conn.is_valid()) {
void *result_ptr = 0;
if (!mforms::Utilities::run_cancelable_task(
_("Opening SQL Editor"), strfmt(_("An SQL editor instance for '%s' is opening and should be available in a "
"moment.\n\nPlease stand by..."),
conn->name().c_str()),
std::bind(connect_editor, editor, tunnel), std::bind(cancel_connect_editor, editor), result_ptr))
throw grt::user_cancelled("canceled");
if (!result_ptr)
throw grt::user_cancelled("connection error");
std::string *result = (std::string *)result_ptr;
if (result->empty())
delete result;
else {
std::string tmp(*result);
delete result;
if (tmp == ":PASSWORD_EXPIRED") {
grt::BaseListRef args(grt::AnyType);
args.ginsert(conn);
ssize_t result =
*grt::IntegerRef::cast_from(grt::GRT::get()->call_module_function("WbAdmin", "handleExpiredPassword", args));
if (result != 0)
return create_connected_editor(conn);
throw grt::user_cancelled("password reset cancelled by user");
} else if (tmp == ":CANCELLED") {
throw grt::user_cancelled("Cancelled");
} else if (tmp == ":SSL_ONLY") {
throw grt::server_denied(
"Connections using insecure transport are prohibited while --require_secure_transport=ON.", 3159);
} else if (tmp == ":OFFLINE_MODE") {
throw grt::server_denied("The server is currently in offline mode.", 3032);
}
throw std::runtime_error(tmp);
}
}
{
// Create entry for grt tree and update volatile data in the connection.
db_query_EditorRef object(grt::Initialized);
object->owner(wb::WBContextUI::get()->get_wb()->get_root());
object->name(conn.is_valid() ? conn->name() : "unconnected");
object->set_data(new db_query_EditorConcreteImplData(editor, object));
if (conn.is_valid()) {
std::map<std::string, std::string> details(editor->connection_details());
grt::DictRef parameter_values = conn->parameterValues();
parameter_values.gset("serverVersion", details["dbmsProductVersion"]);
std::time_t time = std::time(0);
parameter_values.gset("lastConnected", (long)time);
object->serverVersion(editor->rdbms_version());
}
wb::WBContextUI::get()->get_wb()->get_root()->sqlEditors().insert(object);
}
_open_editors.push_back(editor);
editor->finish_startup();
// setup auto-save for model
if (!_auto_save_active) {
_auto_save_active = true;
ssize_t interval = wb::WBContextUI::get()->get_wb()->get_root()->options()->options().get_int(
"workbench:AutoSaveSQLEditorInterval", 60);
if (interval > 0)
_auto_save_handle =
mforms::Utilities::add_timeout((float)interval, std::bind(&WBContextSQLIDE::auto_save_workspaces, this));
_auto_save_interval = interval;
if (!_option_change_signal_connected) {
scoped_connect(wb::WBContextUI::get()->get_wb()->get_root()->options()->signal_dict_changed(),
std::bind(&WBContextSQLIDE::option_changed, this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
_option_change_signal_connected = true;
}
}
if (conn.is_valid()) {
if (::auto_save_sessions.find(conn.id()) != ::auto_save_sessions.end()) {
::auto_save_sessions.erase(conn.id());
wb::WBContextUI::get()->refresh_home_connections();
}
}
return editor;
}
SqlEditorForm *WBContextSQLIDE::get_active_sql_editor() {
bec::UIForm *form = wb::WBContextUI::get()->get_active_main_form();
if (form)
return dynamic_cast<SqlEditorForm *>(form);
return 0;
}
bool WBContextSQLIDE::activate_live_object(GrtObjectRef object) {
SqlEditorForm *editor = get_active_sql_editor();
if (!editor)
return false;
return editor->get_live_tree()->activate_live_object(object);
}
//--------------------------------------------------------------------------------------------------
void WBContextSQLIDE::open_document(const std::string &path) {
SqlEditorForm *editor = get_active_sql_editor();
if (editor) {
editor->open_file(path);
} else {
std::shared_ptr<SqlEditorForm> editor(wb::WBContextUI::get()->get_wb()->add_new_query_window());
editor->open_file(path);
}
}
static bool compare(SqlEditorForm::Ptr ptr, SqlEditorForm *editor) {
return ptr.lock().get() == editor;
}
void WBContextSQLIDE::editor_will_close(SqlEditorForm *editor) {
std::list<SqlEditorForm::Ptr>::iterator iter =
std::find_if(_open_editors.begin(), _open_editors.end(), std::bind(compare, std::placeholders::_1, editor));
if (iter != _open_editors.end()) {
// delete entry from grt tree
grt::ListRef<db_query_Editor> editors(wb::WBContextUI::get()->get_wb()->get_root()->sqlEditors());
for (size_t c = editors.count(), i = 0; i < c; i++) {
db_query_EditorRef current_editor(editors[i]);
if (dynamic_cast<db_query_EditorConcreteImplData *>(current_editor->get_data())->editor_object().get() ==
editor) {
current_editor->reset_references();
dynamic_cast<db_query_EditorConcreteImplData *>(current_editor->get_data())->detach();
editors.remove(i);
break;
}
}
_open_editors.erase(iter);
if (_open_editors.empty())
_auto_save_active = false;
}
}
bool WBContextSQLIDE::request_quit() {
for (std::list<SqlEditorForm::Ptr>::iterator ed = _open_editors.begin(); ed != _open_editors.end(); ++ed) {
if (!ed->expired() && !ed->lock()->can_close())
return false;
}
return true;
}
void WBContextSQLIDE::update_plugin_arguments_pool(bec::ArgumentPool &args) {
SqlEditorForm *editor_ptr = get_active_sql_editor();
if (editor_ptr) {
db_query_EditorRef editor(get_grt_editor_object(editor_ptr));
if (editor.is_valid()) {
db_query_QueryEditorRef qeditor(editor->activeQueryEditor());
if (qeditor.is_valid()) {
db_query_ResultPanelRef rpanel(qeditor->activeResultPanel());
args.add_entries_for_object("activeSQLEditor", editor);
args.add_entries_for_object("activeQueryBuffer", qeditor);
args.add_entries_for_object("activeQueryEditor", qeditor);
args.add_entries_for_object("", qeditor);
if (rpanel.is_valid() && rpanel->resultset().is_valid())
args.add_entries_for_object("activeResultset", rpanel->resultset(), "db.query.Resultset");
} else
args.add_entries_for_object("activeSQLEditor", editor);
}
}
}
db_query_EditorRef WBContextSQLIDE::get_grt_editor_object(SqlEditorForm *editor) {
if (editor) {
grt::ListRef<db_query_Editor> list(wb::WBContextUI::get()->get_wb()->get_root()->sqlEditors());
for (grt::ListRef<db_query_Editor>::const_iterator ed = list.begin(); ed != list.end(); ++ed) {
if (dynamic_cast<db_query_EditorConcreteImplData *>((*ed)->get_data())->editor_object().get() == editor)
return *ed;
}
}
return db_query_EditorRef();
}
//--------------------------------------------------------------------------------------------------
static struct RegisterNotifDocs_wb_context_sqlide {
RegisterNotifDocs_wb_context_sqlide() {
base::NotificationCenter::get()->register_notification(
"GRNSQLEditorOpened", "sqlide",
"Sent when a connection tab finishes initializing and is about to be shown on screen.",
"db.query.Editor instance", "");
base::NotificationCenter::get()->register_notification(
"GRNSQLEditorReconnected", "sqlide",
"Sent when the connection state of a SQL editor changes (reconnect, disconnect) it's DB connection. Resent "
"whenever the user clicks Reconnect.",
"db.query.Editor instance", "connected - whether the connection is open");
base::NotificationCenter::get()->register_notification(
"GRNServerStateChanged", "sqlide",
"Sent by the Admin module when it is detected that the server state (running or stopped) changed.",
"db.query.Editor instance",
"state - running or stopped\n"
"connection - the connection object for the server");
base::NotificationCenter::get()->register_notification(
"GRNLiveDBObjectMenuWillShow", "sqlide",
"Sent when the context menu is about to be shown for a live DB object (eg. from the live schema tree in the SQL "
"IDE sidebar).",
"db.query.Editor instance",
"menu - mforms.ObjectReference of a mforms.Menu object which is being shown\n"
"selection - a list of db.query.LiveDBObject for the selected objects");
base::NotificationCenter::get()->register_notification(
"GRNLiveDBObjectSelectionDidChange", "sqlide",
"Sent when the selection in the schema tree changes. Avoid hooking slow actions to this as it will make object "
"selection less smooth.",
"db.query.Editor instance", "selection-size - number of selected items in the tree");
base::NotificationCenter::get()->register_notification(
"GRNSQLResultSetMenuWillShow", "sqlide",
"Sent when the context menu is about to be shown for a resultset in the SQL IDE.", "db.query.Resultset instance",
"menu - mforms.ObjectReference of a mforms.Menu object which is being shown\n"
"selected_rows - a list of int values of the selected rows\n"
"selected_column - if this value is set, the selection is a single cell at selected_rows[0], selected_column");
}
} initdocs_wb_context_sqlide;