2
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; version 2 of the
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22
#include "wb_sql_editor_form.h"
24
#include "sqlide/recordset_be.h"
25
#include "sqlide/recordset_cdbc_storage.h"
26
#include "grtsqlparser/sql_facade.h"
27
#include "grtui/confirm_save_dialog.h"
28
#include "sqlide/wb_sql_editor_snippets.h"
29
#include "base/string_utilities.h"
30
#include "base/notifications.h"
32
#include "objimpl/db.query/db_query_Resultset.h"
34
#include "workbench/wb_context_ui.h"
36
#include "sqlide/sql_script_run_wizard.h"
38
#include "base/file_functions.h"
41
// TODO: check if this inclusion is still necessary.
42
// this doesn't belong here, but there's no other workaround for having access to mysql_info
43
// because of that we also need to link wbprivate directly to the connector, making their
44
// dynamic loading pretty much useless
45
#include <cppconn/../driver/mysql_connection.h>
47
#include "base/boost_smart_ptr_helpers.h"
49
#include <boost/foreach.hpp>
50
#include <boost/scoped_ptr.hpp>
51
#include <boost/signals2/connection.hpp>
52
#include "grt/common.h"
54
#include "query_side_palette.h"
56
#include "mforms/menubar.h"
57
#include "mforms/grttreeview.h"
58
#include "mforms/hypertext.h" // needed for d-tor
59
#include "mforms/tabview.h" // needed for d-tor
60
#include "mforms/splitter.h" // needed for d-tor
61
#include "mforms/toolbar.h"
68
using boost::signals2::scoped_connection;
70
DEFAULT_LOG_DOMAIN("SqlEditor")
72
static const char *SQL_EXCEPTION_MSG_FORMAT= _("Error Code: %i\n%s");
73
static const char *EXCEPTION_MSG_FORMAT= _("Error: %s");
75
#define CATCH_SQL_EXCEPTION_AND_DISPATCH(statement, log_message_index, duration) \
76
catch (sql::SQLException &e)\
78
set_log_message(log_message_index, DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement, duration);\
81
#define CATCH_EXCEPTION_AND_DISPATCH(statement) \
82
catch (std::exception &e)\
84
add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(EXCEPTION_MSG_FORMAT, e.what()), statement, "");\
87
#define CATCH_ANY_EXCEPTION_AND_DISPATCH(statement) \
88
catch (sql::SQLException &e)\
90
add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement, "");\
92
CATCH_EXCEPTION_AND_DISPATCH(statement)
94
#define CATCH_ANY_EXCEPTION_AND_DISPATCH_TO_DEFAULT_LOG(statement) \
95
catch (sql::SQLException &e)\
97
_grtm->get_grt()->send_error(strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement);\
99
catch (std::exception &e)\
101
_grtm->get_grt()->send_error(strfmt(EXCEPTION_MSG_FORMAT, e.what()), statement);\
108
Timer(bool run_immediately) : _is_running(false), _start_timestamp(0), _duration(0)
124
_start_timestamp= timestamp();
131
_duration+= timestamp() - _start_timestamp;
135
return _is_running ? (_duration + timestamp() - _start_timestamp) : _duration;
137
std::string duration_formatted()
140
s= std::div((int)duration(), 1000);
141
return strfmt(_("%i.%.3i sec"), s.quot, s.rem);
145
time_t _start_timestamp;
150
std::string & strip_sql_ident(std::string &text)
154
size_t size= text.size();
157
for (; start < size; ++start)
158
if (!std::isspace(text[start], loc) && (text[start] != '`') && (text[start] != '\'') && (text[start] != '"'))
162
for (; end > 0; --end)
163
if (!std::isspace(text[end-1], loc) && (text[end-1] != '`') && (text[end-1] != '\'') && (text[end-1] != '"'))
166
return text= text.substr(start, end-start);
170
SqlEditorForm::Ref SqlEditorForm::create(wb::WBContextSQLIDE *wbsql, const db_mgmt_ConnectionRef &conn)
172
SqlEditorForm::Ref instance(new SqlEditorForm(wbsql, conn));
174
instance->get_base_schema_tree()->set_delegate(instance);
175
instance->get_base_schema_tree()->set_fetch_delegate(instance);
177
instance->get_filtered_schema_tree()->set_delegate(instance);
178
instance->get_filtered_schema_tree()->set_fetch_delegate(instance);
179
instance->get_filtered_schema_tree()->set_base(instance->get_base_schema_tree());
183
//instance->connect();
184
//instance->finish_startup();
190
SqlEditorForm::SqlEditorForm(wb::WBContextSQLIDE *wbsql, const db_mgmt_ConnectionRef &conn)
193
_context_ui(_wbsql->get_wbui()),
194
_grtm(wbsql->get_grt_manager()),
195
_menu(NULL), // Please use NULL where pointers are assigned, not 0, to avoid confusing the param with ctor flags or similar!
197
_autosave_lock(NULL),
198
_autosave_disabled(false),
199
_loading_workspace(false),
201
_sql_editors_serial(0),
202
_scratch_editors_serial(0),
203
_active_sql_editor_index(0),
204
_updating_sql_editor(0),
205
_keep_alive_thread(NULL),
207
_aux_dbc_conn(new sql::Dbc_connection_handler()),
208
_usr_dbc_conn(new sql::Dbc_connection_handler()),
209
exec_sql_task(GrtThreadedTask::create(wbsql->get_grt_manager())),
210
_is_running_query(false),
211
_schema_tree(&_base_schema_tree),
212
_base_schema_tree(wbsql->get_grt_manager()->get_grt()),
213
_filtered_schema_tree(wbsql->get_grt_manager()->get_grt()),
214
live_schema_fetch_task(GrtThreadedTask::create(wbsql->get_grt_manager())),
216
_side_splitter(NULL), _info_tabview(NULL), _object_info(NULL), _session_info(NULL),
217
_side_palette_host(NULL),
219
_is_refreshing_schema_tree(false),
220
live_schemata_refresh_task(GrtThreadedTask::create(wbsql->get_grt_manager())),
221
_log(DbSqlEditorLog::create(wbsql->get_grt_manager())),
222
_history(DbSqlEditorHistory::create(wbsql->get_grt_manager()))
224
_options= DictRef::cast_from(_grtm->get_grt()->get("/wb/options/options"));
226
//scoped_connect(_wbsql->get_wbui()->signal_main_form_change(),boost::bind(&SqlEditorForm::main_form_changed, this, _1));
227
NotificationCenter::get()->add_observer(this, "GNMainFormChanged");
228
NotificationCenter::get()->add_observer(this, "GNFormTitleDidChange");
230
_sql_editors_mutex= g_mutex_new();
231
_aux_dbc_conn_mutex= g_mutex_new();
232
_usr_dbc_conn_mutex= g_mutex_new();
233
_keep_alive_thread_mutex= g_mutex_new();
234
_schema_contents_mutex= g_mutex_new();
236
_base_schema_tree.is_schema_contents_enabled(_options.get_int("DbSqlEditor:ShowSchemaTreeSchemaContents", 1) != 0);
237
_filtered_schema_tree.is_schema_contents_enabled(_options.get_int("DbSqlEditor:ShowSchemaTreeSchemaContents", 1) != 0);
239
_base_schema_tree.sql_editor_text_insert_signal.connect(boost::bind(&SqlEditorForm::call_sql_editor_text_insert_signal,this,_1));
240
_filtered_schema_tree.sql_editor_text_insert_signal.connect(boost::bind(&SqlEditorForm::call_sql_editor_text_insert_signal,this,_1));
242
_base_schema_tree.object_plugin_items_slot= boost::bind(&SqlEditorForm::get_live_catalog_menu_items, this, _1, _2, _3);
243
_filtered_schema_tree.object_plugin_items_slot= boost::bind(&SqlEditorForm::get_live_catalog_menu_items, this, _1, _2, _3);
244
_base_schema_tree.call_object_plugin_items_slot= boost::bind(&SqlEditorForm::call_live_catalog_menu_item, this, _1, _2, _3, _4);
245
_filtered_schema_tree.call_object_plugin_items_slot= boost::bind(&SqlEditorForm::call_live_catalog_menu_item, this, _1, _2, _3, _4);
247
live_schemata_refresh_task->send_task_res_msg(false);
248
live_schemata_refresh_task->msg_cb(boost::bind(&SqlEditorForm::add_log_message, this, _1, _2, _3, ""));
250
live_schema_fetch_task->send_task_res_msg(false);
251
live_schema_fetch_task->msg_cb(boost::bind(&SqlEditorForm::add_log_message, this, _1, _2, _3, ""));
253
exec_sql_task->send_task_res_msg(false);
254
exec_sql_task->msg_cb(boost::bind(&SqlEditorForm::add_log_message, this, _1, _2, _3, ""));
256
_last_log_message_timestamp= timestamp();
258
_progress_status_update_interval= _options.get_int("DbSqlEditor:ProgressStatusUpdateInterval", 500);
260
int keep_alive_interval= _options.get_int("DbSqlEditor:KeepAliveInterval", 600);
261
if (keep_alive_interval != 0)
263
_keep_alive_thread= TimerActionThread::create(boost::bind(&SqlEditorForm::send_message_keep_alive, this), keep_alive_interval*1000);
264
_keep_alive_thread->on_exit.connect(boost::bind(&SqlEditorForm::reset_keep_alive_thread, this));
267
_continue_on_error= (_options.get_int("DbSqlEditor:ContinueOnError", 0) != 0);
271
void SqlEditorForm::finish_startup()
273
// this part was in do_connect(), but moved here since they need to be called
274
// from main thread anyway
276
//live_schemata_refresh_task->execute_in_main_thread(
277
// boost::bind(&LiveSchemaTree::set_active_schema, _schema_tree,
278
// _connection->parameterValues().get_string("schema")), false, true);
279
if (_usr_dbc_conn && !_usr_dbc_conn->active_schema.empty())
280
_base_schema_tree.set_active_schema(_usr_dbc_conn->active_schema);
282
//live_schemata_refresh_task->execute_in_main_thread(
283
// boost::bind((void(SqlEditorForm::*)(bool))&SqlEditorForm::refresh_schema_tree, this),
286
refresh_schema_tree(true);
287
update_menu_and_toolbar();
292
std::string name = _connection->name();
294
name = _connection->hostIdentifier();
296
load_workspace(sanitize_file_name(name));
298
if (_sql_editors.empty())
303
// update the info box
304
schema_row_selected();
307
// Gets the title for a NEW editor
308
_title = get_title(true);
312
void SqlEditorForm::title_changed()
314
base::NotificationInfo info;
315
info["form"] = form_id();
316
info["title"] = _title;
317
info["hostName"] = _connection->parameterValues().get_string("hostName");
318
base::NotificationCenter::get()->send("GNFormTitleDidChange", this, info);
321
SqlEditorForm::~SqlEditorForm()
323
NotificationCenter::get()->remove_observer(this);
325
delete _autosave_lock;
327
delete _info_tabview;
330
// Destructor can be called before the startup was finished.
331
if (_side_palette_host != NULL)
332
_side_palette_host->release();
333
if (_side_palette != NULL)
334
_side_palette->release();
335
if (_side_splitter != NULL)
336
_side_splitter->release();
338
delete _session_info;
345
reset_keep_alive_thread();
347
g_mutex_free(_schema_contents_mutex);
348
g_mutex_free(_keep_alive_thread_mutex);
349
g_mutex_free(_usr_dbc_conn_mutex);
350
g_mutex_free(_aux_dbc_conn_mutex);
351
g_mutex_free(_sql_editors_mutex);
355
void SqlEditorForm::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info)
357
if (name == "GNMainFormChanged")
360
_side_palette->close_popover();
361
if (_wbsql->get_wbui()->get_active_main_form() == this)
362
update_menu_and_toolbar();
364
else if (name == "GNFormTitleDidChange")
366
// Validates only if another editor to the same connection has sent the notification
367
if (info["form"] != form_id() && info["hostName"] == _connection->parameterValues().get_string("hostName"))
369
// This code is reached when at least 2 editors to the same host
370
// have been opened, so the label of the old editor (which may not
371
// contain the schema name should be updated with it)
372
std::string temp_title = get_title(true);
374
if (temp_title != _title)
384
void SqlEditorForm::reset_keep_alive_thread()
386
GMutexLock keep_alive_thread_lock(_keep_alive_thread_mutex);
387
if (_keep_alive_thread)
389
_keep_alive_thread->stop(true);
390
_keep_alive_thread= NULL;
394
grt::StringRef SqlEditorForm::do_disconnect(grt::GRT *grt)
396
if (_usr_dbc_conn->ref.get())
398
sql::Driver *dbc_driver= _usr_dbc_conn->ref->getDriver();
400
close_connection(_usr_dbc_conn);
401
close_connection(_aux_dbc_conn);
402
_aux_dbc_conn->ref.reset();
403
_usr_dbc_conn->ref.reset();
405
dbc_driver->threadEnd();
407
return grt::StringRef();
410
bool SqlEditorForm::close()
416
if (exec_sql_task && exec_sql_task->is_busy())
418
_grtm->replace_status_text("Cannot close SQL Editor while busy");
422
bool res= bec::UIForm::close();
425
grt::ValueRef option(_grtm->get_app_option("workbench:SaveSQLWorkspaceOnClose"));
426
if (option.is_valid() && *grt::IntegerRef::cast_from(option))
428
_grtm->replace_status_text("Saving workspace state...");
429
if (_autosave_path.empty())
431
save_workspace(sanitize_file_name(_connection->name()), false);
432
delete _autosave_lock;
438
// Remove auto lock first or renaming the folder will fail.
439
delete _autosave_lock;
440
std::string new_name(base::strip_extension(_autosave_path)+".workspace");
443
// Rename our temporary workspace if one exists to make it a persistent one.
444
if (base::file_exists(_autosave_path))
450
if (base::file_exists(new_name))
451
base::remove_recursive(new_name);
452
base::rename(_autosave_path, new_name);
454
catch (base::file_error &err)
456
std::string path(dirname(_autosave_path));
460
new_name = make_path(path, sanitize_file_name(_connection->name()).append(strfmt("-%i.workspace", try_count)));
461
} while (file_exists(new_name));
463
if (err.code() == base::already_exists)
465
log_warning("Could not rename autosave directory: %s\n",
466
_autosave_path.c_str(), err.what());
477
delete _autosave_lock;
479
if (!_autosave_path.empty())
480
base_rmdir_recursively(_autosave_path.c_str());
483
_grtm->replace_status_text("Closing SQL Editor...");
484
wbsql()->editor_will_close(this);
486
exec_sql_task->exec(true, boost::bind(&SqlEditorForm::do_disconnect, this, _1));
487
exec_sql_task->disconnect_callbacks();
488
reset_keep_alive_thread();
489
// close_connection(_usr_dbc_conn);
490
// close_connection(_aux_dbc_conn);
491
// _aux_dbc_conn->ref.reset();
492
// _usr_dbc_conn->ref.reset();
493
_grtm->replace_status_text("SQL Editor closed");
499
std::string SqlEditorForm::get_form_context_name() const
501
return WB_CONTEXT_QUERY;
505
bool SqlEditorForm::get_session_variable(sql::Dbc_connection_handler::Ref &dbc_conn, const std::string &name, std::string &value)
507
if (dbc_conn && dbc_conn->ref.get())
509
SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms());
510
Sql_specifics::Ref sql_specifics= sql_facade->sqlSpecifics();
511
std::string query= sql_specifics->query_variable(name);
514
boost::scoped_ptr<sql::Statement> statement(dbc_conn->ref->createStatement());
515
boost::scoped_ptr<sql::ResultSet> rs(statement->executeQuery(query));
518
value= rs->getString(2);
526
void SqlEditorForm::cache_sql_mode()
528
std::string sql_mode;
529
if (get_session_variable(_usr_dbc_conn, "sql_mode", sql_mode))
531
if (sql_mode != _sql_mode)
534
GMutexLock sql_editors_mutex(_sql_editors_mutex);
535
BOOST_FOREACH (Sql_editor_info::Ref sql_editor_info, _sql_editors)
536
sql_editor_info->editor->sql_mode(sql_mode);
542
int SqlEditorForm::run_sql_in_scratch_tab(const std::string &sql, bool reuse_if_possible, bool start_collapsed)
544
if (_active_sql_editor_index < 0 || !reuse_if_possible || !sql_editor_is_scratch(_active_sql_editor_index))
545
new_sql_scratch_area(start_collapsed);
546
set_sql_editor_text(sql);
548
sql_editor_dirty(_active_sql_editor_index, false);
550
return _active_sql_editor_index;
554
void SqlEditorForm::reset()
560
RowId SqlEditorForm::add_log_message(int msg_type, const std::string &msg, const std::string &context, const std::string &duration)
562
RowId new_log_message_index= _log->add_message(msg_type, context, msg, duration);
563
_has_pending_log_messages= true;
564
refresh_log_messages(false);
565
if (msg_type == DbSqlEditorLog::ErrorMsg || msg_type == DbSqlEditorLog::WarningMsg)
566
_exec_sql_error_count++;
567
return new_log_message_index;
571
void SqlEditorForm::set_log_message(RowId log_message_index, int msg_type, const std::string &msg, const std::string &context, const std::string &duration)
573
if (log_message_index != (RowId)-1)
575
_log->set_message(log_message_index, msg_type, context, msg, duration);
576
_has_pending_log_messages= true;
577
if (msg_type == DbSqlEditorLog::ErrorMsg || msg_type == DbSqlEditorLog::WarningMsg)
578
_exec_sql_error_count++;
579
bool force_refresh = true;
580
if (msg_type == DbSqlEditorLog::BusyMsg)
581
force_refresh = false;
582
refresh_log_messages(force_refresh);
587
void SqlEditorForm::refresh_log_messages(bool ignore_last_message_timestamp)
589
if (_has_pending_log_messages)
591
bool is_refresh_needed= ignore_last_message_timestamp;
592
if (!ignore_last_message_timestamp)
594
time_t now= timestamp();
595
if (_last_log_message_timestamp + _progress_status_update_interval < now)
596
is_refresh_needed= true;
597
_last_log_message_timestamp= now;
599
if (is_refresh_needed)
601
exec_sql_task->send_progress(0.f, std::string(), std::string());
602
_has_pending_log_messages= false;
608
void SqlEditorForm::get_log_event_details(int log_event_no, int &log_event_type_code, std::string &log_event_time, std::string &log_event_action, std::string &log_event_message)
610
NodeId node(log_event_no);
611
_log->get_field(node, 0, log_event_type_code);
612
_log->get_field(node, 2, log_event_time);
613
_log->get_field(node, 3, log_event_action);
614
_log->get_field(node, 4, log_event_message);
617
std::string SqlEditorForm::get_text_for_actions(const std::list<int> &indexes, bool query_only)
621
for (std::list<int>::const_iterator end = indexes.end(), it = indexes.begin(); it != end; ++it)
630
_log->get_field(*it, 3, s);
631
sql.append(s).append("\n");
635
_log->get_field(*it, 0, s);
636
sql.append(s).append("\t");
637
_log->get_field(*it, 2, s);
638
sql.append(s).append("\t");
639
_log->get_field(*it, 3, s);
640
sql.append(s).append("\t");
641
_log->get_field(*it, 4, s);
642
sql.append(s).append("\n");
648
int SqlEditorForm::recordset_count(int editor)
650
if (editor >= 0 && editor < (int)_sql_editors.size())
651
return sql_editor_recordsets(editor)->size();
656
Recordset::Ref SqlEditorForm::recordset(int editor, int new_index)
658
if (editor >= 0 && editor < (int)_sql_editors.size())
659
return sql_editor_recordsets(editor)->at(new_index);
660
return Recordset::Ref();
664
void SqlEditorForm::init_connection(sql::Connection* dbc_conn_ref, const db_mgmt_ConnectionRef& connectionProperties, sql::Dbc_connection_handler::Ref& dbc_conn)
666
db_mgmt_RdbmsRef rdbms= db_mgmt_RdbmsRef::cast_from(_connection->driver()->owner());
667
SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms);
668
Sql_specifics::Ref sql_specifics= sql_facade->sqlSpecifics();
670
// connection startup script
672
std::list<std::string> sql_script;
674
sql_specifics->get_connection_startup_script(sql_script);
675
bool use_ansi_quotes= (connectionProperties->parameterValues().get_int("useAnsiQuotes", 0) != 0);
678
std::string sql= sql_specifics->setting_ansi_quotes();
680
sql_script.push_back(sql);
684
// check if SQL_SAFE_UPDATES should be enabled
685
if (_options.get_int("DbSqlEditor:SafeUpdates", 1))
686
sql_script.push_back("SET SQL_SAFE_UPDATES=1");
688
std::auto_ptr<sql::Statement> stmt(dbc_conn_ref->createStatement());
689
sql::SqlBatchExec sql_batch_exec;
690
sql_batch_exec(stmt.get(), sql_script);
693
// remember connection id
695
std::string query_connection_id= sql_specifics->query_connection_id();
696
if (!query_connection_id.empty())
698
std::auto_ptr<sql::Statement> stmt(dbc_conn_ref->createStatement());
699
stmt->execute(query_connection_id);
700
boost::shared_ptr<sql::ResultSet> rs(stmt->getResultSet());
702
dbc_conn->id= rs->getInt(1);
708
void SqlEditorForm::create_connection(sql::Dbc_connection_handler::Ref &dbc_conn, db_mgmt_ConnectionRef db_mgmt_conn,
709
sql::AuthenticationSet *authset, bool autocommit_mode)
711
dbc_conn->is_stop_query_requested= false;
713
sql::DriverManager *dbc_drv_man= sql::DriverManager::getDriverManager();
715
db_mgmt_ConnectionRef temp_connection = db_mgmt_ConnectionRef::cast_from(CopyContext(db_mgmt_conn.get_grt()).copy(db_mgmt_conn));
717
int read_timeout = _options.get_int("DbSqlEditor:ReadTimeOut");
718
if (read_timeout > 0)
719
temp_connection->parameterValues().set("OPT_READ_TIMEOUT", grt::IntegerRef(read_timeout));
721
// we could either always pick last used schema or only pick last used schema if the connection doesn't have a default schema
722
// since customer from bug #11758776 wants the 1st, we do that for now...
723
//if (temp_connection->parameterValues().get_string("schema") == "")
724
// don't override the default schema used to connect, because the schema may have been deleted and the connection will fail
725
// further, the schema can't be changed from the connection editor. Default schema is set once connected
726
// temp_connection->parameterValues().gset("schema", temp_connection->parameterValues().get_string("DbSqlEditor:LastDefaultSchema"));
729
dbc_conn->ref= dbc_drv_man->getConnection(temp_connection,
730
boost::bind(&SqlEditorForm::init_connection, this, _1, _2, dbc_conn));
732
dbc_conn->ref= dbc_drv_man->getConnection(temp_connection, *authset,
733
boost::bind(&SqlEditorForm::init_connection, this, _1, _2, dbc_conn));
734
//! dbms-specific code
735
if (dbc_conn->ref->getMetaData()->getDatabaseMajorVersion() < 5)
737
throw std::runtime_error("MySQL Server version is older than 5.0, which is not supported");
740
if (dbc_conn->active_schema.empty())
742
std::string last_default = temp_connection->parameterValues().get_string("DbSqlEditor:LastDefaultSchema");
745
if (!last_default.empty())
746
dbc_conn->ref->setSchema(last_default);
748
catch (std::exception &exc)
750
log_error("Can't restore LastDefaultSchema (%s): %s", last_default.c_str(), exc.what());
751
temp_connection->parameterValues().gset("DbSqlEditor:LastDefaultSchema", "");
754
dbc_conn->active_schema= dbc_conn->ref->getSchema();
757
dbc_conn->ref->setSchema(dbc_conn->active_schema);
758
dbc_conn->ref->setAutoCommit(autocommit_mode);
759
dbc_conn->autocommit_mode= dbc_conn->ref->getAutoCommit();
763
void SqlEditorForm::connect()
765
sql::AuthenticationSet authset;
769
// if an auth error happens in the worker thread, this ptr will be set
770
sql::AuthenticationError *auth_error_ptr = 0;
772
// connection must happen in the worker thread
775
exec_sql_task->exec(true, boost::bind(&SqlEditorForm::do_connect, this, _1, authset, boost::ref(auth_error_ptr)));
777
catch (grt::grt_runtime_error)
781
if (auth_error_ptr->authentication() && !auth_error_ptr->authentication()->is_valid())
782
authset.insert(auth_error_ptr->authentication());
785
delete auth_error_ptr;
787
for (sql::AuthenticationSet::iterator auth = authset.begin(); auth != authset.end(); ++auth)
789
if (!(*auth)->is_valid())
791
std::string pwd = sql::DriverManager::getDriverManager()->requestPassword((*auth)->connectionProperties(), true);
792
(*auth)->set_password(pwd.c_str());
802
//--------------------------------------------------------------------------------------------------
805
* Little helper to create a single html line used for info output.
807
std::string create_html_line(const std::string& name, const std::string& value)
809
return "<div style=\"padding-left: 15px\"><span style=\"color: #717171\">" + name + "</span> <i>" +
810
value + "</i></div>";
813
//--------------------------------------------------------------------------------------------------
815
grt::StringRef SqlEditorForm::do_connect(grt::GRT *grt, sql::AuthenticationSet &authset, sql::AuthenticationError *&autherr_ptr)
819
GMutexLock aux_dbc_conn_mutex(_aux_dbc_conn_mutex);
820
GMutexLock usr_dbc_conn_mutex(_usr_dbc_conn_mutex);
824
_aux_dbc_conn->ref.reset();
825
_usr_dbc_conn->ref.reset();
826
create_connection(_aux_dbc_conn, _connection, &authset, _aux_dbc_conn->autocommit_mode);
827
create_connection(_usr_dbc_conn, _connection, &authset, _usr_dbc_conn->autocommit_mode);
834
_connection_details["name"] = _connection->name();
835
_connection_details["hostName"] = _connection->parameterValues().get_string("hostName");
836
_connection_details["port"] = strfmt("%li\n", _connection->parameterValues().get_int("port"));
837
_connection_details["socket"] = _connection->parameterValues().get_string("socket");
838
_connection_details["driverName"] = _connection->driver()->name();
839
_connection_details["dbmsProductName"] = _usr_dbc_conn->ref->getMetaData()->getDatabaseProductName();
840
_connection_details["dbmsProductVersion"] = _usr_dbc_conn->ref->getMetaData()->getDatabaseProductVersion();
841
_connection_details["userName"] = _connection->parameterValues().get_string("userName");
844
_connection_info= "<div style=\"color=#3b3b3b; font-weight:bold\">Connection:</div>";
845
_connection_info.append(create_html_line("Name: ", _connection->name()));
847
if (_connection->driver()->name() == "MysqlNativeSocket")
850
std::string name = _connection->parameterValues().get_string("socket", "");
854
std::string name = _connection->parameterValues().get_string("socket", "");
856
name = "UNIX socket";
858
_connection_info.append(create_html_line("Host:", "localhost (" + name + ")"));
862
_connection_info.append(create_html_line("Host:", _connection->parameterValues().get_string("hostName")));
863
_connection_info.append(create_html_line("Port:", strfmt("%i", (int)_connection->parameterValues().get_int("port"))));
867
_connection_info.append(create_html_line("Server:", _connection_details["dbmsProductName"]));
868
_connection_info.append(create_html_line("Version:", _connection_details["dbmsProductVersion"]));
870
_connection_info.append(create_html_line("Login User:", _connection->parameterValues().get_string("userName")));
872
// check the actual user we're logged in as
873
if (_usr_dbc_conn && _usr_dbc_conn->ref.get())
875
boost::scoped_ptr<sql::Statement> statement(_usr_dbc_conn->ref->createStatement());
876
boost::scoped_ptr<sql::ResultSet> rs(statement->executeQuery("SELECT current_user()"));
878
_connection_info.append(create_html_line("Current User:", rs->getString(1)));
881
CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Get connection information"));
883
catch (sql::AuthenticationError &exc)
885
autherr_ptr = new sql::AuthenticationError(exc);
889
// not sure why this was making a copy of rdbms
890
// db_mgmt_RdbmsRef rdbms= db_mgmt_RdbmsRef::cast_from(grt::copy_object(_grtm->get_grt(), this->rdbms()));
891
db_mgmt_RdbmsRef rdbms= this->rdbms();
892
std::string database_package= *rdbms->databaseObjectPackage();
894
workbench_physical_ModelRef model(_grtm->get_grt());
897
db_CatalogRef catalog= _grtm->get_grt()->create_object<db_Catalog>(database_package + ".Catalog");
899
catalog->owner(model);
900
catalog->name("default");
901
catalog->oldName("default");
902
catalog->version(rdbms->version());
903
grt::replace_contents(catalog->simpleDatatypes(), rdbms->simpleDatatypes());
905
// setup list of synonyms
906
grt::Module *module= _grtm->get_grt()->get_module("DbMySQL");
909
grt::BaseListRef args(_grtm->get_grt());
911
grt::ListRef<db_UserDatatype> user_types(grt::ListRef<db_UserDatatype>::cast_from(module->call_function("getDefaultUserDatatypes", args)));
913
if (user_types.is_valid())
915
GRTLIST_FOREACH(db_UserDatatype, user_types, ut)
917
(*ut)->owner(catalog);
918
catalog->userDatatypes().insert(*ut);
923
model->catalog(catalog);
926
return grt::StringRef();
930
bool SqlEditorForm::connected() const
932
return (_usr_dbc_conn && _usr_dbc_conn->ref.get_ptr() && !_usr_dbc_conn->ref->isClosed());
936
GMutexLock SqlEditorForm::ensure_valid_aux_connection()
938
return ensure_valid_dbc_connection(_aux_dbc_conn, _aux_dbc_conn_mutex);
942
GMutexLock SqlEditorForm::ensure_valid_usr_connection()
944
return ensure_valid_dbc_connection(_usr_dbc_conn, _usr_dbc_conn_mutex);
948
void SqlEditorForm::close_connection(sql::Dbc_connection_handler::Ref &dbc_conn)
950
if (dbc_conn && dbc_conn->ref.get_ptr())
954
dbc_conn->ref->close();
956
catch (sql::SQLException &)
958
// ignore if the connection is already closed
964
GMutexLock SqlEditorForm::ensure_valid_dbc_connection(sql::Dbc_connection_handler::Ref &dbc_conn, GMutex *dbc_conn_mutex)
966
GMutexLock mutex_lock(dbc_conn_mutex);
968
if (dbc_conn && dbc_conn->ref.get_ptr())
970
if (dbc_conn->ref->isClosed())
972
if (dbc_conn->autocommit_mode)
974
sql::AuthenticationSet authset;
975
create_connection(dbc_conn, _connection, 0, dbc_conn->autocommit_mode);
976
if (!dbc_conn->ref->isClosed())
984
throw std::runtime_error("DBMS connection is not available");
990
bool SqlEditorForm::auto_commit()
993
return _usr_dbc_conn->autocommit_mode;
998
void SqlEditorForm::auto_commit(bool value)
1003
const char *STATEMENT= value ? "AUTOCOMMIT=1" : "AUTOCOMMIT=0";
1006
GMutexLock usr_dbc_conn_mutex= ensure_valid_usr_connection();
1007
_usr_dbc_conn->ref->setAutoCommit(value);
1008
_usr_dbc_conn->autocommit_mode= _usr_dbc_conn->ref->getAutoCommit();
1010
CATCH_ANY_EXCEPTION_AND_DISPATCH(STATEMENT)
1012
update_menu_and_toolbar();
1016
void SqlEditorForm::toggle_autocommit()
1018
auto_commit(!auto_commit());
1019
update_menu_and_toolbar();
1023
void SqlEditorForm::cancel_query()
1025
std::string query_kill_query;
1027
db_mgmt_RdbmsRef rdbms= db_mgmt_RdbmsRef::cast_from(_connection->driver()->owner());
1028
SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms);
1029
Sql_specifics::Ref sql_specifics= sql_facade->sqlSpecifics();
1030
query_kill_query= sql_specifics->query_kill_query(_usr_dbc_conn->id);
1032
if (query_kill_query.empty())
1035
const char *STATEMENT= "INTERRUPT";
1036
RowId log_message_index= add_log_message(DbSqlEditorLog::BusyMsg, _("Running..."), STATEMENT, "");
1042
GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
1043
std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
1045
ScopeExitTrigger schedule_timer_stop(boost::bind(&Timer::stop, &timer));
1047
stmt->execute(query_kill_query);
1049
// this can potentially cause threading issues, since connector driver isn't thread-safe
1050
//close_connection(_usr_dbc_conn);
1052
// connection drop doesn't interrupt fetching stage (surprisingly)
1053
// to workaround that we set special flag and check it periodically during fetching
1054
_usr_dbc_conn->is_stop_query_requested= is_running_query();
1058
if (_usr_dbc_conn->is_stop_query_requested)
1060
_grtm->replace_status_text("Query Cancelled");
1061
set_log_message(log_message_index, DbSqlEditorLog::NoteMsg, _("OK - Query cancelled"), STATEMENT, timer.duration_formatted());
1064
set_log_message(log_message_index, DbSqlEditorLog::NoteMsg, _("OK - Query already completed"), STATEMENT, timer.duration_formatted());
1066
// reconnect but only if in autocommit mode
1067
if (_usr_dbc_conn->autocommit_mode)
1069
// this will restore connection if it was established previously
1070
exec_sql_task->execute_in_main_thread(
1071
boost::bind(&SqlEditorForm::send_message_keep_alive, this),
1076
CATCH_SQL_EXCEPTION_AND_DISPATCH(STATEMENT, log_message_index, "")
1080
void SqlEditorForm::commit()
1082
exec_sql_retaining_editor_contents("COMMIT", Sql_editor::Ref(), false);
1086
void SqlEditorForm::rollback()
1088
exec_sql_retaining_editor_contents("ROLLBACK", Sql_editor::Ref(), false);
1092
void SqlEditorForm::explain_sql()
1095
Sql_editor::Ref sql_editor_= active_sql_editor();
1098
sql_editor_->selected_range(start, end);
1099
std::string sql= sql_editor_->sql();
1101
sql= sql.substr(start, end-start);
1103
do_explain_sql(sql);
1108
void SqlEditorForm::explain_current_statement()
1110
Sql_editor::Ref sql_editor_= active_sql_editor();
1112
do_explain_sql(sql_editor_->current_statement());
1116
void SqlEditorForm::do_explain_sql(const std::string &sql)
1118
SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms());
1119
std::list<std::string> statements;
1120
sql_facade->splitSqlScript(sql, statements);
1121
Sql_syntax_check::Ref sql_syntax_check= sql_facade->sqlSyntaxCheck();
1122
std::string sql_script;
1123
for (std::list<std::string>::iterator i= statements.begin(), i_end= statements.end(); i != i_end; ++i)
1124
if (Sql_syntax_check::sql_select == sql_syntax_check->determine_statement_type(*i))
1125
sql_script+= "EXPLAIN " + *i + ";\n";
1127
exec_sql_retaining_editor_contents(sql_script, active_sql_editor(), false);
1131
void SqlEditorForm::exec_sql_retaining_editor_contents(const std::string &sql_script, Sql_editor::Ref editor, bool sync, bool dont_add_limit_clause)
1135
RecordsetsRef recordsets;
1136
int i = sql_editor_index(editor);
1138
recordsets = sql_editor_recordsets(i);
1140
recordsets = RecordsetsRef(new Recordsets());
1142
exec_sql_task->exec(sync,
1143
boost::bind(&SqlEditorForm::do_exec_sql, this, _1,
1144
weak_ptr_from(this), sql_script, Sql_editor::Ref(),
1145
(ExecFlags)(Retaining | (dont_add_limit_clause?Dont_add_limit_clause:0)),
1151
void SqlEditorForm::run_sql()
1153
do_partial_ui_refresh(SqlEditorForm::RunCurrentScript);
1158
RecordsetsRef SqlEditorForm::exec_sql_returning_results(const std::string &sql_script, bool dont_add_limit_clause)
1160
RecordsetsRef rsets(new Recordsets());
1161
std::string sql(sql_script);
1163
do_exec_sql(_grtm->get_grt(), weak_ptr_from(this), sql, Sql_editor::Ref(),
1164
(ExecFlags)(dont_add_limit_clause?Dont_add_limit_clause:0),
1170
void SqlEditorForm::exec_sql(std::string &sql, Sql_editor::Ref editor, bool sync, bool wrap_with_non_std_delimiter, bool dont_add_limit_clause)
1172
ExecFlags flags = (ExecFlags)0;
1174
if (wrap_with_non_std_delimiter)
1175
flags = (ExecFlags)(flags | Wrap_with_non_std_delimiter);
1176
if (dont_add_limit_clause)
1177
flags = (ExecFlags)(flags | Dont_add_limit_clause);
1178
if (_options.get_int("DbSqlEditor:ShowWarnings", 1))
1179
flags = (ExecFlags)(flags | Show_warnings);
1182
RecordsetsRef recordsets;
1184
int i = sql_editor_index(editor);
1186
recordsets = sql_editor_recordsets(i);
1188
recordsets = RecordsetsRef(new Recordsets());
1190
exec_sql_task->exec(sync,
1191
boost::bind(&SqlEditorForm::do_exec_sql, this, _1,
1192
weak_ptr_from(this), sql, editor,
1197
struct GuardBoolFlag
1200
GuardBoolFlag(bool *ptr) : flag(ptr) { if (flag) *flag = true; }
1201
~GuardBoolFlag() { if (flag) *flag = false; }
1204
grt::StringRef SqlEditorForm::do_exec_sql(grt::GRT *grt, Ptr self_ptr, std::string &sql, Sql_editor::Ref editor, ExecFlags flags, RecordsetsRef result_list)
1206
bool retaining = (flags & Retaining) != 0;
1207
bool wrap_with_non_std_delimiter = (flags & Wrap_with_non_std_delimiter) != 0;
1208
bool dont_add_limit_clause = (flags & Dont_add_limit_clause) != 0;
1210
int max_query_size_to_log = _options.get_int("DbSqlEditor:MaxQuerySizeToHistory", 0);
1212
_grtm->replace_status_text(_("Executing Query..."));
1214
RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (SqlEditorForm, self_ptr, self, grt::StringRef(""))
1216
// add_log_message() will increment this variable on errors or warnings
1217
_exec_sql_error_count = 0;
1219
GMutex *result_list_mutex = NULL;
1220
sql::Driver *dbc_driver= NULL;
1221
std::string statement;
1222
int editor_index = -1;
1227
if (!_recordsets_are_pinned_by_default)
1229
GMutexLock recordsets_mutex(_recordsets_mutex);
1230
Recordsets recordsets;
1231
recordsets.reserve(result_list->size());
1232
BOOST_FOREACH (Recordset::Ref rs, *result_list)
1234
if (rs->pinned() || !rs->can_close(false))
1235
recordsets.push_back(rs);
1237
recordset_list_changed.emit(rs, false);
1239
result_list->swap(recordsets);
1243
int default_seq = 0;
1244
int *rs_sequence = &default_seq;
1245
bool *busy_flag = 0;
1247
if (editor && !retaining)
1249
Sql_editor_info::Ref info;
1251
for (Sql_editors::iterator info_ = _sql_editors.begin(); info_ != _sql_editors.end(); ++info_, ++editor_index)
1253
if ((*info_)->editor == editor)
1256
rs_sequence = &info->rs_sequence;
1257
busy_flag = &info->busy;
1262
GuardBoolFlag guard_busy_flag(busy_flag);
1264
// close all recordsets for the same editor
1267
GMutexTryLock recordsets_mutex(info->recordset_mutex);
1268
if (!recordsets_mutex.locked())
1269
throw std::runtime_error("The editor is busy and cannot execute the query now. Please try again later.");
1271
RecordsetsRef recordsets(new Recordsets());
1272
recordsets->reserve(result_list->size());
1273
int index = result_list->size();
1274
while (--index >= 0)
1276
if (!(*result_list)[index]->can_close(false))
1277
recordsets->push_back((*result_list)[index]);
1280
// This must perform the same as on_close_recordset(), but without the redundant mutex locking.
1281
RecordsetsRef rsets = info->recordsets;
1282
Recordset::Ref current_recordset = (*result_list)[index];
1283
Recordsets::iterator iter = std::find(rsets->begin(), rsets->end(), current_recordset);
1284
if (iter != rsets->end())
1286
if (info->active_recordset == *iter)
1287
info->active_recordset.reset();
1290
if (editor_index >= 0)
1291
recordset_list_changed(editor_index, current_recordset, false);
1294
result_list->swap(*recordsets.get());
1295
result_list_mutex = info->recordset_mutex;
1299
GMutexLock use_dbc_conn_mutex= ensure_valid_usr_connection();
1301
GuardBoolFlag guard_busy_flag(busy_flag);
1303
dbc_driver= _usr_dbc_conn->ref->getDriver();
1304
dbc_driver->threadInit();
1306
bool is_running_query= true;
1307
AutoSwap<bool> is_running_query_keeper(_is_running_query, is_running_query);
1308
update_menu_and_toolbar();
1310
_has_pending_log_messages= false;
1311
bec::TimerActionThread *log_messages_refresh_thread= TimerActionThread::create(
1312
boost::bind(&SqlEditorForm::refresh_log_messages, this, true),
1313
_progress_status_update_interval);
1314
ScopeExitTrigger schedule_log_messages_refresh_thread_stop(boost::bind(
1315
&bec::TimerActionThread::stop, log_messages_refresh_thread, false));
1316
ScopeExitTrigger schedule_log_messages_refresh(boost::bind(
1317
&SqlEditorForm::refresh_log_messages, this, true));
1319
SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms());
1320
Sql_syntax_check::Ref sql_syntax_check= sql_facade->sqlSyntaxCheck();
1321
Sql_specifics::Ref sql_specifics= sql_facade->sqlSpecifics();
1323
if (wrap_with_non_std_delimiter)
1324
sql= sql_specifics->setting_non_std_sql_delimiter() + sql;
1326
bool ran_set_sql_mode = false;
1327
//XXX this will create a copy of the whole script, should work with ranges instead
1328
std::list<std::string> statements;
1329
sql_facade->splitSqlScript(sql, statements);
1331
if (!max_query_size_to_log || max_query_size_to_log >= (int)sql.size() )
1332
_history->add_entry(statements);
1334
//std::list<Recordset::Ref> added_recordsets;
1335
BOOST_FOREACH (statement, statements)
1337
std::list<std::string> sub_statements;
1338
sql_facade->splitSqlScript(statement, sub_statements);
1339
int multiple_statement_count= sub_statements.size();
1340
bool is_multiple_statement= (1 < multiple_statement_count);
1342
statement= strip_text(statement, false, true);
1343
if (statement.empty())
1346
Sql_syntax_check::Statement_type statement_type= sql_syntax_check->determine_statement_type(statement);
1347
if (Sql_syntax_check::sql_empty == statement_type)
1350
std::string schema_name;
1351
std::string table_name;
1352
std::string editable_query_tail;
1353
if (!is_multiple_statement && (Sql_syntax_check::sql_edit == statement_type))
1355
// to open table contents in edit mode the following syntax is used: EDIT [SCHEMA.]TABLE ...
1356
// this type of query needs special preprocessing: rewriting using 'select' statement
1357
statement_type= Sql_syntax_check::sql_select;
1358
sql_syntax_check->parse_edit_statement(statement, schema_name, table_name, editable_query_tail);
1359
if (schema_name.empty())
1360
schema_name= active_schema();
1361
if (schema_name.empty())
1362
schema_name= _usr_dbc_conn->ref->getSchema();
1365
Recordset_cdbc_storage::Ref data_storage;
1367
// for select queries add limit clause if specified by global option
1368
if (!is_multiple_statement && (Sql_syntax_check::sql_select == statement_type))
1370
data_storage= Recordset_cdbc_storage::create(_grtm);
1371
data_storage->rdbms(rdbms());
1372
data_storage->dbms_conn(_usr_dbc_conn);
1374
std::string error = "";
1376
if(!table_name.empty() || is_editable_select(statement, schema_name, table_name, error))
1378
data_storage->schema_name(schema_name);
1379
data_storage->table_name(table_name);
1382
data_storage->readonly_reason(error);
1384
data_storage->sql_query(statement);
1386
data_storage->additional_clauses(editable_query_tail);
1388
bool limit_rows= !dont_add_limit_clause && (0 != _options.get_int("SqlEditor:LimitRows", 0));
1389
data_storage->limit_rows(limit_rows);
1391
int limit_rows_count= _options.get_int("SqlEditor:LimitRowsCount");
1392
if (0 != limit_rows_count)
1393
data_storage->limit_rows_count(limit_rows_count);
1395
statement= data_storage->decorated_sql_query();
1399
RowId log_message_index= add_log_message(DbSqlEditorLog::BusyMsg, _("Running..."), statement,
1400
((Sql_syntax_check::sql_select == statement_type) ? "? / ?" : "?"));
1402
bool statement_failed= false;
1403
long long updated_rows_count= -1;
1404
Timer statement_exec_timer(false);
1405
Timer statement_fetch_timer(false);
1406
boost::shared_ptr<sql::Statement> dbc_statement(_usr_dbc_conn->ref->createStatement());
1407
bool is_result_set_first= false;
1409
if (_usr_dbc_conn->is_stop_query_requested)
1410
throw std::runtime_error(_("Query execution has been stopped, the connection to the DB server was not restarted, any open transaction remains open"));
1415
ScopeExitTrigger schedule_statement_exec_timer_stop(boost::bind(&Timer::stop, &statement_exec_timer));
1416
statement_exec_timer.run();
1417
is_result_set_first= dbc_statement->execute(statement);
1419
updated_rows_count= dbc_statement->getUpdateCount();
1420
if (Sql_syntax_check::sql_use == statement_type)
1421
cache_active_schema_name();
1422
if (Sql_syntax_check::sql_set == statement_type && statement.find("@sql_mode") != std::string::npos)
1423
ran_set_sql_mode= true;
1425
catch (sql::SQLException &e)
1427
std::string err_msg;
1429
switch (e.getErrorCode())
1431
case 1046: // not default DB selected
1432
err_msg= strfmt(_("Error Code: %i. %s\nSelect the default DB to be used by double-clicking its name in the SCHEMAS list in the sidebar."), e.getErrorCode(), e.what());
1434
case 1175: // safe mode
1435
err_msg= strfmt(_("Error Code: %i. %s\nTo disable safe mode, toggle the option in Preferences -> SQL Editor -> Query Editor and reconnect."), e.getErrorCode(), e.what());
1438
err_msg= strfmt(_("Error Code: %i. %s"), e.getErrorCode(), e.what());
1441
set_log_message(log_message_index, DbSqlEditorLog::ErrorMsg, err_msg, statement, statement_exec_timer.duration_formatted());
1442
statement_failed= true;
1444
catch (std::exception &e)
1446
std::string err_msg= strfmt(_("Error: %s"), e.what());
1447
set_log_message(log_message_index, DbSqlEditorLog::ErrorMsg, err_msg, statement, statement_exec_timer.duration_formatted());
1448
statement_failed= true;
1450
if (statement_failed)
1452
if (_continue_on_error)
1453
continue; // goto next statement
1455
goto stop_processing_sql_script;
1458
sql::mysql::MySQL_Connection* mysql_connection = dynamic_cast<sql::mysql::MySQL_Connection*>(dbc_statement->getConnection());
1459
sql::SQLString last_statement_info;
1460
if (mysql_connection != NULL)
1461
last_statement_info = mysql_connection->getLastStatementInfo();
1462
if (updated_rows_count >= 0)
1464
std::string message = strfmt(_("%lli row(s) affected"), updated_rows_count);
1465
bool has_warning = false;
1466
if (flags & Show_warnings)
1468
std::string warnings_message;
1469
const sql::SQLWarning *warnings= dbc_statement->getWarnings();
1473
const sql::SQLWarning *w = warnings;
1476
warnings_message.append(strfmt("\n%i %s", w->getErrorCode(), w->getMessage().c_str()));
1478
w= w->getNextWarning();
1480
message.append(strfmt(_(", %i warning(s):"), count));
1483
if (!warnings_message.empty())
1484
message.append(warnings_message);
1486
if (!last_statement_info->empty())
1487
message.append("\n").append(last_statement_info);
1488
set_log_message(log_message_index, has_warning ? DbSqlEditorLog::WarningMsg : DbSqlEditorLog::OKMsg, message, statement, statement_exec_timer.duration_formatted());
1491
int resultset_count= 0;
1492
bool more_results= is_result_set_first;
1493
bool reuse_log_msg= false;
1494
if ((updated_rows_count < 0) || is_multiple_statement)
1496
for (int processed_substatements_count= 0; processed_substatements_count < multiple_statement_count; ++processed_substatements_count)
1502
if (!reuse_log_msg && ((updated_rows_count >= 0) || (resultset_count)))
1503
log_message_index= add_log_message(DbSqlEditorLog::BusyMsg, _("Fetching..."), statement, "- / ?");
1505
set_log_message(log_message_index, DbSqlEditorLog::BusyMsg, _("Fetching..."), statement, statement_exec_timer.duration_formatted() + " / ?");
1506
reuse_log_msg= false;
1507
boost::shared_ptr<sql::ResultSet> dbc_resultset;
1509
ScopeExitTrigger schedule_statement_fetch_timer_stop(boost::bind(&Timer::stop, &statement_fetch_timer));
1510
statement_fetch_timer.run();
1511
dbc_resultset.reset(dbc_statement->getResultSet());
1517
data_storage= Recordset_cdbc_storage::create(_grtm);
1518
data_storage->rdbms(rdbms());
1519
data_storage->dbms_conn(_usr_dbc_conn);
1520
if (table_name.empty())
1521
data_storage->sql_query(statement);
1522
data_storage->schema_name(schema_name);
1523
data_storage->table_name(table_name);
1526
data_storage->dbc_statement(dbc_statement);
1527
data_storage->dbc_resultset(dbc_resultset);
1528
data_storage->reloadable(!is_multiple_statement && (Sql_syntax_check::sql_select == statement_type));
1530
Recordset::Ref rs= Recordset::create(exec_sql_task);
1531
rs->is_field_value_truncation_enabled(true);
1532
rs->apply_changes= boost::bind(&SqlEditorForm::apply_changes_to_recordset, this, Recordset::Ptr(rs));
1533
rs->on_close.connect(boost::bind(&SqlEditorForm::on_close_recordset, this, _1));
1534
rs->caption(strfmt("%s %i",
1535
(table_name.empty() ? _("Result") : table_name.c_str()),
1537
rs->generator_query(statement);
1538
rs->action_list().register_action("recall_query",
1539
boost::bind(&SqlEditorForm::recall_recordset_query, this, Recordset::Ptr(rs)));
1541
// XXX: refresh recordset title on status bar change? Huh?
1542
scoped_connect(&rs->refresh_ui_status_bar_signal,
1543
boost::bind(&bec::RefreshUI::do_partial_ui_refresh, this, (int)RefreshRecordsetTitle));
1545
rs->data_storage(data_storage);
1546
rs->export_wizard= boost::bind(&SqlEditorForm::show_export_recordset, this, sql_editor_index(editor), Recordset::Ptr(rs));
1547
rs->popup_menu_items_slot= boost::bind(&SqlEditorForm::recordset_popup_menu_items, this, _1, _2, Recordset::Ptr(rs));
1548
rs->call_popup_menu_item_slot= boost::bind(&SqlEditorForm::call_recordset_popup_menu_item, this, _1, _2, _3, Recordset::Ptr(rs));
1550
if (data_storage->valid()) // query statement
1553
GMutexLock recordsets_mutex(result_list_mutex);
1554
result_list->push_back(rs);
1556
if (editor_index >= 0)
1557
recordset_list_changed(editor_index, rs, true);
1558
std::string statement_res_msg= strfmt(_("%i row(s) returned"), rs->row_count());
1559
if (!last_statement_info->empty())
1560
statement_res_msg.append("\n").append(last_statement_info);
1561
std::string exec_and_fetch_durations=
1562
(((updated_rows_count >= 0) || (resultset_count)) ? std::string("-") : statement_exec_timer.duration_formatted()) + " / " +
1563
statement_fetch_timer.duration_formatted();
1564
set_log_message(log_message_index, DbSqlEditorLog::OKMsg, statement_res_msg, statement, exec_and_fetch_durations);
1566
//! else failed to fetch data
1567
//added_recordsets.push_back(rs);
1572
reuse_log_msg= true;
1574
data_storage.reset();
1577
while ((more_results= dbc_statement->getMoreResults()));
1581
if ((updated_rows_count < 0) && !(resultset_count))
1583
set_log_message(log_message_index, DbSqlEditorLog::OKMsg, _("OK"), statement, statement_exec_timer.duration_formatted());
1587
} // BOOST_FOREACH (statement, statements)
1589
_grtm->replace_status_text(_("Query Completed"));
1591
stop_processing_sql_script:
1592
// try to minimize the times this is called, since this will change the state of the connection
1593
// after a user query is ran (eg, it will reset all warnings)
1594
if (ran_set_sql_mode)
1597
CATCH_ANY_EXCEPTION_AND_DISPATCH(statement)
1600
dbc_driver->threadEnd();
1602
update_menu_and_toolbar();
1604
_usr_dbc_conn->is_stop_query_requested = false;
1606
return grt::StringRef("");
1610
bool SqlEditorForm::is_running_query()
1612
return _is_running_query;
1616
void SqlEditorForm::continue_on_error(bool val)
1618
if (_continue_on_error == val)
1621
_continue_on_error= val;
1622
_options.set("DbSqlEditor:ContinueOnError", grt::IntegerRef((int)_continue_on_error));
1625
_menu->set_item_checked("query.stopOnError", !continue_on_error());
1626
set_editor_tool_items_checked("query.stopOnError", !continue_on_error());
1630
void SqlEditorForm::send_message_keep_alive()
1634
// ping server and reset connection timeout counter
1635
// this also checks the connection state and restores it if possible
1636
ensure_valid_aux_connection();
1637
ensure_valid_usr_connection();
1639
catch (const std::exception &)
1645
Recordset::Ref SqlEditorForm::active_recordset(int editor)
1647
GMutexLock lock(_sql_editors_mutex);
1648
if (editor >= 0 && editor < (int)_sql_editors.size())
1649
return _sql_editors[editor]->active_recordset;
1650
return Recordset::Ref();
1653
void SqlEditorForm::active_recordset(int editor, Recordset::Ref rset)
1655
GMutexLock lock(_sql_editors_mutex);
1656
if (editor >= 0 && editor < (int)_sql_editors.size())
1657
_sql_editors[editor]->active_recordset = rset;
1661
_menu->set_item_enabled("query.save_edits", rset && rset->has_pending_changes());
1662
_menu->set_item_enabled("query.discard_edits", rset && rset->has_pending_changes());
1663
_menu->set_item_enabled("query.export", (bool)rset);
1667
bool SqlEditorForm::recordset_reorder(int editor, Recordset::Ref value, int new_index)
1669
GMutexTryLock lock(_sql_editors_mutex);
1672
if (editor >= 0 && editor < (int)_sql_editors.size())
1674
GMutexTryLock rlock(_sql_editors[editor]->recordset_mutex);
1677
RecordsetsRef recordsets(sql_editor_recordsets(editor));
1679
Recordsets::iterator iter = std::find(recordsets->begin(), recordsets->end(), value);
1680
if (iter != recordsets->end())
1682
if (new_index >= (int)recordsets->size()-1)
1684
recordsets->erase(iter);
1686
recordsets->push_back(value);
1688
recordsets->insert(recordsets->begin() + new_index, value);
1698
void SqlEditorForm::on_close_recordset(Recordset::Ptr rs_ptr)
1701
RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
1704
GMutexLock ed_lock(_sql_editors_mutex);
1705
editor = sql_editor_index_for_recordset(rs_ref->key());
1708
GMutexLock recordsets_mutex(_sql_editors[editor]->recordset_mutex);
1709
RecordsetsRef rsets(sql_editor_recordsets(editor));
1710
rsets->erase(std::find(rsets->begin(), rsets->end(), rs_ref));
1711
if (_sql_editors[editor]->active_recordset == rs_ref)
1712
_sql_editors[editor]->active_recordset.reset();
1715
recordset_list_changed(editor, rs_ref, false);
1716
//delme close_recordset_ui.emit(rs->key());
1720
void SqlEditorForm::recall_recordset_query(Recordset::Ptr rs_ptr)
1722
RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
1724
std::string query = rs->generator_query();
1726
new_sql_scratch_area();
1727
set_sql_editor_text(query);
1731
void SqlEditorForm::apply_changes_to_recordset(Recordset::Ptr rs_ptr)
1733
RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
1737
GMutexLock usr_dbc_conn_mutex= ensure_valid_usr_connection();
1739
// we need transaction to enforce atomicity of change set
1740
// so if autocommit is currently enabled disable it temporarily
1741
bool auto_commit= _usr_dbc_conn->ref->getAutoCommit();
1742
ScopeExitTrigger autocommit_mode_keeper;
1746
int res= mforms::Utilities::show_warning(
1747
_("Apply Changes to Recordset"),
1748
_("Autocommit is currently disabled. Do you want to perform a COMMIT before applying the changes?\n"
1749
"If you do not commit, a failure during the recordset update will result in a rollback of the active transaction, if you have one."),
1750
_("Commit and Apply"),
1754
if (res == mforms::ResultOk)
1756
_usr_dbc_conn->ref->commit();
1761
autocommit_mode_keeper.slot= boost::bind(
1762
&sql::Connection::setAutoCommit, _usr_dbc_conn->ref.get(),
1764
_usr_dbc_conn->ref->setAutoCommit(false);
1767
if (res != mforms::ResultCancel) // only if not canceled
1769
bool is_data_changes_commit_wizard_enabled= (0 != _options.get_int("DbSqlEditor:IsDataChangesCommitWizardEnabled", 1));
1770
if (is_data_changes_commit_wizard_enabled)
1772
/*bool are_changes_committed= */run_data_changes_commit_wizard(rs_ptr);
1776
rs->apply_changes_();
1780
CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Apply changes to recordset"))
1784
bool SqlEditorForm::run_data_changes_commit_wizard(Recordset::Ptr rs_ptr)
1786
RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs, false)
1788
on_sql_script_run_error.disconnect_all_slots();
1789
on_sql_script_run_progress.disconnect_all_slots();
1790
on_sql_script_run_statistics.disconnect_all_slots();
1792
// set underlying recordset data storage to use sql substitute (potentially modified by user)
1793
// instead of generating sql based on swap db contents
1794
Recordset_data_storage::Ref data_storage_ref= rs->data_storage();
1795
Recordset_sql_storage *sql_storage= dynamic_cast<Recordset_sql_storage *>(data_storage_ref.get());
1798
sql_storage->init_sql_script_substitute(rs_ptr, true);
1799
sql_storage->is_sql_script_substitute_enabled(true);
1800
const Sql_script &sql_script= sql_storage->sql_script_substitute();;
1801
std::string sql_script_text= Recordset_sql_storage::statements_as_sql_script(sql_script.statements);
1803
SqlScriptRunWizard wizard(_grtm);
1804
scoped_connection c1(on_sql_script_run_error.connect(boost::bind(&SqlScriptApplyPage::on_error, wizard.apply_page, _1, _2, _3)));
1805
scoped_connection c2(on_sql_script_run_progress.connect(boost::bind(&SqlScriptApplyPage::on_exec_progress, wizard.apply_page, _1)));
1806
scoped_connection c3(on_sql_script_run_statistics.connect(boost::bind(&SqlScriptApplyPage::on_exec_stat, wizard.apply_page, _1, _2)));
1807
wizard.values().gset("sql_script", sql_script_text);
1808
wizard.apply_page->apply_sql_script= boost::bind(&SqlEditorForm::apply_data_changes_commit, this, _1, rs_ptr);
1811
return !wizard.has_errors();
1815
void SqlEditorForm::apply_data_changes_commit(std::string &sql_script_text, Recordset::Ptr rs_ptr)
1817
RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs);
1819
// this lock is supposed to be acquired lower in call-stack by SqlEditorForm::apply_changes_to_recordset
1820
//GMutexLock usr_conn_mutex= ensure_valid_usr_connection();
1822
Recordset_data_storage::Ref data_storage_ref= rs->data_storage();
1823
Recordset_sql_storage *sql_storage= dynamic_cast<Recordset_sql_storage *>(data_storage_ref.get());
1827
int max_query_size_to_log = _options.get_int("DbSqlEditor:MaxQuerySizeToHistory", 0);
1829
Sql_script sql_script= sql_storage->sql_script_substitute();
1830
sql_script.statements.clear();
1831
SqlFacade::Ref sql_splitter= SqlFacade::instance_for_rdbms(rdbms());
1832
sql_splitter->splitSqlScript(sql_script_text, sql_script.statements);
1834
scoped_connection on_sql_script_run_error_conn(sql_storage->on_sql_script_run_error.connect(on_sql_script_run_error));
1835
scoped_connection on_sql_script_run_progress_conn(sql_storage->on_sql_script_run_progress.connect(on_sql_script_run_progress));
1836
scoped_connection on_sql_script_run_statistics_conn(sql_storage->on_sql_script_run_statistics.connect(on_sql_script_run_statistics));
1838
sql_storage->sql_script_substitute(sql_script);
1839
rs->do_apply_changes(_grtm->get_grt(), rs_ptr, Recordset_data_storage::Ptr(data_storage_ref));
1841
if (!max_query_size_to_log || max_query_size_to_log >= (int)sql_script_text.size() )
1842
_history->add_entry(sql_script.statements);
1846
std::string SqlEditorForm::active_schema() const
1848
return (_usr_dbc_conn) ? _usr_dbc_conn->active_schema : std::string();
1852
int SqlEditorForm::active_schema_index() const
1854
return _schema_tree->get_index_of_schema(active_schema());
1858
void SqlEditorForm::cache_active_schema_name()
1860
std::string schema=_usr_dbc_conn->ref->getSchema();
1861
_usr_dbc_conn->active_schema= schema;
1862
_aux_dbc_conn->active_schema= schema;
1864
_schema_tree->set_active_schema(schema);
1866
exec_sql_task->execute_in_main_thread(
1867
boost::bind(&SqlEditorForm::update_editor_title_schema, this, schema),
1873
//_grtm->run_once_when_idle(boost::bind(&mforms::GRTTreeView::refresh, _side_bar->get_schema_tree(), bec::NodeId()));
1874
_grtm->run_once_when_idle(boost::bind(&mforms::View::set_needs_repaint, _side_bar->get_schema_tree()));
1879
void SqlEditorForm::active_schema(const std::string &value)
1886
std::string schema= active_schema();
1887
if (value == schema)
1891
GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
1892
_aux_dbc_conn->ref->setSchema(value);
1893
_aux_dbc_conn->active_schema= value;
1897
GMutexLock usr_dbc_conn_mutex= ensure_valid_usr_connection();
1898
_usr_dbc_conn->ref->setSchema(value);
1899
_usr_dbc_conn->active_schema= value;
1902
_schema_tree->set_active_schema(value);
1905
_grtm->run_once_when_idle(boost::bind(&mforms::View::set_needs_repaint, _side_bar->get_schema_tree()));
1906
//_side_bar->get_schema_tree()->refresh(bec::NodeId());
1908
// remember active schema
1909
_connection->parameterValues().gset("DbSqlEditor:LastDefaultSchema", value);
1911
update_editor_title_schema(value);
1913
grt_manager()->replace_status_text(strfmt(_("Active schema changed to %s"), value.c_str()));
1915
CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Set active schema"))
1919
wb::LiveSchemaTree *SqlEditorForm::get_base_schema_tree()
1921
return &_base_schema_tree;
1924
wb::LiveSchemaTree *SqlEditorForm::get_schema_tree()
1926
return _schema_tree;
1929
wb::LiveSchemaTree *SqlEditorForm::get_filtered_schema_tree()
1931
return &_filtered_schema_tree;
1934
db_mgmt_RdbmsRef SqlEditorForm::rdbms()
1936
if (_connection.is_valid())
1938
if (!_connection->driver().is_valid())
1939
throw std::runtime_error("Connection has invalid driver, check connection parameters.");
1940
return db_mgmt_RdbmsRef::cast_from(_connection->driver()->owner());
1943
return db_mgmt_RdbmsRef::cast_from(_grtm->get_grt()->get("/wb/doc/physicalModels/0/rdbms"));
1947
int SqlEditorForm::count_connection_editors(std::string conn_name)
1950
boost::weak_ptr<SqlEditorForm> editor;
1952
std::list<boost::weak_ptr<SqlEditorForm> >::iterator index, end;
1954
end = _wbsql->get_open_editors()->end();
1955
for(index = _wbsql->get_open_editors()->begin(); index != end; index++)
1957
SqlEditorForm::Ref editor((*index).lock());
1958
std::string editor_connection = editor->_connection->name();
1959
if (editor_connection == conn_name)
1966
std::string SqlEditorForm::caption() const
1971
std::string SqlEditorForm::get_title(bool new_editor)
1973
int count_limit = new_editor ? 0 : 1;
1974
std::string caption= _("SQL Editor");
1975
std::string editor_connection = _connection->name();
1977
if (*_connection->name().c_str())
1978
caption+= strfmt(" (%s", _connection->name().c_str());
1981
if (_connection->driver()->name() == "MysqlNativeSocket")
1982
caption+= " (localhost";
1984
caption+= strfmt(" (%s", truncate_text(editor_connection,21).c_str());
1987
// This function is called in three different scenarios:
1988
// New Editor: The title should include the active schema if there's a previously
1989
// registered open editor to the same host (because the new editor is
1990
// not registered yet)
1991
// Activating a new schema : in which case the default schema should be included
1992
// if there are at least 2 registered open editors
1993
// Other editor has been opened to the same host : in which case the active schema
1994
// should be included
1995
if(count_connection_editors(editor_connection) > count_limit && !_usr_dbc_conn->active_schema.empty())
1996
caption+= strfmt(" - %s", truncate_text(_usr_dbc_conn->active_schema, 20).c_str());
2004
bool SqlEditorForm::save_snippet(const std::string &text)
2006
if (text.empty()) return false;
2009
if (!wbsql()->get_wbui()->get_wb()->request_input(_("Enter a Name for the Snippet (optional):"), 0, name))
2012
DbSqlEditorSnippets::get_instance()->add_snippet(name, text);
2017
//--------------------------------------------------------------------------------------------------
2019
bool SqlEditorForm::can_close()
2021
return can_close_(true);
2025
bool SqlEditorForm::can_close_(bool interactive)
2027
bool check_editors = true;
2029
if (!bec::UIForm::can_close())
2033
_options.gset("DbSqlEditor:SidebarCollapseState", _side_bar->get_collapse_states());
2036
// if Save of workspace on close is enabled, we don't need to check whether there are unsaved SQL editors
2037
// but other stuff should be checked
2038
grt::ValueRef option(_grtm->get_app_option("workbench:SaveSQLWorkspaceOnClose"));
2039
if (option.is_valid() && *grt::IntegerRef::cast_from(option))
2040
check_editors = false;
2045
ConfirmSaveDialog dialog(0, "Close SQL Editor", "The following files/resultsets have unsaved changes.\nDo you want to review these changes before closing?");
2046
for (int i = 0; i < sql_editor_count(); i++)
2048
if (sql_editor_dirty(i) && check_editors)
2050
std::string n= sql_editor_path(i);
2052
n= std::string(g_basename(n.c_str())).append(" - ").append(n);
2054
n= "Unsaved SQL Query";
2055
dialog.add_item("Script Buffers", n);
2058
RecordsetsRef rsets = sql_editor_recordsets(i);
2059
BOOST_FOREACH (Recordset::Ref rs, *(rsets.get()))
2061
if (!rs->can_close(false))
2062
dialog.add_item("Resultset", rs->caption());
2067
if (dialog.change_count() > 1)
2069
switch (dialog.run())
2071
case ConfirmSaveDialog::ReviewChanges:
2075
case ConfirmSaveDialog::DiscardChanges:
2079
case ConfirmSaveDialog::Cancel:
2083
else if (dialog.change_count() == 1)
2086
// review changes 1 by 1
2087
if (review && check_editors)
2089
for (int i = 0; i < sql_editor_count(); i++)
2091
// sql_editor_wilL_close() checks for unsaved recordsets
2092
if (!sql_editor_will_close(i))
2097
else // !interactive
2099
for (int i = 0; i < sql_editor_count(); i++)
2101
if (check_editors && sql_editor_dirty(i))
2105
GMutexTryLock lock(_sql_editors[i]->recordset_mutex);
2109
RecordsetsRef rsets = sql_editor_recordsets(i);
2110
BOOST_FOREACH (Recordset::Ref rs, *(rsets.get()))
2112
if (!rs->can_close(false))
2124
bec::MenuItemList SqlEditorForm::recordset_popup_menu_items(const std::vector<int> &rows, int column, Recordset::Ptr rs_ptr)
2126
RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs, bec::MenuItemList())
2129
bec::ArgumentPool argpool;
2133
grt::IntegerListRef row_list(grt_manager()->get_grt());
2134
for (std::vector<int>::const_iterator iter= rows.begin(); iter != rows.end(); ++iter)
2135
row_list.insert(grt::IntegerRef(*iter));
2136
argpool.add_simple_value("selectedRows", row_list);
2137
argpool.add_simple_value("clickedRow", grt::IntegerRef(rows[0]));
2139
argpool.add_simple_value("clickedColumn", grt::IntegerRef(column));
2141
_context_ui->get_wb()->update_plugin_arguments_pool(argpool);
2143
db_query_EditorRef editor(_wbsql->get_grt_editor_object(this));
2145
for (size_t c= editor->resultsets().count(), i= 0; i < c; i++)
2147
db_query_ResultsetRef rset(editor->resultsets()[i]);
2148
if (rset->get_data()->recordset.get() == rs)
2150
// add rset to the pool, overriding the one added by update_args
2151
argpool.add_entries_for_object("", rset, "db.query.Resultset");
2156
std::list<std::string> groups;
2157
groups.push_back("Menu/SQL/Resultset");
2159
return grt_manager()->get_plugin_context_menu_items(groups, argpool);
2162
return bec::MenuItemList();
2166
bool SqlEditorForm::call_recordset_popup_menu_item(const std::string &item, const std::vector<int> &rows,
2167
int column, Recordset::Ptr rs_ptr)
2169
RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs, false)
2173
bec::ArgumentPool argpool;
2177
grt::IntegerListRef row_list(grt_manager()->get_grt());
2178
for (std::vector<int>::const_iterator iter= rows.begin(); iter != rows.end(); ++iter)
2179
row_list.insert(grt::IntegerRef(*iter));
2180
argpool["app.PluginInputDefinition:selectedRows"]= row_list;
2181
argpool["app.PluginInputDefinition:clickedRow"]= grt::IntegerRef(rows[0]);
2183
argpool["app.PluginInputDefinition:clickedColumn"]= grt::IntegerRef(column);
2185
_context_ui->get_wb()->update_plugin_arguments_pool(argpool);
2187
db_query_EditorRef editor(_wbsql->get_grt_editor_object(this));
2189
for (size_t c= editor->resultsets().count(), i= 0; i < c; i++)
2191
db_query_ResultsetRef rset(editor->resultsets()[i]);
2192
if (rset->get_data()->recordset.get() == rs)
2194
// add rset to the pool, overriding the one added by update_args
2195
argpool.add_entries_for_object("", rset, "db.query.Resultset");
2200
return _context_ui->get_command_ui()->activate_command(item, argpool);
2207
//--------------------------------------------------------------------------------------------------
2209
bool SqlEditorForm::is_editable_select(const std::string& statement, std::string &schema_name, std::string& table_name, std::string& error)
2211
SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms());
2212
SqlFacade::Column_name_list column_names;
2216
bool missing_pk = false;
2217
bool found_id = false;
2221
// Inserts the key columns if not already present included
2222
if (sql_facade->parseSelectStatementForEdit(statement, schema_name, table_name, column_names))
2224
bool fetch_table_data = true;
2226
if ( schema_name.length() == 0 )
2227
schema_name = _usr_dbc_conn->active_schema;
2229
LiveSchemaTree::TableNode table;
2231
bec::NodeId table_node = _schema_tree->get_node_for_object(schema_name, LiveSchemaTree::Table, table_name);
2233
if (table_node.is_valid())
2235
table = *dynamic_cast<LiveSchemaTree::TableNode *>(_schema_tree->get_object_node_by_index(table_node));
2237
// If the column info is not available it must be fetched
2238
fetch_table_data = !table.columns_loaded;
2241
// If the table information has not been loaded
2242
if (fetch_table_data)
2244
// Loads the table information
2245
fetch_object_details(LiveSchemaTree::Table, schema_name, table_name, LiveSchemaTree::COLUMN_DATA,
2246
boost::bind(&SqlEditorForm::table_details_arrived, this, _1, _2, _3, &table));
2249
// Checks the id columns (if exist) until one is found on the select statement
2250
for(size_t index = 0; !found_id && index < table.columns.size(); index++)
2252
if (table.columns[index].is_id)
2254
// It could be ID and not PK
2255
bool is_pk = table.columns[index].is_pk;
2257
std::string column_name = table.columns[index].name;
2259
// Counts the primary key columns
2263
// If the query is not retrieving all the columns, reviews if the primary key column is being retrieved
2264
if (column_names.front().first != "*")
2267
SqlFacade::Column_name_list::iterator index, end = column_names.end();
2268
for(index = column_names.begin(); !found && index != end; index++)
2269
found = ((*index).first == column_name);
2271
if (!found && is_pk)
2274
if (found && !is_pk)
2283
error = base::strfmt("Table %s has no primary keys or unique non nullable columns defined. Only tables with primary keys or unique non nullable columns can be edited.", table_name.c_str());
2285
error = base::strfmt("The select statement is missing primary key or unique non nullable columns on table %s.", table_name.c_str());
2289
error = "The statement is not editable.";
2291
// The return value is true if:
2292
// All the columns included on the PK were found on the select statement (in case of a compound PK)
2293
// A unique non nullable column was found on the select statement
2294
return ((pk_count > 0) && !missing_pk) || found_id;
2297
void SqlEditorForm::table_details_arrived(const wb::LiveSchemaTree::SchemaNode &schema_node, const wb::LiveSchemaTree::ObjectNode *obj_node, wb::LiveSchemaTree::ObjectType obj_type, wb::LiveSchemaTree::TableNode *table_node)
2299
*table_node = *obj_node;
2302
void SqlEditorForm::update_editor_title_schema(const std::string& schema)
2304
// Gets the editor label including the schema name only if
2305
// the number of opened editors to the same host is > 1
2306
std::string temp_title = get_title(false);
2308
if (temp_title != _title)
2310
_title = temp_title;
b'\\ No newline at end of file'